Skip to content

Commit 9689317

Browse files
authored
Handle other pathlib.Path.open calls in read_text, read_bytes, write_text and write_bytes (#1022)
* Add more regression tests for #1012 * Resolve pytype errors by adding __enter__, __exit__, read and write to file wrappers
1 parent c23e3de commit 9689317

6 files changed

+101
-20
lines changed

CHANGES.md

+6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ The released versions correspond to PyPI releases.
55
* remove support for patching legacy modules `scandir` and `pathlib2`
66
* remove support for Python 3.7
77

8+
## Unreleased
9+
10+
### Fixes
11+
12+
* Use real open calls for remaining `pathlib` functions so that it works nice with skippedmodules (see #1012)
13+
814
## [Version 5.5.0](https://pypi.python.org/pypi/pyfakefs/5.5.0) (2024-05-12)
915
Deprecates the usage of `pathlib2` and `scandir`.
1016

pyfakefs/fake_file.py

+39
Original file line numberDiff line numberDiff line change
@@ -1258,6 +1258,10 @@ def fileno(self) -> int:
12581258
def read(self, n: int = -1) -> bytes:
12591259
return cast(bytes, self._stream_object.read())
12601260

1261+
def write(self, contents: bytes) -> int:
1262+
self._stream_object.write(cast(str, contents))
1263+
return len(contents)
1264+
12611265
def close(self) -> None:
12621266
"""We do not support closing standard streams."""
12631267

@@ -1267,6 +1271,19 @@ def close_fd(self, fd: Optional[int]) -> None:
12671271
def is_stream(self) -> bool:
12681272
return True
12691273

1274+
def __enter__(self) -> "StandardStreamWrapper":
1275+
"""To support usage of this standard stream with the 'with' statement."""
1276+
return self
1277+
1278+
def __exit__(
1279+
self,
1280+
exc_type: Optional[Type[BaseException]],
1281+
exc_val: Optional[BaseException],
1282+
exc_tb: Optional[TracebackType],
1283+
) -> None:
1284+
"""To support usage of this standard stream with the 'with' statement."""
1285+
self.close()
1286+
12701287

12711288
class FakeDirWrapper:
12721289
"""Wrapper for a FakeDirectory object to be used in open files list."""
@@ -1302,6 +1319,28 @@ def close_fd(self, fd: Optional[int]) -> None:
13021319
assert fd is not None
13031320
self._filesystem.close_open_file(fd)
13041321

1322+
def read(self, numBytes: int = -1) -> bytes:
1323+
"""Read from the directory."""
1324+
return self.file_object.read(numBytes)
1325+
1326+
def write(self, contents: bytes) -> int:
1327+
"""Write to the directory."""
1328+
self.file_object.write(contents)
1329+
return len(contents)
1330+
1331+
def __enter__(self) -> "FakeDirWrapper":
1332+
"""To support usage of this fake directory with the 'with' statement."""
1333+
return self
1334+
1335+
def __exit__(
1336+
self,
1337+
exc_type: Optional[Type[BaseException]],
1338+
exc_val: Optional[BaseException],
1339+
exc_tb: Optional[TracebackType],
1340+
) -> None:
1341+
"""To support usage of this fake directory with the 'with' statement."""
1342+
self.close()
1343+
13051344

13061345
class FakePipeWrapper:
13071346
"""Wrapper for a read or write descriptor of a real pipe object to be

pyfakefs/fake_pathlib.py

+23-10
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545

4646
from pyfakefs import fake_scandir
4747
from pyfakefs.fake_filesystem import FakeFilesystem
48-
from pyfakefs.fake_open import FakeFileOpen, fake_open
48+
from pyfakefs.fake_open import fake_open
4949
from pyfakefs.fake_os import FakeOsModule, use_original_os
5050
from pyfakefs.helpers import IS_PYPY
5151

@@ -650,17 +650,25 @@ def read_bytes(self):
650650
OSError: if the target object is a directory, the path is
651651
invalid or permission is denied.
652652
"""
653-
with FakeFileOpen(self.filesystem)(
654-
self._path(), mode="rb"
655-
) as f: # pytype: disable=attribute-error
653+
with fake_open(
654+
self.filesystem,
655+
self.skip_names,
656+
self._path(),
657+
mode="rb",
658+
) as f:
656659
return f.read()
657660

658661
def read_text(self, encoding=None, errors=None):
659662
"""
660663
Open the fake file in text mode, read it, and close the file.
661664
"""
662-
with FakeFileOpen(self.filesystem)( # pytype: disable=attribute-error
663-
self._path(), mode="r", encoding=encoding, errors=errors
665+
with fake_open(
666+
self.filesystem,
667+
self.skip_names,
668+
self._path(),
669+
mode="r",
670+
encoding=encoding,
671+
errors=errors,
664672
) as f:
665673
return f.read()
666674

@@ -674,9 +682,12 @@ def write_bytes(self, data):
674682
"""
675683
# type-check for the buffer interface before truncating the file
676684
view = memoryview(data)
677-
with FakeFileOpen(self.filesystem)(
678-
self._path(), mode="wb"
679-
) as f: # pytype: disable=attribute-error
685+
with fake_open(
686+
self.filesystem,
687+
self.skip_names,
688+
self._path(),
689+
mode="wb",
690+
) as f:
680691
return f.write(view)
681692

682693
def write_text(self, data, encoding=None, errors=None, newline=None):
@@ -701,7 +712,9 @@ def write_text(self, data, encoding=None, errors=None, newline=None):
701712
raise TypeError(
702713
"write_text() got an unexpected " "keyword argument 'newline'"
703714
)
704-
with FakeFileOpen(self.filesystem)( # pytype: disable=attribute-error
715+
with fake_open(
716+
self.filesystem,
717+
self.skip_names,
705718
self._path(),
706719
mode="w",
707720
encoding=encoding,

pyfakefs/tests/fake_open_test.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
from pyfakefs.helpers import is_root, IS_PYPY, get_locale_encoding
2828
from pyfakefs.fake_io import FakeIoModule
2929
from pyfakefs.fake_filesystem_unittest import PatchMode, Patcher
30-
from pyfakefs.tests.skip_open import read_open
30+
from pyfakefs.tests.skipped_pathlib import read_open
3131
from pyfakefs.tests.test_utils import RealFsTestCase
3232

3333

@@ -2107,8 +2107,8 @@ def use_real_fs(self):
21072107

21082108
class SkipOpenTest(unittest.TestCase):
21092109
def test_open_in_skipped_module(self):
2110-
with Patcher(additional_skip_names=["skip_open"]):
2111-
contents = read_open("skip_open.py")
2110+
with Patcher(additional_skip_names=["skipped_pathlib"]):
2111+
contents = read_open("skipped_pathlib.py")
21122112
self.assertTrue(contents.startswith("# Licensed under the Apache License"))
21132113

21142114

pyfakefs/tests/fake_pathlib_test.py

+22-7
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,12 @@
3131

3232
from pyfakefs import fake_pathlib, fake_filesystem, fake_filesystem_unittest, fake_os
3333
from pyfakefs.fake_filesystem import OSType
34-
from pyfakefs.fake_filesystem_unittest import Patcher
3534
from pyfakefs.helpers import IS_PYPY, is_root
36-
from pyfakefs.tests.skip_open import read_pathlib
35+
from pyfakefs.tests.skipped_pathlib import (
36+
read_bytes_pathlib,
37+
read_pathlib,
38+
read_text_pathlib,
39+
)
3740
from pyfakefs.tests.test_utils import RealFsTestMixin
3841

3942
is_windows = sys.platform == "win32"
@@ -1314,12 +1317,24 @@ def test_posix_pure_path_parsing(self):
13141317
)
13151318

13161319

1317-
class SkipOpenTest(unittest.TestCase):
1318-
def test_open_pathlib_in_skipped_module(self):
1320+
class SkipPathlibTest(fake_filesystem_unittest.TestCase):
1321+
def setUp(self):
1322+
self.setUpPyfakefs(additional_skip_names=["skipped_pathlib"])
1323+
1324+
def test_open_in_skipped_module(self):
1325+
# regression test for #1012
1326+
contents = read_pathlib("skipped_pathlib.py")
1327+
self.assertTrue(contents.startswith("# Licensed under the Apache License"))
1328+
1329+
def test_read_text_in_skipped_module(self):
1330+
# regression test for #1012
1331+
contents = read_text_pathlib("skipped_pathlib.py")
1332+
self.assertTrue(contents.startswith("# Licensed under the Apache License"))
1333+
1334+
def test_read_bytes_in_skipped_module(self):
13191335
# regression test for #1012
1320-
with Patcher(additional_skip_names=["skip_open"]):
1321-
contents = read_pathlib("skip_open.py")
1322-
self.assertTrue(contents.startswith("# Licensed under the Apache License"))
1336+
contents = read_bytes_pathlib("skipped_pathlib.py")
1337+
self.assertTrue(contents.startswith(b"# Licensed under the Apache License"))
13231338

13241339

13251340
if __name__ == "__main__":

pyfakefs/tests/skip_open.py pyfakefs/tests/skipped_pathlib.py

+8
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ def read_pathlib(file_name):
2121
return (Path(__file__).parent / file_name).open("r").read()
2222

2323

24+
def read_text_pathlib(file_name):
25+
return (Path(__file__).parent / file_name).read_text()
26+
27+
28+
def read_bytes_pathlib(file_name):
29+
return (Path(__file__).parent / file_name).read_bytes()
30+
31+
2432
def read_open(file_name):
2533
with open(os.path.join(os.path.dirname(__file__), file_name)) as f:
2634
return f.read()

0 commit comments

Comments
 (0)