Merge pull request #1026 from samschott/forgiving-follow-symlinks

be more forgiving if follow_symlinks=False is not supported
This commit is contained in:
samschott 2024-03-24 17:35:30 +00:00 committed by GitHub
commit 9502ef0bfd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 24 additions and 26 deletions

View File

@ -211,9 +211,6 @@ class Maestral:
@staticmethod
def _check_system_compatibility() -> None:
if os.stat not in os.supports_follow_symlinks:
raise RuntimeError("Maestral requires lstat support")
if not (IS_MACOS or IS_LINUX):
raise RuntimeError("Only macOS and Linux are supported")

View File

@ -370,7 +370,9 @@ def move(
also a folder.
:param raise_error: Whether to raise errors or return them.
:param preserve_dest_permissions: Whether to apply the permissions of the source
path to the destination path. Permissions will not be set recursively.
path to the destination path. Permissions will not be set recursively and may
will be set for symlinks if this is not supported by the platform, i.e., if
``os.chmod not in os.supports_follow_symlinks``.
:returns: Any caught exception during the move.
"""
err: Optional[OSError] = None
@ -380,7 +382,7 @@ def move(
# save dest permissions
try:
orig_mode = os.lstat(dest_path).st_mode & 0o777
except FileNotFoundError:
except (FileNotFoundError, NotADirectoryError):
pass
try:
@ -392,12 +394,11 @@ def move(
err = exc
else:
if orig_mode:
# reapply dest permissions
# Reapply dest permissions. If the dest is a symlink, only apply permissions
# if this is supported for symlinks by the platform.
try:
if os.chmod in os.supports_follow_symlinks:
os.chmod(dest_path, orig_mode, follow_symlinks=False)
else:
os.chmod(dest_path, orig_mode)
except OSError:
pass
@ -530,36 +531,32 @@ def opener_no_symlink(path: _AnyPath, flags: int) -> int:
return os.open(path, flags=flags)
def _get_stats_no_symlink(path: _AnyPath) -> Optional[os.stat_result]:
try:
return os.lstat(path)
except (FileNotFoundError, NotADirectoryError):
return None
def exists(path: _AnyPath) -> bool:
"""Returns whether an item exists at the path. Returns True for symlinks."""
return _get_stats_no_symlink(path) is not None
try:
os.lstat(path)
except (FileNotFoundError, NotADirectoryError):
return False
else:
return True
def isfile(path: _AnyPath) -> bool:
"""Returns whether a file exists at the path. Returns True for symlinks."""
stat = _get_stats_no_symlink(path)
if stat is None:
return False
else:
try:
stat = os.lstat(path)
return not S_ISDIR(stat.st_mode)
except (FileNotFoundError, NotADirectoryError):
return False
def isdir(path: _AnyPath) -> bool:
"""Returns whether a folder exists at the path. Returns False for symlinks."""
stat = _get_stats_no_symlink(path)
if stat is None:
return False
else:
try:
stat = os.lstat(path)
return S_ISDIR(stat.st_mode)
except (FileNotFoundError, NotADirectoryError):
return False
def getsize(path: _AnyPath) -> int:

View File

@ -450,6 +450,10 @@ def test_excluded_folder_cleared_on_deletion(m: Maestral) -> None:
assert_no_errors(m)
@pytest.mark.skipif(
os.chmod not in os.supports_follow_symlinks,
reason="chmod does not support follow_symlinks=False",
)
def test_unix_permissions(m: Maestral) -> None:
"""
Tests that a newly downloaded file is created with default permissions for our