diff --git a/CHANGES.md b/CHANGES.md index 08fa02a8..ac90166d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,8 @@ The released versions correspond to PyPI releases. * fixes handling of unhashable modules which cannot be cached (see [#923](../../issues/923)) * reload modules loaded by the dynamic patcher instead of removing them - sometimes they may not be reloaded automatically (see [#932](../../issues/932)) +* add back argument `use_dynamic_patch` as a fallback for similar problems + ## [Version 5.3.2](https://pypi.python.org/pypi/pyfakefs/5.3.2) (2023-11-30) Bugfix release. diff --git a/docs/usage.rst b/docs/usage.rst index 95376b06..20291f48 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -657,6 +657,13 @@ If you want to clear the cache just for a specific test instead, you can call fs.clear_cache() ... +use_dynamic_patch +~~~~~~~~~~~~~~~~~ +If ``True`` (the default), dynamic patching after setup is used (for example +for modules loaded locally inside of functions). +Can be switched off if it causes unwanted side effects, which happened at least in +once instance while testing a django project. + .. _convenience_methods: diff --git a/pyfakefs/fake_filesystem_unittest.py b/pyfakefs/fake_filesystem_unittest.py index b0bd238d..f43e14b1 100644 --- a/pyfakefs/fake_filesystem_unittest.py +++ b/pyfakefs/fake_filesystem_unittest.py @@ -105,6 +105,7 @@ def patchfs( patch_open_code: PatchMode = PatchMode.OFF, patch_default_args: bool = False, use_cache: bool = True, + use_dynamic_patch: bool = True, ) -> Callable: """Convenience decorator to use patcher with additional parameters in a test function. @@ -132,6 +133,7 @@ def wrapped(*args, **kwargs): patch_open_code=patch_open_code, patch_default_args=patch_default_args, use_cache=use_cache, + use_dynamic_patch=use_dynamic_patch, ) as p: args = list(args) args.append(p.fs) @@ -167,6 +169,7 @@ def load_doctests( use_known_patches: bool = True, patch_open_code: PatchMode = PatchMode.OFF, patch_default_args: bool = False, + use_dynamic_patch: bool = True, ) -> TestSuite: # pylint:disable=unused-argument """Load the doctest tests for the specified module into unittest. Args: @@ -186,6 +189,7 @@ def load_doctests( use_known_patches=use_known_patches, patch_open_code=patch_open_code, patch_default_args=patch_default_args, + use_dynamic_patch=use_dynamic_patch, is_doc_test=True, ) assert Patcher.DOC_PATCHER is not None @@ -312,6 +316,7 @@ def setUpClassPyfakefs( patch_open_code: PatchMode = PatchMode.OFF, patch_default_args: bool = False, use_cache: bool = True, + use_dynamic_patch: bool = True, ) -> None: """Similar to :py:func:`setUpPyfakefs`, but as a class method that can be used in `setUpClass` instead of in `setUp`. @@ -349,6 +354,7 @@ def setUpClassPyfakefs( patch_open_code=patch_open_code, patch_default_args=patch_default_args, use_cache=use_cache, + use_dynamic_patch=use_dynamic_patch, ) Patcher.PATCHER.setUp() @@ -513,6 +519,7 @@ def __init__( patch_open_code: PatchMode = PatchMode.OFF, patch_default_args: bool = False, use_cache: bool = True, + use_dynamic_patch: bool = True, is_doc_test: bool = False, ) -> None: """ @@ -545,6 +552,9 @@ def __init__( cached between tests for performance reasons. As this is a new feature, this argument allows to turn it off in case it causes any problems. + use_dynamic_patch: If `True`, dynamic patching after setup is used + (for example for modules loaded locally inside of functions). + Can be switched off if it causes unwanted side effects. """ self.is_doc_test = is_doc_test if is_doc_test: @@ -582,6 +592,7 @@ def __init__( self.modules_to_reload.extend(modules_to_reload) self.patch_default_args = patch_default_args self.use_cache = use_cache + self.use_dynamic_patch = use_dynamic_patch if use_known_patches: from pyfakefs.patched_packages import ( @@ -914,6 +925,9 @@ def start_patching(self) -> None: for module in self.modules_to_reload: if sys.modules.get(module.__name__) is module: reload(module) + if not self.use_dynamic_patch: + self._dyn_patcher.cleanup() + sys.meta_path.pop(0) def patch_functions(self) -> None: assert self._stubs is not None @@ -989,7 +1003,7 @@ def stop_patching(self, temporary=False) -> None: if self._stubs: self._stubs.smart_unset_all() self.unset_defaults() - if self._dyn_patcher: + if self.use_dynamic_patch and self._dyn_patcher: self._dyn_patcher.cleanup() sys.meta_path.pop(0)