diff --git a/fs/base.py b/fs/base.py index 5eb0b90b..ddfbe5fe 100644 --- a/fs/base.py +++ b/fs/base.py @@ -22,6 +22,7 @@ import six from . import copy, errors, fsencode, iotools, move, tools, walk, wildcard +from .enums import ResourceType from .glob import BoundGlobber from .mode import validate_open_mode from .path import abspath, join, normpath @@ -49,7 +50,6 @@ Union, ) from types import TracebackType - from .enums import ResourceType from .info import Info, RawInfo from .subfs import SubFS from .permissions import Permissions @@ -981,8 +981,10 @@ def islink(self, path): bool: `True` if ``path`` maps to a symlink. """ - self.getinfo(path) - return False + try: + return self.gettype(path) == ResourceType.symlink + except errors.ResourceNotFound: + return False def lock(self): # type: () -> RLock diff --git a/fs/osfs.py b/fs/osfs.py index f854b16a..d3563c89 100644 --- a/fs/osfs.py +++ b/fs/osfs.py @@ -599,7 +599,7 @@ def gettype(self, path): self.check() sys_path = self._to_sys_path(path) with convert_os_errors("gettype", path): - stat = os.stat(sys_path) + stat = os.lstat(sys_path) resource_type = self._get_type_from_stat(stat) return resource_type @@ -608,8 +608,6 @@ def islink(self, path): self.check() _path = self.validatepath(path) sys_path = self._to_sys_path(_path) - if not self.exists(path): - raise errors.ResourceNotFound(path) with convert_os_errors("islink", path): return os.path.islink(sys_path) diff --git a/fs/test.py b/fs/test.py index 53ed290e..18f26dd9 100644 --- a/fs/test.py +++ b/fs/test.py @@ -392,8 +392,7 @@ def test_isdir(self): def test_islink(self): self.fs.touch("foo") self.assertFalse(self.fs.islink("foo")) - with self.assertRaises(errors.ResourceNotFound): - self.fs.islink("bar") + self.assertFalse(self.fs.islink("bar")) def test_getsize(self): self.fs.writebytes("empty", b"") diff --git a/tests/test_osfs.py b/tests/test_osfs.py index f656646c..f16cbfd4 100644 --- a/tests/test_osfs.py +++ b/tests/test_osfs.py @@ -11,6 +11,7 @@ import pytest from fs import osfs, open_fs +from fs.enums import ResourceType from fs.path import relpath, dirname from fs import errors from fs.test import FSTestCases @@ -203,3 +204,19 @@ def test_complex_geturl(self): def test_geturl_return_no_url(self): self.assertRaises(errors.NoURL, self.fs.geturl, "test/path", "upload") + + @pytest.mark.skipif(not hasattr(os, "symlink"), reason="No symlink support") + def test_symlinks_dangling(self): + self.fs.create("a") + os.symlink(self.fs.getsyspath("a"), self.fs.getsyspath("b")) + + self.assertTrue(self.fs.exists("a")) + self.assertFalse(self.fs.islink("a")) + self.assertEqual(self.fs.gettype("a"), ResourceType.file) + self.assertTrue(self.fs.exists("b")) + self.assertTrue(self.fs.islink("b")) + self.assertEqual(self.fs.gettype("b"), ResourceType.symlink) + + self.fs.remove("a") + self.assertTrue(self.fs.islink("b")) + self.assertEqual(self.fs.gettype("b"), ResourceType.symlink)