Skip to content
Open
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
1 change: 1 addition & 0 deletions newsfragments/214.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added ``Path.is_symlink()`` for compatibility with ``pathlib``. Deprecated ``Path.islink()`` in favor of ``is_symlink()``.
15 changes: 12 additions & 3 deletions path/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1025,10 +1025,19 @@ def is_file(self) -> bool:
""".. seealso:: :func:`os.path.isfile`"""
return self.module.isfile(self)

def islink(self) -> bool:
""".. seealso:: :func:`os.path.islink`"""
def is_symlink(self) -> bool:
""".. seealso:: :func:`os.path.islink`, :meth:`pathlib.Path.is_symlink`"""
return self.module.islink(self)

def islink(self) -> bool:
"""Deprecated alias for :meth:`is_symlink`."""
warnings.warn(
"islink is deprecated; use is_symlink",
DeprecationWarning,
stacklevel=2,
)
return self.is_symlink()

def ismount(self) -> bool:
"""
>>> Path('.').ismount()
Expand Down Expand Up @@ -1661,7 +1670,7 @@ def ignored(item: Self) -> bool:

for source in itertools.filterfalse(ignored, sources):
dest = dst_path / source.name
if symlinks and source.islink():
if symlinks and source.is_symlink():
target = source.readlink()
target.symlink(dest)
elif source.is_dir():
Expand Down
35 changes: 31 additions & 4 deletions tests/test_path.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,33 @@ def test_get_owner(self):


class TestLinks:
@pytest.mark.parametrize('target_type', ['file', 'directory', 'missing'])
@pytest.mark.parametrize('symlink', [False, True])
def test_is_symlink(self, tmp_path, target_type, symlink):
target = tmp_path / 'target'
if target_type == 'file':
target.touch()
elif target_type == 'directory':
target.mkdir()
candidate = target
if symlink:
candidate = tmp_path / 'link'
candidate.symlink_to(target, target_is_directory=target_type == 'directory')

assert Path(candidate).is_symlink() is symlink

@pytest.mark.parametrize('symlink', [False, True])
def test_islink_deprecated(self, tmp_path, symlink):
candidate = tmp_path / 'link'
if symlink:
candidate.symlink_to(tmp_path / 'missing')

with pytest.warns(
DeprecationWarning, match='islink is deprecated; use is_symlink'
) as recorded:
assert Path(candidate).islink() is symlink
assert recorded[0].filename == __file__

def test_hardlink_to(self, tmpdir):
target = Path(tmpdir) / 'target'
target.write_text('hello', encoding='utf-8')
Expand Down Expand Up @@ -397,7 +424,7 @@ def test_skip_symlinks(self, tmpdir):
assert len(list(root.walk())) == 4

skip_links = path.Traversal(
lambda item: item.is_dir() and not item.islink(),
lambda item: item.is_dir() and not item.is_symlink(),
)
assert len(list(skip_links(root.walk()))) == 3

Expand Down Expand Up @@ -681,7 +708,7 @@ def test_shutil(self, tmpdir):
testC.iterdir(),
[testC / testCopy.name, testC / testFile.name, testCopyOfLink],
)
assert not testCopyOfLink.islink()
assert not testCopyOfLink.is_symlink()

# Clean up for another try.
testC.rmtree()
Expand All @@ -695,7 +722,7 @@ def test_shutil(self, tmpdir):
[testC / testCopy.name, testC / testFile.name, testCopyOfLink],
)
if hasattr(os, 'symlink'):
assert testCopyOfLink.islink()
assert testCopyOfLink.is_symlink()
assert testCopyOfLink.realpath() == testFile

# Clean up.
Expand Down Expand Up @@ -896,7 +923,7 @@ def testing_structure(self, tmpdir):

def check_link(self):
target = Path(self.subdir_b / self.test_link.name)
check = target.islink if hasattr(os, 'symlink') else target.is_file
check = target.is_symlink if hasattr(os, 'symlink') else target.is_file
assert check()

def test_with_nonexisting_dst_kwargs(self):
Expand Down