|
15 | 15 | """A fake open() function replacement. See ``fake_filesystem`` for usage."""
|
16 | 16 |
|
17 | 17 | import errno
|
| 18 | +import io |
18 | 19 | import os
|
19 | 20 | import sys
|
| 21 | +import traceback |
20 | 22 | from stat import (
|
21 | 23 | S_ISDIR,
|
22 | 24 | )
|
|
28 | 30 | cast,
|
29 | 31 | AnyStr,
|
30 | 32 | TYPE_CHECKING,
|
| 33 | + Callable, |
| 34 | + IO, |
| 35 | + List, |
31 | 36 | )
|
32 | 37 |
|
33 | 38 | from pyfakefs import helpers
|
|
63 | 68 | }
|
64 | 69 |
|
65 | 70 |
|
| 71 | +def fake_open( |
| 72 | + filesystem: "FakeFilesystem", |
| 73 | + skip_names: List[str], |
| 74 | + file: Union[AnyStr, int], |
| 75 | + mode: str = "r", |
| 76 | + buffering: int = -1, |
| 77 | + encoding: Optional[str] = None, |
| 78 | + errors: Optional[str] = None, |
| 79 | + newline: Optional[str] = None, |
| 80 | + closefd: bool = True, |
| 81 | + opener: Optional[Callable] = None, |
| 82 | +) -> Union[AnyFileWrapper, IO[Any]]: |
| 83 | + """Redirect the call to FakeFileOpen. |
| 84 | + See FakeFileOpen.call() for description. |
| 85 | + """ |
| 86 | + # workaround for built-in open called from skipped modules (see #552) |
| 87 | + # as open is not imported explicitly, we cannot patch it for |
| 88 | + # specific modules; instead we check if the caller is a skipped |
| 89 | + # module (should work in most cases) |
| 90 | + stack = traceback.extract_stack(limit=3) |
| 91 | + # handle the case that we try to call the original `open_code` |
| 92 | + # and get here instead (since Python 3.12) |
| 93 | + from_open_code = ( |
| 94 | + sys.version_info >= (3, 12) |
| 95 | + and stack[0].name == "open_code" |
| 96 | + and stack[0].line == "return self._io_module.open_code(path)" |
| 97 | + ) |
| 98 | + module_name = os.path.splitext(stack[0].filename)[0] |
| 99 | + module_name = module_name.replace(os.sep, ".") |
| 100 | + if from_open_code or any( |
| 101 | + [module_name == sn or module_name.endswith("." + sn) for sn in skip_names] |
| 102 | + ): |
| 103 | + return io.open( # pytype: disable=wrong-arg-count |
| 104 | + file, |
| 105 | + mode, |
| 106 | + buffering, |
| 107 | + encoding, |
| 108 | + errors, |
| 109 | + newline, |
| 110 | + closefd, |
| 111 | + opener, |
| 112 | + ) |
| 113 | + fake_file_open = FakeFileOpen(filesystem) |
| 114 | + return fake_file_open( |
| 115 | + file, mode, buffering, encoding, errors, newline, closefd, opener |
| 116 | + ) |
| 117 | + |
| 118 | + |
66 | 119 | class FakeFileOpen:
|
67 | 120 | """Faked `file()` and `open()` function replacements.
|
68 | 121 |
|
@@ -288,7 +341,6 @@ def _init_file_object(
|
288 | 341 | if open_modes.can_write:
|
289 | 342 | if open_modes.truncate:
|
290 | 343 | file_object.set_contents("")
|
291 |
| - file_object |
292 | 344 | else:
|
293 | 345 | if open_modes.must_exist:
|
294 | 346 | self.filesystem.raise_os_error(errno.ENOENT, file_path)
|
@@ -344,7 +396,7 @@ def _handle_file_arg(
|
344 | 396 | can_write,
|
345 | 397 | )
|
346 | 398 |
|
347 |
| - # open a file file by path |
| 399 | + # open a file by path |
348 | 400 | file_path = cast(AnyStr, file_) # pytype: disable=invalid-annotation
|
349 | 401 | if file_path == self.filesystem.dev_null.name:
|
350 | 402 | file_object = self.filesystem.dev_null
|
|
0 commit comments