From c1b8b73c3c5fe294561302ff11be8133e4c54583 Mon Sep 17 00:00:00 2001 From: Martin Larralde Date: Sat, 26 Sep 2020 17:05:41 +0200 Subject: [PATCH 1/8] Add tests to check `OSFS.islink` behaviour --- tests/test_osfs.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/test_osfs.py b/tests/test_osfs.py index f656646c..cb9019ca 100644 --- a/tests/test_osfs.py +++ b/tests/test_osfs.py @@ -203,3 +203,17 @@ 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.assertTrue(self.fs.exists("b")) + self.assertTrue(self.fs.islink("b")) + + self.fs.remove("a") + self.assertTrue(self.fs.islink("b")) + self.assertIs(self.fs.getinfo("b", namespaces=["link"]).target, None) From 4d86fdff2b3da179a2756dded71e9440e85c9801 Mon Sep 17 00:00:00 2001 From: Martin Larralde Date: Sat, 26 Sep 2020 17:11:06 +0200 Subject: [PATCH 2/8] Fix `FSTestCases.test_islink` not to expect errors on non-existing paths --- fs/test.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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"") From 8b1053eb1b127383773f6a305632107de257c6b0 Mon Sep 17 00:00:00 2001 From: Martin Larralde Date: Sat, 26 Sep 2020 17:14:17 +0200 Subject: [PATCH 3/8] Fix base implementation of `FS.islink` to use `Info.target` attribute --- fs/base.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/base.py b/fs/base.py index 5eb0b90b..7d5369cd 100644 --- a/fs/base.py +++ b/fs/base.py @@ -981,8 +981,12 @@ def islink(self, path): bool: `True` if ``path`` maps to a symlink. """ - self.getinfo(path) - return False + try: + self.getinfo(path, namespaces=["link"]).target is not None + except errors.MissingInfoNamespace: + return False # filesystem does not support symlinks + except errors.ResourceNotFound: + return False # path does not exist, so it can't be a link def lock(self): # type: () -> RLock From 907c9c79f968e43d06f53f8b16c5818da6a568e7 Mon Sep 17 00:00:00 2001 From: Martin Larralde Date: Sat, 26 Sep 2020 17:16:41 +0200 Subject: [PATCH 4/8] Path `OSFS.islink` not to raise `ResourceNotFound` on non-existing path --- fs/osfs.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/osfs.py b/fs/osfs.py index f854b16a..0eda729a 100644 --- a/fs/osfs.py +++ b/fs/osfs.py @@ -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) From f5b01d6d6a4dfbd8b5f44797d6f63bc5ee8e5f3b Mon Sep 17 00:00:00 2001 From: Martin Larralde Date: Sat, 26 Sep 2020 17:31:03 +0200 Subject: [PATCH 5/8] Use `os.lstat` instead of `os.stat` in `OSFS.gettype` --- fs/osfs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/osfs.py b/fs/osfs.py index 0eda729a..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 From 1cff26bb775be4b306cd95cbf662e19c9634bbdc Mon Sep 17 00:00:00 2001 From: Martin Larralde Date: Sat, 26 Sep 2020 17:32:29 +0200 Subject: [PATCH 6/8] Make `OSFS.test_symlinks_dangling` also check the `gettype` method --- tests/test_osfs.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_osfs.py b/tests/test_osfs.py index cb9019ca..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 @@ -211,9 +212,11 @@ def test_symlinks_dangling(self): 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.assertIs(self.fs.getinfo("b", namespaces=["link"]).target, None) + self.assertEqual(self.fs.gettype("b"), ResourceType.symlink) From 3ce045485ee614278ee5b18149172e28bae1ee6e Mon Sep 17 00:00:00 2001 From: Martin Larralde Date: Sat, 26 Sep 2020 17:40:24 +0200 Subject: [PATCH 7/8] Fix base `FS.islink` to use `FS.gettype` instead of the `target` attribute --- fs/base.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/fs/base.py b/fs/base.py index 7d5369cd..1a8ac5a3 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 @@ -982,11 +983,9 @@ def islink(self, path): """ try: - self.getinfo(path, namespaces=["link"]).target is not None - except errors.MissingInfoNamespace: - return False # filesystem does not support symlinks + return self.gettype(path) == ResourceType.symlink except errors.ResourceNotFound: - return False # path does not exist, so it can't be a link + return False def lock(self): # type: () -> RLock From 6a60aa58c99daf72a2fbc9a4095342104674a397 Mon Sep 17 00:00:00 2001 From: Martin Larralde Date: Sat, 26 Sep 2020 18:07:07 +0200 Subject: [PATCH 8/8] Fix duplicate imports in `fs.base` --- fs/base.py | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/base.py b/fs/base.py index 1a8ac5a3..ddfbe5fe 100644 --- a/fs/base.py +++ b/fs/base.py @@ -50,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