Skip to content

PermissionError raised, yet uncaught #643

@nicolashainaux

Description

@nicolashainaux

Describe the bug
Trying to move a file to a directory without write permissions does raise a PermissionError (as expected) that cannot be caught (unexpected).

I'm puzzled about this, but cannot find out where I possibly could have wrongly written the tests, nor can I figure out how to get to catch the exception and why it does not get caught. I notice there's another exception raised "in the same time", that tells FileNotFoundError: [Errno 2] No such file or directory in the fake filesystem: PosixPath('/rootpath/dir2/f1.txt'), but this file has been created. I think it's maybe because it gets deleted at some point during the move() call, though I do not understand why it is deleted before the copy has been done.

How To Reproduce
Here are two tests failing exactly in the same way. fs is the regular pyfakefs's fs fixture.
The situation is the same in both tests, I only changed the test itself: in the first test I only directly try to catch the exception, in the second one, I use the pytest's raises() feature for that.
Both times, the exception is raised, as stated in the logs, but it is not caught.

def test_catch_permission_error01(fs):
    set_uid(1000)
    fs.create_dir('/rootpath/')
    dir1 = fs.create_dir('/rootpath/dir1')
    Path(dir1.path).chmod(0o555)  # remove write permissions
    fs.create_dir('/rootpath/dir2')
    fs.create_file('/rootpath/dir2/f1.txt')

    try:
        shutil.move(Path('/rootpath/dir2/f1.txt'),
                    Path('/rootpath/dir1/f1.txt'))  # PermissionError is raised, but not caught
    except PermissionError:
        print('PERMISSION ERROR')


def test_catch_permission_error02(fs):
    set_uid(1000)
    fs.create_dir('/rootpath/')
    dir1 = fs.create_dir('/rootpath/dir1')
    Path(dir1.path).chmod(0o555)  # remove write permissions
    fs.create_dir('/rootpath/dir2')
    fs.create_file('/rootpath/dir2/f1.txt')

    with pytest.raises(PermissionError) as excinfo:
        shutil.move(Path('/rootpath/dir2/f1.txt'),
                    Path('/rootpath/dir1/f1.txt'))  # PermissionError is raised, but not caught
    assert str(excinfo.value) \
        == "PermissionError: [Errno 13] Permission Denied: '/rootpath/dir1'"

Output of test_catch_permission_error01:

================================================================================== test session starts ==================================================================================
platform linux -- Python 3.8.6, pytest-5.4.3, py-1.10.0, pluggy-0.13.1 -- /home/nico/.local/dev/zano/.venv/py38/bin/python
cachedir: .pytest_cache
rootdir: /home/nico/.local/dev/zano
plugins: mock-3.6.1, pyfakefs-4.5.1
collected 1 item                                                                                                                                                                        

tests/04_tasks_test.py::test_catch_permission_error01 FAILED

======================================================================================= FAILURES ========================================================================================
_____________________________________________________________________________ test_catch_permission_error01 _____________________________________________________________________________

src = PosixPath('/rootpath/dir2/f1.txt'), dst = PosixPath('/rootpath/dir1/f1.txt'), copy_function = <function copy2 at 0x7f580ed268b0>

    def move(src, dst, copy_function=copy2):
        """Recursively move a file or directory to another location. This is
        similar to the Unix "mv" command. Return the file or directory's
        destination.
    
        If the destination is a directory or a symlink to a directory, the source
        is moved inside the directory. The destination path must not already
        exist.
    
        If the destination already exists but is not a directory, it may be
        overwritten depending on os.rename() semantics.
    
        If the destination is on our current filesystem, then rename() is used.
        Otherwise, src is copied to the destination and then removed. Symlinks are
        recreated under the new name if os.rename() fails because of cross
        filesystem renames.
    
        The optional `copy_function` argument is a callable that will be used
        to copy the source or it will be delegated to `copytree`.
        By default, copy2() is used, but any function that supports the same
        signature (like copy()) can be used.
    
        A lot more could be done here...  A look at a mv.c shows a lot of
        the issues this implementation glosses over.
    
        """
        sys.audit("shutil.move", src, dst)
        real_dst = dst
        if os.path.isdir(dst):
            if _samefile(src, dst):
                # We might be on a case insensitive filesystem,
                # perform the rename anyway.
                os.rename(src, dst)
                return
    
            real_dst = os.path.join(dst, _basename(src))
            if os.path.exists(real_dst):
                raise Error("Destination path '%s' already exists" % real_dst)
        try:
