Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions Doc/library/pathlib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1139,6 +1139,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)

Expand All @@ -1148,11 +1157,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)

Expand Down Expand Up @@ -1245,7 +1260,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` [#]_
Expand Down
9 changes: 9 additions & 0 deletions Doc/whatsnew/3.10.rst
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,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
--------

Expand Down Expand Up @@ -1150,6 +1155,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
=======
Expand Down
15 changes: 15 additions & 0 deletions Lib/pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -1305,6 +1306,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.
Expand All @@ -1314,7 +1323,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
Expand Down
21 changes: 20 additions & 1 deletion Lib/test/test_pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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()`.