[Python-checkins] bpo-39950: add `pathlib.Path.hardlink_to()` method that supersedes `link_to()` (GH-18909)

miss-islington webhook-mailer at python.org
Fri Apr 23 16:49:00 EDT 2021


https://github.com/python/cpython/commit/f24e2e5464ba6498e7b8d73c3f9b417d59fd1b26
commit: f24e2e5464ba6498e7b8d73c3f9b417d59fd1b26
branch: master
author: Barney Gale <barney.gale at gmail.com>
committer: miss-islington <31488909+miss-islington at users.noreply.github.com>
date: 2021-04-23T13:48:52-07:00
summary:

bpo-39950: add `pathlib.Path.hardlink_to()` method that supersedes `link_to()` (GH-18909)



The argument order of `link_to()` is reversed compared to what one may expect, so:

    a.link_to(b)

Might be expected to create *a* as a link to *b*, in fact it creates *b* as a link to *a*, making it function more like a "link from". This doesn't match `symlink_to()` nor the documentation and doesn't seem to be the original author's intent.

This PR deprecates `link_to()` and introduces `hardlink_to()`, which has the same argument order as `symlink_to()`.

files:
A Misc/NEWS.d/next/Library/2021-01-22-00-15-37.bpo-39950.NzLVaR.rst
M Doc/library/pathlib.rst
M Doc/whatsnew/3.10.rst
M Lib/pathlib.py
M Lib/test/test_pathlib.py

diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst
index 122642ad5a41e..b6507eb4d6fa2 100644
--- a/Doc/library/pathlib.rst
+++ b/Doc/library/pathlib.rst
@@ -1140,6 +1140,15 @@ call fails (for example because the path doesn't exist).
       The order of arguments (link, target) is the reverse
       of :func:`os.symlink`'s.
 
+.. method:: Path.hardlink_to(target)
+
+   Make this path a hard link to the same file as *target*.
+
+   .. note::
+      The order of arguments (link, target) is the reverse
+      of :func:`os.link`'s.
+
+   .. versionadded:: 3.10
 
 .. method:: Path.link_to(target)
 
@@ -1149,11 +1158,17 @@ call fails (for example because the path doesn't exist).
 
       This function does not make this path a hard link to *target*, despite
       the implication of the function and argument names. The argument order
-      (target, link) is the reverse of :func:`Path.symlink_to`, but matches
-      that of :func:`os.link`.
+      (target, link) is the reverse of :func:`Path.symlink_to` and
+      :func:`Path.hardlink_to`, but matches that of :func:`os.link`.
 
    .. versionadded:: 3.8
 
+   .. deprecated:: 3.10
+
+      This method is deprecated in favor of :meth:`Path.hardlink_to`, as the
+      argument order of :meth:`Path.link_to`  does not match that of
+      :meth:`Path.symlink_to`.
+
 
 .. method:: Path.touch(mode=0o666, exist_ok=True)
 
@@ -1246,7 +1261,7 @@ Below is a table mapping various :mod:`os` functions to their corresponding
 :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.link`                        :meth:`Path.link_to`
+:func:`os.link`                        :meth:`Path.hardlink_to`
 :func:`os.symlink`                     :meth:`Path.symlink_to`
 :func:`os.readlink`                    :meth:`Path.readlink`
 :func:`os.path.relpath`                :meth:`Path.relative_to` [#]_
diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst
index 86cf11f796a66..247749a48a5f4 100644
--- a/Doc/whatsnew/3.10.rst
+++ b/Doc/whatsnew/3.10.rst
@@ -1007,6 +1007,11 @@ Added negative indexing support to :attr:`PurePath.parents
 <pathlib.PurePath.parents>`.
 (Contributed by Yaroslav Pankovych in :issue:`21041`)
 
+Added :meth:`Path.hardlink_to <pathlib.Path.hardlink_to>` method that
+supersedes :meth:`~pathlib.Path.link_to`. The new method has the same argument
+order as :meth:`~pathlib.Path.symlink_to`.
+(Contributed by Barney Gale in :issue:`39950`.)
+
 platform
 --------
 
@@ -1363,6 +1368,10 @@ Deprecated
 
   (Contributed by Jelle Zijlstra in :issue:`21574`.)
 
+* :meth:`pathlib.Path.link_to` is deprecated and slated for removal in
+  Python 3.12. Use :meth:`pathlib.Path.hardlink_to` instead.
+  (Contributed by Barney Gale in :issue:`39950`.)
+
 
 Removed
 =======
diff --git a/Lib/pathlib.py b/Lib/pathlib.py
index ebc2c02ecc0dc..37934c6038e1d 100644
--- a/Lib/pathlib.py
+++ b/Lib/pathlib.py
@@ -6,6 +6,7 @@
 import posixpath
 import re
 import sys
+import warnings
 from _collections_abc import Sequence
 from errno import EINVAL, ENOENT, ENOTDIR, EBADF, ELOOP
 from operator import attrgetter
@@ -1306,6 +1307,14 @@ def symlink_to(self, target, target_is_directory=False):
         """
         self._accessor.symlink(target, self, target_is_directory)
 
+    def hardlink_to(self, target):
+        """
+        Make this path a hard link pointing to the same file as *target*.
+
+        Note the order of arguments (self, target) is the reverse of os.link's.
+        """
+        self._accessor.link(target, self)
+
     def link_to(self, target):
         """
         Make the target path a hard link pointing to this path.
@@ -1315,7 +1324,13 @@ def link_to(self, target):
         of arguments (target, link) is the reverse of Path.symlink_to, but
         matches that of os.link.
 
+        Deprecated since Python 3.10 and scheduled for removal in Python 3.12.
+        Use `hardlink_to()` instead.
         """
+        warnings.warn("pathlib.Path.link_to() is deprecated and is scheduled "
+                      "for removal in Python 3.12. "
+                      "Use pathlib.Path.hardlink_to() instead.",
+                      DeprecationWarning)
         self._accessor.link(self, target)
 
     # Convenience functions for querying the stat results
diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py
index 0c89b6ef141d7..6ed08f7e70ce3 100644
--- a/Lib/test/test_pathlib.py
+++ b/Lib/test/test_pathlib.py
@@ -1925,7 +1925,8 @@ def test_link_to(self):
         # linking to another path.
         q = P / 'dirA' / 'fileAA'
         try:
-            p.link_to(q)
+            with self.assertWarns(DeprecationWarning):
+                p.link_to(q)
         except PermissionError as e:
             self.skipTest('os.link(): %s' % e)
         self.assertEqual(q.stat().st_size, size)
@@ -1937,6 +1938,24 @@ def test_link_to(self):
         self.assertEqual(os.stat(r).st_size, size)
         self.assertTrue(q.stat)
 
+    @unittest.skipUnless(hasattr(os, "link"), "os.link() is not present")
+    def test_hardlink_to(self):
+        P = self.cls(BASE)
+        target = P / 'fileA'
+        size = target.stat().st_size
+        # linking to another path.
+        link = P / 'dirA' / 'fileAA'
+        link.hardlink_to(target)
+        self.assertEqual(link.stat().st_size, size)
+        self.assertTrue(os.path.samefile(target, link))
+        self.assertTrue(target.exists())
+        # Linking to a str of a relative path.
+        link2 = P / 'dirA' / 'fileAAA'
+        target2 = rel_join('fileA')
+        link2.hardlink_to(target2)
+        self.assertEqual(os.stat(target2).st_size, size)
+        self.assertTrue(link2.exists())
+
     @unittest.skipIf(hasattr(os, "link"), "os.link() is present")
     def test_link_to_not_implemented(self):
         P = self.cls(BASE)
diff --git a/Misc/NEWS.d/next/Library/2021-01-22-00-15-37.bpo-39950.NzLVaR.rst b/Misc/NEWS.d/next/Library/2021-01-22-00-15-37.bpo-39950.NzLVaR.rst
new file mode 100644
index 0000000000000..33b8acf9c18fb
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2021-01-22-00-15-37.bpo-39950.NzLVaR.rst
@@ -0,0 +1,2 @@
+Add `pathlib.Path.hardlink_to()` method that supersedes `link_to()`. The new
+method has the same argument order as `symlink_to()`.



More information about the Python-checkins mailing list