>           os.rename(src, real_dst)

/home/nico/.pyenv/versions/3.8.6/lib/python3.8/shutil.py:788: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <pyfakefs.fake_filesystem.FakeOsModule object at 0x7f580da0edf0>, src = '/rootpath/dir2/f1.txt', dst = '/rootpath/dir1/f1.txt'

    def rename(self, src: AnyStr, dst: AnyStr, *,
               src_dir_fd: Optional[int] = None,
               dst_dir_fd: Optional[int] = None) -> None:
        """Rename a FakeFile object at old_file_path to new_file_path,
        preserving all properties.
        Also replaces existing new_file_path object, if one existed
        (Unix only).
    
        Args:
            src: Path to filesystem object to rename.
            dst: Path to where the filesystem object will live
                after this call.
            src_dir_fd: If not `None`, the file descriptor of a directory,
                with `src` being relative to this directory.
            dst_dir_fd: If not `None`, the file descriptor of a directory,
                with `dst` being relative to this directory.
    
        Raises:
            OSError: if old_file_path does not exist.
            OSError: if new_file_path is an existing directory.
            OSError: if new_file_path is an existing file (Windows only)
            OSError: if new_file_path is an existing file and could not
                be removed (Unix)
            OSError: if `dirname(new_file)` does not exist
            OSError: if the file would be moved to another filesystem
                (e.g. mount point)
        """
        src = self._path_with_dir_fd(src, self.rename, src_dir_fd)
        dst = self._path_with_dir_fd(dst, self.rename, dst_dir_fd)
>       self.filesystem.rename(src, dst)

/home/nico/.local/dev/zano/.venv/py38/lib/python3.8/site-packages/pyfakefs/fake_filesystem.py:4309: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <pyfakefs.fake_filesystem.FakeFilesystem object at 0x7f580da90ee0>, old_file_path = '/rootpath/dir2/f1.txt', new_file_path = '/rootpath/dir1/f1.txt', force_replace = False

    def rename(self, old_file_path: AnyPath,
               new_file_path: AnyPath,
               force_replace: bool = False) -> None:
        """Renames a FakeFile object at old_file_path to new_file_path,
        preserving all properties.
    
        Args:
            old_file_path: Path to filesystem object to rename.
            new_file_path: Path to where the filesystem object will live
                after this call.
            force_replace: If set and destination is an existing file, it
                will be replaced even under Windows if the user has
                permissions, otherwise replacement happens under Unix only.
    
        Raises:
            OSError: if old_file_path does not exist.
            OSError: if new_file_path is an existing directory
                (Windows, or Posix if old_file_path points to a regular file)
            OSError: if old_file_path is a directory and new_file_path a file
            OSError: if new_file_path is an existing file and force_replace
                not set (Windows only).
            OSError: if new_file_path is an existing file and could not be
                removed (Posix, or Windows with force_replace set).
            OSError: if dirname(new_file_path) does not exist.
            OSError: if the file would be moved to another filesystem
                (e.g. mount point).
        """
        old_path = make_string_path(old_file_path)
        new_path = make_string_path(new_file_path)
        ends_with_sep = self.ends_with_path_separator(old_path)
        old_path = self.absnormpath(old_path)
        new_path = self.absnormpath(new_path)
        if not self.exists(old_path, check_link=True):
            self.raise_os_error(errno.ENOENT, old_path, 2)
        if ends_with_sep:
            self._handle_broken_link_with_trailing_sep(old_path)
    
        old_object = self.lresolve(old_path)
        if not self.is_windows_fs:
            self._handle_posix_dir_link_errors(
                new_path, old_path, ends_with_sep)
    
        if self.exists(new_path, check_link=True):
            renamed_path = self._rename_to_existing_path(
                force_replace, new_path, old_path,
                old_object, ends_with_sep)
    
            if renamed_path is None:
                return
            else:
                new_path = renamed_path
    
        old_dir, old_name = self.splitpath(old_path)
        new_dir, new_name = self.splitpath(new_path)
        if not self.exists(new_dir):
            self.raise_os_error(errno.ENOENT, new_dir)
        old_dir_object = self.resolve(old_dir)
        new_dir_object = self.resolve(new_dir)
        if old_dir_object.st_dev != new_dir_object.st_dev:
            self.raise_os_error(errno.EXDEV, old_path)
        if not S_ISDIR(new_dir_object.st_mode):
            self.raise_os_error(
                errno.EACCES if self.is_windows_fs else errno.ENOTDIR,
                new_path)
        if new_dir_object.has_parent_object(old_object):
            self.raise_os_error(errno.EINVAL, new_path)
    
        object_to_rename = old_dir_object.get_entry(old_name)
        old_dir_object.remove_entry(old_name, recursive=False)
        object_to_rename.name = new_name
        new_name = new_dir_object._normalized_entryname(new_name)
        if new_name in new_dir_object.entries:
            # in case of overwriting remove the old entry first
            new_dir_object.remove_entry(new_name)
