Skip to content

Commit

Permalink
Avoid reloading glob in Python 3.13
Browse files Browse the repository at this point in the history
- patch glob instead
- avoids performance hit on tests
  • Loading branch information
mrbean-bremen committed Sep 29, 2024
1 parent 20a0c34 commit 636069b
Show file tree
Hide file tree
Showing 4 changed files with 13 additions and 16 deletions.
3 changes: 2 additions & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ The released versions correspond to PyPI releases.
* the `additional_skip_names` parameter now works with more modules (see [#1023](../../issues/1023))
* added support for `os.fchmod`, allow file descriptor argument for `os.chmod` only for POSIX
for Python < 3.13
* avoid reloading `glob` in Python 3.13 (did affect test performance)

### Fixes
* removing files while iterating over `scandir` results is now possible (see [#1051](../../issues/1051))
* fake `pathlib.PosixPath` and `pathlib.WindowsPath` now behave more like in the real filesystem
(see [#1055](../../issues/1055))
(see [#1053](../../issues/1053))

## [Version 5.6.0](https://pypi.python.org/pypi/pyfakefs/5.6.0) (2024-07-12)
Adds preliminary Python 3.13 support.
Expand Down
12 changes: 9 additions & 3 deletions pyfakefs/fake_filesystem_unittest.py
Original file line number Diff line number Diff line change
Expand Up @@ -593,9 +593,6 @@ def __init__(
self.modules_to_reload: List[ModuleType] = (
[] if sys.platform == "win32" else [tempfile]
)
if sys.version_info >= (3, 13):
# need to reload glob which holds references to os functions
self.modules_to_reload.append(glob)
if modules_to_reload is not None:
self.modules_to_reload.extend(modules_to_reload)
self.patch_default_args = patch_default_args
Expand Down Expand Up @@ -983,6 +980,7 @@ def start_patching(self) -> None:
self.patch_modules()
self.patch_functions()
self.patch_defaults()
self._set_glob_os_functions()

self._dyn_patcher = DynamicPatcher(self)
sys.meta_path.insert(0, self._dyn_patcher)
Expand All @@ -993,6 +991,13 @@ def start_patching(self) -> None:
self._dyn_patcher.cleanup()
sys.meta_path.pop(0)

def _set_glob_os_functions(self):
# make sure the os functions cached in glob are patched
if sys.version_info >= (3, 13):
globber = glob._StringGlobber # type: ignore[module-attr]
globber.lstat = staticmethod(os.lstat)
globber.scandir = staticmethod(os.scandir)

def patch_functions(self) -> None:
assert self._stubs is not None
for (name, ft_name, ft_mod), modules in self.FS_FUNCTIONS.items():
Expand Down Expand Up @@ -1073,6 +1078,7 @@ def stop_patching(self, temporary=False) -> None:
if self.linecache_updatecache is not None:
linecache.updatecache = self.linecache_updatecache
linecache.checkcache = self.linecache_checkcache
self._set_glob_os_functions()

@property
def is_patching(self):
Expand Down
10 changes: 0 additions & 10 deletions pyfakefs/fake_pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
import errno
import fnmatch
import functools
import glob
import inspect
import ntpath
import os
Expand Down Expand Up @@ -640,15 +639,6 @@ def _init(self, template=None):
# only needed until Python 3.8
self._closed = False

if sys.version_info >= (3, 13):

def _glob_selector(self, parts, case_sensitive, recurse_symlinks):
# make sure we get the patched version of the globber
self._globber = glob._StringGlobber # type: ignore[module-attr]
return super()._glob_selector( # type: ignore[attribute-error]
parts, case_sensitive, recurse_symlinks
)

def _path(self):
"""Returns the underlying path string as used by the fake
filesystem.
Expand Down
4 changes: 2 additions & 2 deletions pyfakefs/tests/fake_pathlib_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -822,11 +822,11 @@ def test_glob(self):
self.create_file(self.make_path("foo", "setup.pyc"))
path = self.path(self.make_path("foo"))
self.assertEqual(
sorted(path.glob("*.py")),
[
self.path(self.make_path("foo", "all_tests.py")),
self.path(self.make_path("foo", "setup.py")),
],
sorted(path.glob("*.py")),
)

@unittest.skipIf(not is_windows, "Windows specific test")
Expand All @@ -837,12 +837,12 @@ def test_glob_case_windows(self):
self.create_file(self.make_path("foo", "example.Py"))
path = self.path(self.make_path("foo"))
self.assertEqual(
sorted(path.glob("*.py")),
[
self.path(self.make_path("foo", "all_tests.PY")),
self.path(self.make_path("foo", "example.Py")),
self.path(self.make_path("foo", "setup.py")),
],
sorted(path.glob("*.py")),
)

@unittest.skipIf(is_windows, "Posix specific test")
Expand Down

0 comments on commit 636069b

Please sign in to comment.