diff --git a/Doc/library/os.path.rst b/Doc/library/os.path.rst index a5abacf0214..eb34e1f98d3 100644 --- a/Doc/library/os.path.rst +++ b/Doc/library/os.path.rst @@ -377,7 +377,7 @@ the :mod:`glob` module.) Accepts a :term:`path-like object`. -.. function:: samefile(path1, path2) +.. function:: samefile(path1, path2, follow_symlinks=True) Return ``True`` if both pathname arguments refer to the same file or directory. This is determined by the device number and i-node number and raises an @@ -394,6 +394,9 @@ the :mod:`glob` module.) .. versionchanged:: 3.6 Accepts a :term:`path-like object`. + .. versionchanged:: 3.10 + Supports optional argument ``follow_symlinks``. + .. function:: sameopenfile(fp1, fp2) diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 2bbf3aad619..be09210d3a3 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -1078,7 +1078,7 @@ call fails (for example because the path doesn't exist). Remove this directory. The directory must be empty. -.. method:: Path.samefile(other_path) +.. method:: Path.samefile(other_path, follow_symlinks=True) Return whether this path points to the same file as *other_path*, which can be either a Path object, or a string. The semantics are similar @@ -1098,6 +1098,9 @@ call fails (for example because the path doesn't exist). .. versionadded:: 3.5 + .. versionchanged:: 3.10 + Supports optional argument ``follow_symlinks``. + .. method:: Path.symlink_to(target, target_is_directory=False) diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index aa547ff4648..76e84b82074 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -264,6 +264,9 @@ descriptors without copying between kernel address space and user address space, where one of the file descriptors must refer to a pipe. (Contributed by Pablo Galindo in :issue:`41625`.) +Added optional ``follow_symlinks`` argument to :func:`os.path.samefile`. +(Contributed by Ross Rhodes in :issue:`42778`.) + pathlib ------- @@ -274,6 +277,9 @@ Added negative indexing support to :attr:`PurePath.parents `. (Contributed by Yaroslav Pankovych in :issue:`21041`) +Added optional ``follow_symlinks`` argument to :func:`pathlib.samefile`. +(Contributed by Ross Rhodes in :issue:`42778`.) + platform -------- diff --git a/Lib/genericpath.py b/Lib/genericpath.py index ce36451a3af..21dc565af45 100644 --- a/Lib/genericpath.py +++ b/Lib/genericpath.py @@ -91,14 +91,14 @@ def samestat(s1, s2): # Are two filenames really pointing to the same file? -def samefile(f1, f2): +def samefile(f1, f2, follow_symlinks=True): """Test whether two pathnames reference the same actual file or directory This is determined by the device number and i-node number and raises an exception if an os.stat() call on either pathname fails. """ - s1 = os.stat(f1) - s2 = os.stat(f2) + s1 = os.stat(f1, follow_symlinks=follow_symlinks) + s2 = os.stat(f2, follow_symlinks=follow_symlinks) return samestat(s1, s2) diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 531a699a40d..aa35eb3e724 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -1134,15 +1134,15 @@ class Path(PurePath): """ return cls(cls()._flavour.gethomedir(None)) - def samefile(self, other_path): + def samefile(self, other_path, follow_symlinks=True): """Return whether other_path is the same or not as this file (as returned by os.path.samefile()). """ - st = self.stat() + st = self.stat(follow_symlinks=follow_symlinks) try: - other_st = other_path.stat() + other_st = other_path.stat(follow_symlinks=follow_symlinks) except AttributeError: - other_st = self._accessor.stat(other_path) + other_st = self._accessor.stat(other_path, follow_symlinks=follow_symlinks) return os.path.samestat(st, other_st) def iterdir(self): @@ -1216,12 +1216,12 @@ class Path(PurePath): obj._init(template=self) return obj - def stat(self): + def stat(self, follow_symlinks=True): """ Return the result of the stat() system call on this path, like os.stat() does. """ - return self._accessor.stat(self) + return self._accessor.stat(self, follow_symlinks=follow_symlinks) def owner(self): """ diff --git a/Lib/test/test_genericpath.py b/Lib/test/test_genericpath.py index 1ff7f75ad3e..bf26b04221c 100644 --- a/Lib/test/test_genericpath.py +++ b/Lib/test/test_genericpath.py @@ -227,7 +227,8 @@ class GenericTest: self.assertRaises(TypeError, self.pathmodule.samefile) - def _test_samefile_on_link_func(self, func): + @os_helper.skip_unless_symlink + def test_samefile_on_symlink(self): test_fn1 = os_helper.TESTFN test_fn2 = os_helper.TESTFN + "2" self.addCleanup(os_helper.unlink, test_fn1) @@ -235,20 +236,32 @@ class GenericTest: create_file(test_fn1) - func(test_fn1, test_fn2) + os.symlink(test_fn1, test_fn2) self.assertTrue(self.pathmodule.samefile(test_fn1, test_fn2)) + self.assertFalse(self.pathmodule.samefile(test_fn1, test_fn2, follow_symlinks=False)) os.remove(test_fn2) create_file(test_fn2) self.assertFalse(self.pathmodule.samefile(test_fn1, test_fn2)) - - @os_helper.skip_unless_symlink - def test_samefile_on_symlink(self): - self._test_samefile_on_link_func(os.symlink) + self.assertFalse(self.pathmodule.samefile(test_fn1, test_fn2, follow_symlinks=False)) def test_samefile_on_link(self): try: - self._test_samefile_on_link_func(os.link) + test_fn1 = os_helper.TESTFN + test_fn2 = os_helper.TESTFN + "2" + self.addCleanup(os_helper.unlink, test_fn1) + self.addCleanup(os_helper.unlink, test_fn2) + + create_file(test_fn1) + + os.link(test_fn1, test_fn2) + self.assertTrue(self.pathmodule.samefile(test_fn1, test_fn2)) + self.assertTrue(self.pathmodule.samefile(test_fn1, test_fn2, follow_symlinks=False)) + os.remove(test_fn2) + + create_file(test_fn2) + self.assertFalse(self.pathmodule.samefile(test_fn1, test_fn2)) + self.assertFalse(self.pathmodule.samefile(test_fn1, test_fn2, follow_symlinks=False)) except PermissionError as e: self.skipTest('os.link(): %s' % e) diff --git a/Misc/ACKS b/Misc/ACKS index 211455b4dfc..ff5093cc996 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1431,6 +1431,7 @@ Bernhard Reiter Steven Reiz Roeland Rengelink Antoine Reversat +Ross Rhodes Flávio Ribeiro Francesco Ricciardi Tim Rice diff --git a/Misc/NEWS.d/next/Library/2020-12-29-12-32-49.bpo-42778.SNVoXG.rst b/Misc/NEWS.d/next/Library/2020-12-29-12-32-49.bpo-42778.SNVoXG.rst new file mode 100644 index 00000000000..90fbe3b47c9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-12-29-12-32-49.bpo-42778.SNVoXG.rst @@ -0,0 +1,2 @@ +Add 'follow_symlinks' optional argument to os.path and pathlib samefile +methods