Skip to content

Commit 50e7ef4

Browse files
committed
Remove "in the fake filesystem" from OSError message
- makes it more consistent with real messages - documentation: add a few links to the intro
1 parent e1b407e commit 50e7ef4

File tree

8 files changed

+54
-23
lines changed

8 files changed

+54
-23
lines changed

CHANGES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ The released versions correspond to PyPI releases.
1414

1515
## Unreleased
1616

17+
### Changes
18+
* the message from an `OSError` raised in the fake filesystem has no longer the postfix
19+
_"in the fake filesystem"_ (see [#1159](../../discussions/1159))
20+
1721
### Enhancements
1822
* added convenience function `add_package_metadata` to add the metadata of a given
1923
package to the fake filesystem (see [#1155](../../issues/1155))

docs/convenience.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,8 @@ and you may fail to create new files if the fake file system is full.
214214
To get the file system size, you may use :py:meth:`get_disk_usage()<pyfakefs.fake_filesystem.FakeFilesystem.get_disk_usage>`, which is
215215
modeled after ``shutil.disk_usage()``.
216216

217+
.. _pause_resume:
218+
217219
Suspending patching
218220
~~~~~~~~~~~~~~~~~~~
219221
Sometimes, you may want to access the real filesystem inside the test with
@@ -262,6 +264,8 @@ Here is the same code using a context manager:
262264
assert not os.path.exists(real_temp_file.name)
263265
assert os.path.exists(fake_temp_file.name)
264266
267+
.. _simulate_os:
268+
265269
Simulating other file systems
266270
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
267271
``Pyfakefs`` supports Linux, macOS and Windows operating systems. By default,

docs/intro.rst

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,29 +34,29 @@ Features
3434
the real filesystem will work on the fake filesystem if running under
3535
``pyfakefs``.
3636

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

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

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

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

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

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

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

6161
.. _limitations:
6262

docs/usage.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Test Scenarios
55
--------------
66
There are several approaches for implementing tests using ``pyfakefs``.
77

8+
.. _pytest_plugin:
89

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

58+
.. _unittest_usage:
59+
5760
Patch using fake_filesystem_unittest
5861
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5962
If you are using the Python ``unittest`` package, the easiest approach is to

pyfakefs/fake_file.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,7 @@ def add_entry(self, path_object: FakeFile) -> None:
531531
and not self.filesystem.is_windows_fs
532532
and not self.has_permission(helpers.PERM_WRITE)
533533
):
534-
raise OSError(errno.EACCES, "Permission Denied", self.path)
534+
self.filesystem.raise_os_error(errno.EACCES, self.path)
535535

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

850850
def close(self) -> None:
851851
"""Close the file."""
@@ -1258,7 +1258,7 @@ def fileno(self) -> int:
12581258
"""Return the file descriptor of the wrapped standard stream."""
12591259
if self.filedes is not None:
12601260
return self.filedes
1261-
raise OSError(errno.EBADF, "Invalid file descriptor")
1261+
raise OSError(errno.EBADF, os.strerror(errno.EBADF))
12621262

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

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

13931393
def read(self, numBytes: int = -1) -> bytes:
13941394
"""Read from the real pipe."""

pyfakefs/fake_filesystem.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ def raise_os_error(
488488
filename: The name of the affected file, if any.
489489
winerror: Windows only - the specific Windows error code.
490490
"""
491-
message = os.strerror(err_no) + " in the fake filesystem"
491+
message = os.strerror(err_no)
492492
if winerror is not None and sys.platform == "win32" and self.is_windows_fs:
493493
raise OSError(err_no, message, filename, winerror)
494494
raise OSError(err_no, message, filename)
@@ -1885,7 +1885,7 @@ def lresolve(self, path: AnyPath) -> FakeFile:
18851885
"""
18861886
path_str = make_string_path(path)
18871887
if not path_str:
1888-
raise OSError(errno.ENOENT, path_str)
1888+
self.raise_os_error(errno.ENOENT, path_str)
18891889
if path_str == matching_string(path_str, self.root.name):
18901890
# The root directory will never be a link
18911891
return self.root
@@ -1915,7 +1915,7 @@ def lresolve(self, path: AnyPath) -> FakeFile:
19151915
)
19161916
except KeyError:
19171917
pass
1918-
raise OSError(errno.ENOENT, path_str)
1918+
self.raise_os_error(errno.ENOENT, path_str)
19191919

19201920
def add_object(self, file_path: AnyStr, file_object: AnyFile) -> None:
19211921
"""Add a fake file or directory into the filesystem at file_path.

pyfakefs/fake_os.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -259,13 +259,13 @@ def open(
259259
and self.filesystem.exists(path)
260260
and not self.filesystem.isdir(path)
261261
):
262-
raise OSError(errno.ENOTDIR, "path is not a directory", path)
262+
self.filesystem.raise_os_error(errno.ENOTDIR, path)
263263

264264
has_follow_flag = (
265265
hasattr(os, "O_NOFOLLOW") and flags & os.O_NOFOLLOW == os.O_NOFOLLOW
266266
)
267267
if has_follow_flag and self.filesystem.islink(path):
268-
raise OSError(errno.ELOOP, "path is a symlink", path)
268+
self.filesystem.raise_os_error(errno.ELOOP, path)
269269

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

397397
def pipe(self) -> Tuple[int, int]:
398398
read_fd, write_fd = os.pipe()
@@ -453,13 +453,13 @@ def chdir(self, path: AnyStr) -> None:
453453
path = self.filesystem.resolve_path(path, allow_fd=True)
454454
except OSError as exc:
455455
if self.filesystem.is_macos and exc.errno == errno.EBADF:
456-
raise OSError(errno.ENOTDIR, "Not a directory: " + str(path))
456+
self.filesystem.raise_os_error(errno.ENOTDIR, str(path))
457457
elif (
458458
self.filesystem.is_windows_fs
459459
and exc.errno == errno.ENOENT
460460
and path == ""
461461
):
462-
raise OSError(errno.EINVAL, "Invalid argument: + str(path)")
462+
self.filesystem.raise_os_error(errno.EINVAL, str(path))
463463
raise
464464
try:
465465
self.filesystem.confirmdir(path)
@@ -528,7 +528,7 @@ def getxattr(
528528
attribute = attribute.decode(sys.getfilesystemencoding())
529529
file_obj = self.filesystem.resolve(path, follow_symlinks, allow_fd=True)
530530
if attribute not in file_obj.xattr:
531-
raise OSError(errno.ENODATA, "No data available", path)
531+
self.filesystem.raise_os_error(errno.ENODATA, path)
532532
return file_obj.xattr.get(attribute)
533533

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

10311031
def access(
10321032
self,

pyfakefs/tests/fake_filesystem_shutil_test.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,26 @@ def test_copy(self):
212212
self.assertTrue(os.path.exists(dst_file))
213213
self.assertEqual(os.stat(src_file).st_mode, os.stat(dst_file).st_mode)
214214

215+
def test_permission_error_message(self):
216+
self.check_posix_only()
217+
dst_dir = Path(self.make_path("home1"))
218+
src_dir2 = os.path.join(dst_dir, "dir1")
219+
os.makedirs(src_dir2)
220+
dst_dir.chmod(0o555)
221+
src_dir = Path(self.make_path("drive1"))
222+
os.makedirs(src_dir)
223+
src_file = src_dir / "new.txt"
224+
src_file.touch()
225+
226+
msg = f"[Errno 13] Permission denied: '{dst_dir}'"
227+
with self.assertRaises(PermissionError, msg=msg):
228+
shutil.copy2(src_file, dst_dir)
229+
230+
dst_dir2 = dst_dir / "dir2"
231+
msg = f"[Errno 13] Permission denied: '{dst_dir2}'"
232+
with self.assertRaises(PermissionError, msg=msg):
233+
shutil.move(src_dir2, dst_dir2)
234+
215235
def test_copy_directory(self):
216236
src_file = self.make_path("xyzzy")
217237
parent_directory = self.make_path("parent")

0 commit comments

Comments
 (0)