Skip to content

Commit

Permalink
More special handling for paths with trailing separator
Browse files Browse the repository at this point in the history
- fixed renaming symlink ending with trailing separator under linux and macos
- fixes pytest-dev#391
  • Loading branch information
mrbean-bremen committed May 15, 2018
1 parent e0369d3 commit 51c0f58
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 9 deletions.
26 changes: 17 additions & 9 deletions pyfakefs/fake_filesystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -2011,18 +2011,21 @@ def rename(self, old_file_path, new_file_path, force_replace=False):
OSError: if the file would be moved to another filesystem
(e.g. mount point).
"""
ends_with_sep = self.ends_with_path_separator(old_file_path)
old_file_path = self._original_path(self.absnormpath(old_file_path))
new_file_path = self.absnormpath(new_file_path)
if not self.exists(old_file_path, check_link=True):
self.raise_os_error(errno.ENOENT, old_file_path, 2)

old_object = self.lresolve(old_file_path)
if not self.is_windows_fs:
self._handle_posix_dir_link_errors(new_file_path, old_file_path)
self._handle_posix_dir_link_errors(
new_file_path, old_file_path, ends_with_sep)

if self.exists(new_file_path, check_link=True):
new_file_path = self._rename_to_existing_path(
force_replace, new_file_path, old_file_path, old_object)
force_replace, new_file_path, old_file_path,
old_object, ends_with_sep)

if not new_file_path:
return
Expand Down Expand Up @@ -2051,18 +2054,21 @@ def rename(self, old_file_path, new_file_path, force_replace=False):
new_dir_object.remove_entry(new_name)
new_dir_object.add_entry(object_to_rename)

def _handle_posix_dir_link_errors(self, new_file_path, old_file_path):
def _handle_posix_dir_link_errors(self, new_file_path, old_file_path,
ends_with_sep):
if (self.isdir(old_file_path, follow_symlinks=False) and
self.islink(new_file_path)):
self.raise_os_error(errno.ENOTDIR, new_file_path)
if (self.isdir(new_file_path, follow_symlinks=False) and
self.islink(old_file_path)):
self.raise_os_error(errno.EISDIR, new_file_path)
if ends_with_sep and self.is_macos:
return
error = errno.ENOTDIR if ends_with_sep else errno.EISDIR
self.raise_os_error(error, new_file_path)

def _rename_to_existing_path(self, force_replace, new_file_path,
old_file_path, old_object):
old_file_path, old_object, ends_with_sep):
if old_file_path == new_file_path:
new_file_path = None
return # Nothing to do here.

new_object = self.get_object(new_file_path)
Expand All @@ -2071,7 +2077,8 @@ def _rename_to_existing_path(self, force_replace, new_file_path,
new_file_path, old_file_path)
elif (S_ISDIR(new_object.st_mode) or S_ISLNK(new_object.st_mode)):
self._handle_rename_error_for_dir_or_link(
force_replace, new_file_path, new_object, old_object)
force_replace, new_file_path,
new_object, old_object, ends_with_sep)
elif S_ISDIR(old_object.st_mode):
error = errno.EEXIST if self.is_windows_fs else errno.ENOTDIR
self.raise_os_error(error, new_file_path)
Expand All @@ -2086,15 +2093,16 @@ def _rename_to_existing_path(self, force_replace, new_file_path,

def _handle_rename_error_for_dir_or_link(self, force_replace,
new_file_path, new_object,
old_object):
old_object, ends_with_sep):
if self.is_windows_fs:
if force_replace:
self.raise_os_error(errno.EACCES, new_file_path)
else:
self.raise_os_error(errno.EEXIST, new_file_path)
if not S_ISLNK(new_object.st_mode):
if new_object.contents:
self.raise_os_error(errno.ENOTEMPTY, new_file_path)
if not ends_with_sep or not self.is_macos:
self.raise_os_error(errno.ENOTEMPTY, new_file_path)
if S_ISREG(old_object.st_mode):
self.raise_os_error(errno.EISDIR, new_file_path)

Expand Down
23 changes: 23 additions & 0 deletions pyfakefs/tests/fake_os_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2139,6 +2139,29 @@ def test_broken_symlink_with_trailing_sep_windows(self):
self.assert_raises_os_error(
errno.EINVAL, self.os.symlink, path0, path0)

def test_rename_symlink_with_trailing_sep_linux(self):
# Regression test for #391
self.check_linux_only()
path = self.make_path('foo')
self.os.symlink(self.base_path, path)
self.assert_raises_os_error(errno.ENOTDIR, self.os.rename,
path + self.os.sep, self.base_path)

def test_rename_symlink_with_trailing_sep_macos(self):
# Regression test for #391
self.check_macos_only()
path = self.make_path('foo')
self.os.symlink(self.base_path, path)
self.os.rename(path + self.os.sep, self.base_path)

def test_rename_symlink_with_trailing_sep_windows(self):
self.check_windows_only()
self.skip_if_symlink_not_supported()
path = self.make_path('foo')
self.os.symlink(self.base_path, path)
self.assert_raises_os_error(errno.EEXIST, self.os.rename,
path + self.os.sep, self.base_path)

# hard link related tests
def test_link_bogus(self):
# trying to create a link from a non-existent file should fail
Expand Down

0 comments on commit 51c0f58

Please sign in to comment.