>       new_dir_object.add_entry(object_to_rename)

/home/nico/.local/dev/zano/.venv/py38/lib/python3.8/site-packages/pyfakefs/fake_filesystem.py:2279: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <pyfakefs.fake_filesystem.FakeDirectory object at 0x7f580da78ee0>, path_object = <pyfakefs.fake_filesystem.FakeFile object at 0x7f580e730cd0>

    def add_entry(self, path_object: FakeFile) -> None:
        """Adds a child FakeFile to this directory.
    
        Args:
            path_object: FakeFile instance to add as a child of this directory.
    
        Raises:
            OSError: if the directory has no write permission (Posix only)
            OSError: if the file or directory to be added already exists
        """
        if (not is_root() and not self.st_mode & PERM_WRITE and
                not self.filesystem.is_windows_fs):
>           raise OSError(errno.EACCES, 'Permission Denied', self.path)
E           PermissionError: [Errno 13] Permission Denied: '/rootpath/dir1'

/home/nico/.local/dev/zano/.venv/py38/lib/python3.8/site-packages/pyfakefs/fake_filesystem.py:698: PermissionError

During handling of the above exception, another exception occurred:

fs = <pyfakefs.fake_filesystem.FakeFilesystem object at 0x7f580da90ee0>

    def test_catch_permission_error01(fs):
        set_uid(1000)
        fs.create_dir('/rootpath/')
        dir1 = fs.create_dir('/rootpath/dir1')
        Path(dir1.path).chmod(0o555)  # remove write permissions
        fs.create_dir('/rootpath/dir2')
        fs.create_file('/rootpath/dir2/f1.txt')
    
        try:
>           shutil.move(Path('/rootpath/dir2/f1.txt'),
                        Path('/rootpath/dir1/f1.txt'))

/home/nico/.local/dev/zano/tests/04_tasks_test.py:590: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/home/nico/.pyenv/versions/3.8.6/lib/python3.8/shutil.py:802: in move
    copy_function(src, real_dst)
/home/nico/.pyenv/versions/3.8.6/lib/python3.8/shutil.py:432: in copy2
    copyfile(src, dst, follow_symlinks=follow_symlinks)
/home/nico/.pyenv/versions/3.8.6/lib/python3.8/shutil.py:261: in copyfile
    with open(src, 'rb') as fsrc, open(dst, 'wb') as fdst:
