diff --git a/newsfragments/214.feature.rst b/newsfragments/214.feature.rst new file mode 100644 index 0000000..02eda26 --- /dev/null +++ b/newsfragments/214.feature.rst @@ -0,0 +1 @@ +Added ``Path.is_symlink()`` for compatibility with ``pathlib``. Deprecated ``Path.islink()`` in favor of ``is_symlink()``. diff --git a/path/__init__.py b/path/__init__.py index 33dd979..ea0b55c 100644 --- a/path/__init__.py +++ b/path/__init__.py @@ -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() @@ -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(): diff --git a/tests/test_path.py b/tests/test_path.py index b424bb6..35edef9 100644 --- a/tests/test_path.py +++ b/tests/test_path.py @@ -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') @@ -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 @@ -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() @@ -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. @@ -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):