From a01ba333affcc0677146dc8af57179bdb808d608 Mon Sep 17 00:00:00 2001 From: Girts Date: Wed, 23 Oct 2019 14:18:40 -0700 Subject: [PATCH] bpo-30618: add readlink to pathlib.Path (GH-8285) This adds a "readlink" method to pathlib.Path objects that calls through to os.readlink. https://bugs.python.org/issue30618 Automerge-Triggered-By: @gpshead --- Doc/library/pathlib.rst | 14 ++++++++++++++ Doc/whatsnew/3.9.rst | 7 +++++++ Lib/pathlib.py | 9 +++++++++ Lib/test/test_pathlib.py | 10 ++++++++++ .../2018-07-14-13-48-56.bpo-30618.T5AUF6.rst | 1 + 5 files changed, 41 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2018-07-14-13-48-56.bpo-30618.T5AUF6.rst diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index acbd0e48af2..5906a33bdea 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -944,6 +944,19 @@ call fails (for example because the path doesn't exist). .. versionadded:: 3.5 +.. method:: Path.readlink() + + Return the path to which the symbolic link points (as returned by + :func:`os.readlink`):: + + >>> p = Path('mylink') + >>> p.symlink_to('setup.py') + >>> p.readlink() + PosixPath('setup.py') + + .. versionadded:: 3.9 + + .. method:: Path.rename(target) Rename this file or directory to the given *target*, and return a new Path @@ -1153,6 +1166,7 @@ os and os.path pathlib :func:`os.path.isdir` :meth:`Path.is_dir` :func:`os.path.isfile` :meth:`Path.is_file` :func:`os.path.islink` :meth:`Path.is_symlink` +:func:`os.readlink` :meth:`Path.readlink` :func:`os.stat` :meth:`Path.stat`, :meth:`Path.owner`, :meth:`Path.group` diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index f203930fd1b..b95b0999cb9 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -150,6 +150,13 @@ customization consistently by always using the value specified by case), and one used ``__VENV_NAME__`` instead. (Contributed by Brett Cannon in :issue:`37663`.) +pathlib +------- + +Added :meth:`~pathlib.Path.readlink()` which acts similar to +:func:`~os.readlink`. +(Contributed by Girts Folkmanis in :issue:`30618`) + pprint ------ diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 67b94e01545..825533d8d5f 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -1244,6 +1244,15 @@ class Path(PurePath): with self.open(mode='w', encoding=encoding, errors=errors) as f: return f.write(data) + def readlink(self): + """ + Return the path to which the symbolic link points. + """ + path = self._accessor.readlink(self) + obj = self._from_parts((path,), init=False) + obj._init(template=self) + return obj + def touch(self, mode=0o666, exist_ok=True): """ Create this file with the given access mode, if it doesn't exist. diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index 221c2722d0a..058a201aebc 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -1812,6 +1812,16 @@ class _BasePathTest(object): self.assertEqual(os.stat(r).st_size, size) self.assertFileNotFound(q.stat) + @support.skip_unless_symlink + def test_readlink(self): + P = self.cls(BASE) + self.assertEqual((P / 'linkA').readlink(), self.cls('fileA')) + self.assertEqual((P / 'brokenLink').readlink(), + self.cls('non-existing')) + self.assertEqual((P / 'linkB').readlink(), self.cls('dirB')) + with self.assertRaises(OSError): + (P / 'fileA').readlink() + def test_touch_common(self): P = self.cls(BASE) p = P / 'newfileA' diff --git a/Misc/NEWS.d/next/Library/2018-07-14-13-48-56.bpo-30618.T5AUF6.rst b/Misc/NEWS.d/next/Library/2018-07-14-13-48-56.bpo-30618.T5AUF6.rst new file mode 100644 index 00000000000..35bdb4ef256 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-07-14-13-48-56.bpo-30618.T5AUF6.rst @@ -0,0 +1 @@ +Add :meth:`~pathlib.Path.readlink`. Patch by Girts Folkmanis.