/home/nico/.local/dev/zano/.venv/py38/lib/python3.8/site-packages/pyfakefs/fake_filesystem.py:4838: in open
    return fake_open(file, mode, buffering, encoding, errors,
/home/nico/.local/dev/zano/.venv/py38/lib/python3.8/site-packages/pyfakefs/fake_filesystem.py:5455: in __call__
    return self.call(*args, **kwargs)
/home/nico/.local/dev/zano/.venv/py38/lib/python3.8/site-packages/pyfakefs/fake_filesystem.py:5528: in call
    file_object = self._init_file_object(file_object,
/home/nico/.local/dev/zano/.venv/py38/lib/python3.8/site-packages/pyfakefs/fake_filesystem.py:5587: in _init_file_object
    self.filesystem.raise_os_error(errno.ENOENT, file_path)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <pyfakefs.fake_filesystem.FakeFilesystem object at 0x7f580da90ee0>, err_no = 2, filename = PosixPath('/rootpath/dir2/f1.txt'), winerror = None

    def raise_os_error(self, err_no: int,
                       filename: Optional[AnyString] = None,
                       winerror: Optional[int] = None) -> NoReturn:
        """Raises OSError.
        The error message is constructed from the given error code and shall
        start with the error string issued in the real system.
        Note: this is not true under Windows if winerror is given - in this
        case a localized message specific to winerror will be shown in the
        real file system.
    
        Args:
            err_no: A numeric error code from the C variable errno.
            filename: The name of the affected file, if any.
            winerror: Windows only - the specific Windows error code.
        """
        message = self._error_message(err_no)
        if (winerror is not None and sys.platform == 'win32' and
                self.is_windows_fs):
            raise OSError(err_no, message, filename, winerror)
>       raise OSError(err_no, message, filename)
E       FileNotFoundError: [Errno 2] No such file or directory in the fake filesystem: PosixPath('/rootpath/dir2/f1.txt')

/home/nico/.local/dev/zano/.venv/py38/lib/python3.8/site-packages/pyfakefs/fake_filesystem.py:1057: FileNotFoundError
================================================================================ short test summary info ================================================================================
FAILED tests/04_tasks_test.py::test_catch_permission_error01 - FileNotFoundError: [Errno 2] No such file or directory in the fake filesystem: PosixPath('/rootpath/dir2/f1.txt')
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
=================================================================================== 1 failed in 0.58s ===================================================================================

Output of test_catch_permission_error02:

================================================================================== test session starts ==================================================================================
platform linux -- Python 3.8.6, pytest-5.4.3, py-1.10.0, pluggy-0.13.1 -- /home/nico/.local/dev/zano/.venv/py38/bin/python
cachedir: .pytest_cache
rootdir: /home/nico/.local/dev/zano
plugins: mock-3.6.1, pyfakefs-4.5.1
collected 1 item                                                                                                                                                                        

tests/04_tasks_test.py::test_catch_permission_error02 FAILED

======================================================================================= FAILURES ========================================================================================
_____________________________________________________________________________ test_catch_permission_error02 _____________________________________________________________________________

src = PosixPath('/rootpath/dir2/f1.txt'), dst = PosixPath('/rootpath/dir1/f1.txt'), copy_function = <function copy2 at 0x7f86dd23d8b0>

    def move(src, dst, copy_function=copy2):
        """Recursively move a file or directory to another location. This is
        similar to the Unix "mv" command. Return the file or directory's
        destination.
    
        If the destination is a directory or a symlink to a directory, the source
        is moved inside the directory. The destination path must not already
        exist.
    
        If the destination already exists but is not a directory, it may be
        overwritten depending on os.rename() semantics.
    
        If the destination is on our current filesystem, then rename() is used.
        Otherwise, src is copied to the destination and then removed. Symlinks are
        recreated under the new name if os.rename() fails because of cross
        filesystem renames.
    
        The optional `copy_function` argument is a callable that will be used
        to copy the source or it will be delegated to `copytree`.
        By default, copy2() is used, but any function that supports the same
        signature (like copy()) can be used.
    
        A lot more could be done here...  A look at a mv.c shows a lot of
        the issues this implementation glosses over.
    
        """
        sys.audit("shutil.move", src, dst)
        real_dst = dst
        if os.path.isdir(dst):
            if _samefile(src, dst):
                # We might be on a case insensitive filesystem,
                # perform the rename anyway.
                os.rename(src, dst)
                return
    
            real_dst = os.path.join(dst, _basename(src))
            if os.path.exists(real_dst):
                raise Error("Destination path '%s' already exists" % real_dst)
        try:
>           os.rename(src, real_dst)

/home/nico/.pyenv/versions/3.8.6/lib/python3.8/shutil.py:788: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <pyfakefs.fake_filesystem.FakeOsModule object at 0x7f86dbf27e80>, src = '/rootpath/dir2/f1.txt', dst = '/rootpath/dir1/f1.txt'

    def rename(self, src: AnyStr, dst: AnyStr, *,
               src_dir_fd: Optional[int] = None,
               dst_dir_fd: Optional[int] = None) -> None:
        """Rename a FakeFile object at old_file_path to new_file_path,
        preserving all properties.
        Also replaces existing new_file_path object, if one existed
        (Unix only).
    
        Args:
            src: Path to filesystem object to rename.
            dst: Path to where the filesystem object will live
                after this call.
            src_dir_fd: If not `None`, the file descriptor of a directory,
                with `src` being relative to this directory.
            dst_dir_fd: If not `None`, the file descriptor of a directory,
                with `dst` being relative to this directory.
    
        Raises:
            OSError: if old_file_path does not exist.
            OSError: if new_file_path is an existing directory.
            OSError: if new_file_path is an existing file (Windows only)
            OSError: if new_file_path is an existing file and could not
                be removed (Unix)
            OSError: if `dirname(new_file)` does not exist
            OSError: if the file would be moved to another filesystem
                (e.g. mount point)
        """
        src = self._path_with_dir_fd(src, self.rename, src_dir_fd)
        dst = self._path_with_dir_fd(dst, self.rename, dst_dir_fd)
>       self.filesystem.rename(src, dst)

/home/nico/.local/dev/zano/.venv/py38/lib/python3.8/site-packages/pyfakefs/fake_filesystem.py:4309: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <pyfakefs.fake_filesystem.FakeFilesystem object at 0x7f86dbfa7ee0>, old_file_path = '/rootpath/dir2/f1.txt', new_file_path = '/rootpath/dir1/f1.txt', force_replace = False

    def rename(self, old_file_path: AnyPath,
               new_file_path: AnyPath,
               force_replace: bool = False) -> None:
        """Renames a FakeFile object at old_file_path to new_file_path,
        preserving all properties.
    
        Args:
            old_file_path: Path to filesystem object to rename.
            new_file_path: Path to where the filesystem object will live
                after this call.
            force_replace: If set and destination is an existing file, it
                will be replaced even under Windows if the user has
                permissions, otherwise replacement happens under Unix only.
    
        Raises:
            OSError: if old_file_path does not exist.
            OSError: if new_file_path is an existing directory
                (Windows, or Posix if old_file_path points to a regular file)
            OSError: if old_file_path is a directory and new_file_path a file
            OSError: if new_file_path is an existing file and force_replace
                not set (Windows only).
            OSError: if new_file_path is an existing file and could not be
                removed (Posix, or Windows with force_replace set).
            OSError: if dirname(new_file_path) does not exist.
            OSError: if the file would be moved to another filesystem
                (e.g. mount point).
        """
        old_path = make_string_path(old_file_path)
        new_path = make_string_path(new_file_path)
        ends_with_sep = self.ends_with_path_separator(old_path)
        old_path = self.absnormpath(old_path)
        new_path = self.absnormpath(new_path)
        if not self.exists(old_path, check_link=True):
            self.raise_os_error(errno.ENOENT, old_path, 2)
        if ends_with_sep:
            self._handle_broken_link_with_trailing_sep(old_path)
    
        old_object = self.lresolve(old_path)
        if not self.is_windows_fs:
            self._handle_posix_dir_link_errors(
                new_path, old_path, ends_with_sep)
    
        if self.exists(new_path, check_link=True):
            renamed_path = self._rename_to_existing_path(
                force_replace, new_path, old_path,
                old_object, ends_with_sep)
    
            if renamed_path is None:
                return
            else:
                new_path = renamed_path
    
        old_dir, old_name = self.splitpath(old_path)
        new_dir, new_name = self.splitpath(new_path)
        if not self.exists(new_dir):
            self.raise_os_error(errno.ENOENT, new_dir)
        old_dir_object = self.resolve(old_dir)
        new_dir_object = self.resolve(new_dir)
        if old_dir_object.st_dev != new_dir_object.st_dev:
            self.raise_os_error(errno.EXDEV, old_path)
        if not S_ISDIR(new_dir_object.st_mode):
            self.raise_os_error(
                errno.EACCES if self.is_windows_fs else errno.ENOTDIR,
                new_path)
        if new_dir_object.has_parent_object(old_object):
            self.raise_os_error(errno.EINVAL, new_path)
    
        object_to_rename = old_dir_object.get_entry(old_name)
        old_dir_object.remove_entry(old_name, recursive=False)
        object_to_rename.name = new_name
        new_name = new_dir_object._normalized_entryname(new_name)
        if new_name in new_dir_object.entries:
            # in case of overwriting remove the old entry first
            new_dir_object.remove_entry(new_name)
>       new_dir_object.add_entry(object_to_rename)

/home/nico/.local/dev/zano/.venv/py38/lib/python3.8/site-packages/pyfakefs/fake_filesystem.py:2279: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <pyfakefs.fake_filesystem.FakeDirectory object at 0x7f86dbf8fee0>, path_object = <pyfakefs.fake_filesystem.FakeFile object at 0x7f86dcc47cd0>

    def add_entry(self, path_object: FakeFile) -> None:
        """Adds a child FakeFile to this directory.
    
        Args:
            path_object: FakeFile instance to add as a child of this directory.
    
        Raises:
            OSError: if the directory has no write permission (Posix only)
            OSError: if the file or directory to be added already exists
        """
        if (not is_root() and not self.st_mode & PERM_WRITE and
                not self.filesystem.is_windows_fs):
>           raise OSError(errno.EACCES, 'Permission Denied', self.path)
E           PermissionError: [Errno 13] Permission Denied: '/rootpath/dir1'

/home/nico/.local/dev/zano/.venv/py38/lib/python3.8/site-packages/pyfakefs/fake_filesystem.py:698: PermissionError

During handling of the above exception, another exception occurred:

fs = <pyfakefs.fake_filesystem.FakeFilesystem object at 0x7f86dbfa7ee0>

    def test_catch_permission_error02(fs):
        set_uid(1000)
        fs.create_dir('/rootpath/')
        dir1 = fs.create_dir('/rootpath/dir1')
        Path(dir1.path).chmod(0o555)  # remove write permissions
        fs.create_dir('/rootpath/dir2')
        fs.create_file('/rootpath/dir2/f1.txt')
    
        with pytest.raises(PermissionError) as excinfo:
>           shutil.move(Path('/rootpath/dir2/f1.txt'),
                        Path('/rootpath/dir1/f1.txt'))

/home/nico/.local/dev/zano/tests/04_tasks_test.py:605: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/home/nico/.pyenv/versions/3.8.6/lib/python3.8/shutil.py:802: in move
    copy_function(src, real_dst)
/home/nico/.pyenv/versions/3.8.6/lib/python3.8/shutil.py:432: in copy2
    copyfile(src, dst, follow_symlinks=follow_symlinks)
/home/nico/.pyenv/versions/3.8.6/lib/python3.8/shutil.py:261: in copyfile
    with open(src, 'rb') as fsrc, open(dst, 'wb') as fdst:
/home/nico/.local/dev/zano/.venv/py38/lib/python3.8/site-packages/pyfakefs/fake_filesystem.py:4838: in open
    return fake_open(file, mode, buffering, encoding, errors,
/home/nico/.local/dev/zano/.venv/py38/lib/python3.8/site-packages/pyfakefs/fake_filesystem.py:5455: in __call__
    return self.call(*args, **kwargs)
/home/nico/.local/dev/zano/.venv/py38/lib/python3.8/site-packages/pyfakefs/fake_filesystem.py:5528: in call
    file_object = self._init_file_object(file_object,
/home/nico/.local/dev/zano/.venv/py38/lib/python3.8/site-packages/pyfakefs/fake_filesystem.py:5587: in _init_file_object
    self.filesystem.raise_os_error(errno.ENOENT, file_path)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <pyfakefs.fake_filesystem.FakeFilesystem object at 0x7f86dbfa7ee0>, err_no = 2, filename = PosixPath('/rootpath/dir2/f1.txt'), winerror = None

    def raise_os_error(self, err_no: int,
                       filename: Optional[AnyString] = None,
                       winerror: Optional[int] = None) -> NoReturn:
        """Raises OSError.
        The error message is constructed from the given error code and shall
        start with the error string issued in the real system.
        Note: this is not true under Windows if winerror is given - in this
        case a localized message specific to winerror will be shown in the
        real file system.
    
        Args:
            err_no: A numeric error code from the C variable errno.
            filename: The name of the affected file, if any.
            winerror: Windows only - the specific Windows error code.
        """
        message = self._error_message(err_no)
        if (winerror is not None and sys.platform == 'win32' and
                self.is_windows_fs):
            raise OSError(err_no, message, filename, winerror)
>       raise OSError(err_no, message, filename)
E       FileNotFoundError: [Errno 2] No such file or directory in the fake filesystem: PosixPath('/rootpath/dir2/f1.txt')

/home/nico/.local/dev/zano/.venv/py38/lib/python3.8/site-packages/pyfakefs/fake_filesystem.py:1057: FileNotFoundError
================================================================================ short test summary info ================================================================================
FAILED tests/04_tasks_test.py::test_catch_permission_error02 - FileNotFoundError: [Errno 2] No such file or directory in the fake filesystem: PosixPath('/rootpath/dir2/f1.txt')
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
=================================================================================== 1 failed in 0.58s ===================================================================================

Your environment

$ python -c "import platform; print(platform.platform())"
Linux-5.4.0-89-generic-x86_64-with-glibc2.29
$ python -c "import sys; print('Python', sys.version)"
Python 3.8.6 (default, Jan  9 2021, 22:33:39)
[GCC 9.3.0]
$ python -c "from pyfakefs.fake_filesystem import __version__; print('pyfakefs', __version__)"
pyfakefs 4.5.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions