Skip to content

Remove "in the fake filesystem" from OSError message #1160

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ The released versions correspond to PyPI releases.

## Unreleased

### Changes
* the message from an `OSError` raised in the fake filesystem has no longer the postfix
_"in the fake filesystem"_ (see [#1159](../../discussions/1159))

### Enhancements
* added convenience function `add_package_metadata` to add the metadata of a given
package to the fake filesystem (see [#1155](../../issues/1155))
Expand Down
4 changes: 4 additions & 0 deletions docs/convenience.rst
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@ and you may fail to create new files if the fake file system is full.
To get the file system size, you may use :py:meth:`get_disk_usage()<pyfakefs.fake_filesystem.FakeFilesystem.get_disk_usage>`, which is
modeled after ``shutil.disk_usage()``.

.. _pause_resume:

Suspending patching
~~~~~~~~~~~~~~~~~~~
Sometimes, you may want to access the real filesystem inside the test with
Expand Down Expand Up @@ -262,6 +264,8 @@ Here is the same code using a context manager:
assert not os.path.exists(real_temp_file.name)
assert os.path.exists(fake_temp_file.name)

.. _simulate_os:

Simulating other file systems
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``Pyfakefs`` supports Linux, macOS and Windows operating systems. By default,
Expand Down
16 changes: 8 additions & 8 deletions docs/intro.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,29 +34,29 @@ Features
the real filesystem will work on the fake filesystem if running under
``pyfakefs``.

- ``pyfakefs`` provides direct support for `pytest` (via the `fs` fixture)
and `unittest` (via a `TestCase` base class), but can also be used with
- ``pyfakefs`` provides direct support for `pytest` (see :ref:`pytest_plugin`)
and `unittest` (see :ref:`unittest_usage`), but can also be used with
other test frameworks.

- Each ``pyfakefs`` test starts with an empty (except for the :ref:`os_temporary_directories`) file system,
but it is possible to map files and directories from the real file system into the fake
filesystem if needed.
filesystem if needed (see :ref:`real_fs_access`).

- No files in the real file system are changed during the tests, even in the
case of writing to mapped real files.

- ``pyfakefs`` keeps track of the filesystem size if configured. The file system
size can be configured arbitrarily. It is also possible to create files with a defined
size without setting contents.
size can be configured arbitrarily (see :ref:`set-fs-size`). It is also possible to create files
with a defined size without setting contents.

- It is possible to pause and resume using the fake filesystem, if the
real file system has to be used in a test step.
real file system has to be used in a test step (see :ref:`pause_resume`).

- ``pyfakefs`` defaults to the OS it is running on, but can also be configured
to test code running under another OS (Linux, macOS or Windows).
to test code running under another OS (Linux, macOS or Windows, see :ref:`simulate_os`).

- ``pyfakefs`` can be configured to behave as if running as a root or as a
non-root user, independently from the actual user.
non-root user, independently from the actual user (see :ref:`allow_root_user`).

.. _limitations:

Expand Down
3 changes: 3 additions & 0 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Test Scenarios
--------------
There are several approaches for implementing tests using ``pyfakefs``.

.. _pytest_plugin:

Patch using the pytest plugin
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -54,6 +55,8 @@ respectively.
done in the fake filesystem inside a test will remain there until the respective scope
ends (see also :ref:`nested_patcher_invocation`).

.. _unittest_usage:

Patch using fake_filesystem_unittest
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you are using the Python ``unittest`` package, the easiest approach is to
Expand Down
10 changes: 5 additions & 5 deletions pyfakefs/fake_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,7 @@ def add_entry(self, path_object: FakeFile) -> None:
and not self.filesystem.is_windows_fs
and not self.has_permission(helpers.PERM_WRITE)
):
raise OSError(errno.EACCES, "Permission Denied", self.path)
self.filesystem.raise_os_error(errno.EACCES, self.path)

path_object_name: str = to_string(path_object.name)
if path_object_name in self.entries:
Expand Down Expand Up @@ -845,7 +845,7 @@ def fileno(self) -> int:
"""Return the file descriptor of the file object."""
if self.filedes is not None:
return self.filedes
raise OSError(errno.EBADF, "Invalid file descriptor")
self._filesystem.raise_os_error(errno.EBADF)

def close(self) -> None:
"""Close the file."""
Expand Down Expand Up @@ -1258,7 +1258,7 @@ def fileno(self) -> int:
"""Return the file descriptor of the wrapped standard stream."""
if self.filedes is not None:
return self.filedes
raise OSError(errno.EBADF, "Invalid file descriptor")
raise OSError(errno.EBADF, os.strerror(errno.EBADF))

def read(self, n: int = -1) -> bytes:
return cast(bytes, self._stream_object.read())
Expand Down Expand Up @@ -1313,7 +1313,7 @@ def fileno(self) -> int:
"""Return the file descriptor of the file object."""
if self.filedes is not None:
return self.filedes
raise OSError(errno.EBADF, "Invalid file descriptor")
raise OSError(errno.EBADF, os.strerror(errno.EBADF))

def close(self) -> None:
"""Close the directory."""
Expand Down Expand Up @@ -1388,7 +1388,7 @@ def fileno(self) -> int:
"""Return the fake file descriptor of the pipe object."""
if self.filedes is not None:
return self.filedes
raise OSError(errno.EBADF, "Invalid file descriptor")
raise OSError(errno.EBADF, os.strerror(errno.EBADF))

def read(self, numBytes: int = -1) -> bytes:
"""Read from the real pipe."""
Expand Down
6 changes: 3 additions & 3 deletions pyfakefs/fake_filesystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ def raise_os_error(
filename: The name of the affected file, if any.
winerror: Windows only - the specific Windows error code.
"""
message = os.strerror(err_no) + " in the fake filesystem"
message = os.strerror(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)
Expand Down Expand Up @@ -1885,7 +1885,7 @@ def lresolve(self, path: AnyPath) -> FakeFile:
"""
path_str = make_string_path(path)
if not path_str:
raise OSError(errno.ENOENT, path_str)
self.raise_os_error(errno.ENOENT, path_str)
if path_str == matching_string(path_str, self.root.name):
# The root directory will never be a link
return self.root
Expand Down Expand Up @@ -1915,7 +1915,7 @@ def lresolve(self, path: AnyPath) -> FakeFile:
)
except KeyError:
pass
raise OSError(errno.ENOENT, path_str)
self.raise_os_error(errno.ENOENT, path_str)

def add_object(self, file_path: AnyStr, file_object: AnyFile) -> None:
"""Add a fake file or directory into the filesystem at file_path.
Expand Down
14 changes: 7 additions & 7 deletions pyfakefs/fake_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,13 +259,13 @@ def open(
and self.filesystem.exists(path)
and not self.filesystem.isdir(path)
):
raise OSError(errno.ENOTDIR, "path is not a directory", path)
self.filesystem.raise_os_error(errno.ENOTDIR, path)

has_follow_flag = (
hasattr(os, "O_NOFOLLOW") and flags & os.O_NOFOLLOW == os.O_NOFOLLOW
)
if has_follow_flag and self.filesystem.islink(path):
raise OSError(errno.ELOOP, "path is a symlink", path)
self.filesystem.raise_os_error(errno.ELOOP, path)

has_tmpfile_flag = (
hasattr(os, "O_TMPFILE") and flags & os.O_TMPFILE == os.O_TMPFILE
Expand Down Expand Up @@ -392,7 +392,7 @@ def lseek(self, fd: int, pos: int, whence: int):
if isinstance(file_handle, FakeFileWrapper):
file_handle.seek(pos, whence)
else:
raise OSError(errno.EBADF, "Bad file descriptor for fseek")
self.filesystem.raise_os_error(errno.EBADF)

def pipe(self) -> Tuple[int, int]:
read_fd, write_fd = os.pipe()
Expand Down Expand Up @@ -453,13 +453,13 @@ def chdir(self, path: AnyStr) -> None:
path = self.filesystem.resolve_path(path, allow_fd=True)
except OSError as exc:
if self.filesystem.is_macos and exc.errno == errno.EBADF:
raise OSError(errno.ENOTDIR, "Not a directory: " + str(path))
self.filesystem.raise_os_error(errno.ENOTDIR, str(path))
elif (
self.filesystem.is_windows_fs
and exc.errno == errno.ENOENT
and path == ""
):
raise OSError(errno.EINVAL, "Invalid argument: + str(path)")
self.filesystem.raise_os_error(errno.EINVAL, str(path))
raise
try:
self.filesystem.confirmdir(path)
Expand Down Expand Up @@ -528,7 +528,7 @@ def getxattr(
attribute = attribute.decode(sys.getfilesystemencoding())
file_obj = self.filesystem.resolve(path, follow_symlinks, allow_fd=True)
if attribute not in file_obj.xattr:
raise OSError(errno.ENODATA, "No data available", path)
self.filesystem.raise_os_error(errno.ENODATA, path)
return file_obj.xattr.get(attribute)

def listxattr(
Expand Down Expand Up @@ -1026,7 +1026,7 @@ def ftruncate(self, fd: int, length: int) -> None:
if isinstance(file_object, FakeFileWrapper):
file_object.size = length
else:
raise OSError(errno.EBADF, "Invalid file descriptor")
self.filesystem.raise_os_error(errno.EBADF)

def access(
self,
Expand Down
20 changes: 20 additions & 0 deletions pyfakefs/tests/fake_filesystem_shutil_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,26 @@ def test_copy(self):
self.assertTrue(os.path.exists(dst_file))
self.assertEqual(os.stat(src_file).st_mode, os.stat(dst_file).st_mode)

def test_permission_error_message(self):
self.check_posix_only()
dst_dir = Path(self.make_path("home1"))
src_dir2 = os.path.join(dst_dir, "dir1")
os.makedirs(src_dir2)
dst_dir.chmod(0o555)
src_dir = Path(self.make_path("drive1"))
os.makedirs(src_dir)
src_file = src_dir / "new.txt"
src_file.touch()

msg = f"[Errno 13] Permission denied: '{dst_dir}'"
with self.assertRaises(PermissionError, msg=msg):
shutil.copy2(src_file, dst_dir)

dst_dir2 = dst_dir / "dir2"
msg = f"[Errno 13] Permission denied: '{dst_dir2}'"
with self.assertRaises(PermissionError, msg=msg):
shutil.move(src_dir2, dst_dir2)

def test_copy_directory(self):
src_file = self.make_path("xyzzy")
parent_directory = self.make_path("parent")
Expand Down
Loading