Skip to content

Fix recursion on dynamic import from fake file #1144

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
Mar 15, 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
6 changes: 6 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ The released versions correspond to PyPI releases.
* the default for `FakeFilesystem.shuffle_listdir_results` will change to `True` to reflect
the real filesystem behavior

## Unreleased

### Fixes
* fixed handling of dynamic imports from code in the fake filesystem in Python > 3.11
(see [#1121](../../issues/1121))

## [Version 5.8.0](https://pypi.python.org/pypi/pyfakefs/5.8.0) (2025-03-11)
Adds preliminary support for Python 3.14.

Expand Down
4 changes: 3 additions & 1 deletion pyfakefs/fake_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ def open(
newline: Optional[str] = None,
closefd: bool = True,
opener: Optional[Callable] = None,
is_fake_open_code: bool = False,
) -> Union[AnyFileWrapper, IO[Any]]:
"""Redirect the call to FakeFileOpen.
See FakeFileOpen.call() for description.
Expand All @@ -101,6 +102,7 @@ def open(
newline,
closefd,
opener,
is_fake_open_code,
)

if sys.version_info >= (3, 8):
Expand All @@ -118,7 +120,7 @@ def open_code(self, path):
and self.filesystem.exists(path)
or patch_mode == PatchMode.ON
):
return self.open(path, mode="rb")
return self.open(path, mode="rb", is_fake_open_code=True)
# mostly this is used for compiled code -
# don't patch these, as the files are probably in the real fs
return self._io_module.open_code(path)
Expand Down
5 changes: 4 additions & 1 deletion pyfakefs/fake_open.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,14 @@ def fake_open(
newline: Optional[str] = None,
closefd: bool = True,
opener: Optional[Callable] = None,
is_fake_open_code: bool = False,
) -> Union[AnyFileWrapper, IO[Any]]:
"""Redirect the call to FakeFileOpen.
See FakeFileOpen.call() for description.
"""
if is_called_from_skipped_module(
# We don't need to check this if we are in an `open_code` call
# from a faked file (and this might cause recursions in `linecache`)
if not is_fake_open_code and is_called_from_skipped_module(
skip_names=skip_names,
case_sensitive=filesystem.is_case_sensitive,
check_open_code=sys.version_info >= (3, 12),
Expand Down
7 changes: 7 additions & 0 deletions pyfakefs/tests/fake_filesystem_unittest_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,13 @@ def test_exec_module_in_fake_fs(self):
self.import_foo()
assert stdout.getvalue() == "hello\n"

def test_dynamic_import(self):
# regression test for #1121
self.fs.create_file("/foo.py")
self.fs.create_file("/bar.py", contents="import foo")
sys.path.insert(0, "")
import foo # noqa


class TestOtherFS(fake_filesystem_unittest.TestCase):
def setUp(self):
Expand Down
Loading