diff --git a/Doc/library/os.rst b/Doc/library/os.rst --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -1729,6 +1729,16 @@ Added support for Windows 6.0 (Vista) symbolic links. +.. function:: realpath(path) + + Return a string representing the canonicalized pathname of *path*. + See :manpage:`realpath(3)` for more information. + + Availability: Unix + + .. versionadded:: 3.3 + + .. function:: remove(path) Remove (delete) the file *path*. If *path* is a directory, :exc:`OSError` is diff --git a/Lib/posixpath.py b/Lib/posixpath.py --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -381,31 +381,7 @@ def realpath(filename): """Return the canonical path of the specified filename, eliminating any symbolic links encountered in the path.""" - if isinstance(filename, bytes): - sep = b'/' - empty = b'' - else: - sep = '/' - empty = '' - if isabs(filename): - bits = [sep] + filename.split(sep)[1:] - else: - bits = [empty] + filename.split(sep) - - for i in range(2, len(bits)+1): - component = join(*bits[0:i]) - # Resolve symbolic links. - if islink(component): - resolved = _resolve_link(component) - if resolved is None: - # Infinite loop -- return original component + rest of the path - return abspath(join(*([component] + bits[i:]))) - else: - newpath = join(*([resolved] + bits[i:])) - return realpath(newpath) - - return abspath(filename) - + return os.realpath(filename) def _resolve_link(path): """Internal helper function. Takes a path and follows symlinks diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -7477,6 +7477,36 @@ #endif +#ifdef HAVE_REALPATH +PyDoc_STRVAR(posix_realpath__doc__, +"realpath(path) -> path\n\n\ +Return the canonicalized pathname of the given path."); + +static PyObject * +posix_realpath(PyObject *self, PyObject *args) +{ + PyObject *opath, *orpath; + char *path, *rpath; + + if (!PyArg_ParseTuple(args, "O&:realpath", + PyUnicode_FSConverter, &opath)) + return NULL; + path = PyBytes_AsString(opath); + + Py_BEGIN_ALLOW_THREADS + rpath = realpath(path, NULL); + Py_END_ALLOW_THREADS + if (rpath == NULL) + return posix_error_with_allocated_filename(opath); + + Py_DECREF(opath); + orpath = PyUnicode_DecodeFSDefault(rpath); + free(rpath); + return orpath; +} +#endif + + /* This is used for fpathconf(), pathconf(), confstr() and sysconf(). * It maps strings representing configuration variable names to * integer values, allowing those functions to be called with the @@ -9736,6 +9766,9 @@ #ifdef HAVE_PATHCONF {"pathconf", posix_pathconf, METH_VARARGS, posix_pathconf__doc__}, #endif +#ifdef HAVE_REALPATH + {"realpath", posix_realpath, METH_VARARGS, posix_realpath__doc__}, +#endif /* HAVE_READLINK */ {"abort", posix_abort, METH_NOARGS, posix_abort__doc__}, #ifdef MS_WINDOWS {"_getfullpathname", posix__getfullpathname, METH_VARARGS, NULL},