6666from pyfakefs import mox3_stubout
6767from pyfakefs .extra_packages import pathlib , pathlib2 , use_scandir
6868
69-
7069if use_scandir :
7170 from pyfakefs import fake_scandir
7271
@@ -81,7 +80,8 @@ def patchfs(_func=None, *,
8180 allow_root_user = True ,
8281 use_known_patches = True ,
8382 patch_open_code = PatchMode .OFF ,
84- patch_default_args = False ):
83+ patch_default_args = False ,
84+ use_cache = True ):
8585 """Convenience decorator to use patcher with additional parameters in a
8686 test function.
8787
@@ -106,7 +106,8 @@ def wrapped(*args, **kwargs):
106106 allow_root_user = allow_root_user ,
107107 use_known_patches = use_known_patches ,
108108 patch_open_code = patch_open_code ,
109- patch_default_args = False ) as p :
109+ patch_default_args = patch_default_args ,
110+ use_cache = use_cache ) as p :
110111 args = list (args )
111112 args .append (p .fs )
112113 return f (* args , ** kwargs )
@@ -213,7 +214,8 @@ def setUpPyfakefs(self,
213214 allow_root_user = True ,
214215 use_known_patches = True ,
215216 patch_open_code = PatchMode .OFF ,
216- patch_default_args = False ):
217+ patch_default_args = False ,
218+ use_cache = True ):
217219 """Bind the file-related modules to the :py:class:`pyfakefs` fake file
218220 system instead of the real file system. Also bind the fake `open()`
219221 function.
@@ -238,7 +240,8 @@ def setUpPyfakefs(self,
238240 allow_root_user = allow_root_user ,
239241 use_known_patches = use_known_patches ,
240242 patch_open_code = patch_open_code ,
241- patch_default_args = patch_default_args
243+ patch_default_args = patch_default_args ,
244+ use_cache = use_cache
242245 )
243246
244247 self ._stubber .setUp ()
@@ -382,7 +385,8 @@ def __init__(self, additional_skip_names=None,
382385 modules_to_reload = None , modules_to_patch = None ,
383386 allow_root_user = True , use_known_patches = True ,
384387 patch_open_code = PatchMode .OFF ,
385- patch_default_args = False ):
388+ patch_default_args = False ,
389+ use_cache = True ):
386390 """
387391 Args:
388392 additional_skip_names: names of modules inside of which no module
@@ -406,6 +410,13 @@ def __init__(self, additional_skip_names=None,
406410 patch_open_code: If True, `io.open_code` is patched. The default
407411 is not to patch it, as it mostly is used to load compiled
408412 modules that are not in the fake file system.
413+ patch_default_args: If True, default arguments are checked for
414+ file system functions, which are patched. This check is
415+ expansive, so it is off by default.
416+ use_cache: If True (default), non-patched modules are cached
417+ between tests for performance reasons. As this is a new
418+ feature, this argument allows to turn it off in case it
419+ causes any problems.
409420 """
410421
411422 if not allow_root_user :
@@ -434,9 +445,10 @@ def __init__(self, additional_skip_names=None,
434445 if modules_to_reload is not None :
435446 self .modules_to_reload .extend (modules_to_reload )
436447 self .patch_default_args = patch_default_args
448+ self .use_cache = use_cache
437449 if patch_default_args != self .PATCH_DEFAULT_ARGS :
438450 self .__class__ .PATCH_DEFAULT_ARGS = patch_default_args
439- self .__class__ . CACHED_SKIPMODULES = set ()
451+ self .clear_cache ()
440452
441453 if use_known_patches :
442454 modules_to_patch = modules_to_patch or {}
@@ -450,7 +462,7 @@ def __init__(self, additional_skip_names=None,
450462 patched_module_names = set (modules_to_patch )
451463 if patched_module_names != self .PATCHED_MODULE_NAMES :
452464 self .__class__ .PATCHED_MODULE_NAMES = patched_module_names
453- self .__class__ . CACHED_SKIPMODULES = set ()
465+ self .clear_cache ()
454466
455467 self ._fake_module_functions = {}
456468 self ._init_fake_module_functions ()
@@ -472,6 +484,10 @@ def __init__(self, additional_skip_names=None,
472484 self ._patching = False
473485 self .found_fs_module = False
474486
487+ def clear_cache (self ):
488+ """Clear the cache of non-patched modules."""
489+ self .__class__ .CACHED_SKIPMODULES = set ()
490+
475491 def _init_fake_module_classes (self ):
476492 # IMPORTANT TESTING NOTE: Whenever you add a new module below, test
477493 # it by adding an attribute in fixtures/module_with_attributes.py
@@ -567,16 +583,6 @@ def _is_fs_module(self, mod, name, module_names):
567583 # by side effects of inspect methods
568584 return False
569585
570- def _is_skipped_fs_module (self , mod , name , module_names ):
571- try :
572- # check for __name__ first and ignore the AttributeException
573- # if it does not exist - avoids calling expansive ismodule
574- return mod .__name__ in module_names and inspect .ismodule (mod )
575- except Exception :
576- # handle AttributeError and any other exception possibly triggered
577- # by side effects of inspect.ismodule
578- return False
579-
580586 def _is_fs_function (self , fct ):
581587 try :
582588 # check for __name__ first and ignore the AttributeException
@@ -623,6 +629,11 @@ def _def_values(self, item):
623629 # _DontDoThat() (see #523)
624630 pass
625631
632+ def _find_def_values (self , module_items ):
633+ for _ , fct in module_items :
634+ for f , i , d in self ._def_values (fct ):
635+ self ._def_functions .append ((f , i , d ))
636+
626637 def _find_modules (self ):
627638 """Find and cache all modules that import file system modules.
628639 Later, `setUp()` will stub these with the fake file system
@@ -631,7 +642,7 @@ def _find_modules(self):
631642 module_names = list (self ._fake_module_classes .keys ()) + [PATH_MODULE ]
632643 for name , module in list (sys .modules .items ()):
633644 try :
634- if (module in self .CACHED_SKIPMODULES or
645+ if (self . use_cache and module in self .CACHED_SKIPMODULES or
635646 module in self .SKIPMODULES or
636647 not inspect .ismodule (module )):
637648 continue
@@ -640,7 +651,8 @@ def _find_modules(self):
640651 # where py.error has no __name__ attribute
641652 # see https://github.com/pytest-dev/py/issues/73
642653 # and any other exception triggered by inspect.ismodule
643- self .__class__ .SKIPMODULES .add (module )
654+ if self .use_cache :
655+ self .__class__ .CACHED_SKIPMODULES .add (module )
644656 continue
645657 self .found_fs_module = False
646658 skipped = (any ([sn .startswith (module .__name__ )
@@ -663,16 +675,15 @@ def _find_modules(self):
663675 module_items
664676 if self ._is_fs_function (fct )}
665677
666- # find default arguments that are file system functions
667- if self .patch_default_args :
668- for _ , fct in module_items :
669- for f , i , d in self ._def_values (fct ):
670- self ._def_functions .append ((f , i , d ))
671-
672678 for name , fct in functions .items ():
673679 self ._fct_modules .setdefault (
674680 (name , fct .__name__ , fct .__module__ ), set ()).add (module )
675- if not self .found_fs_module :
681+
682+ # find default arguments that are file system functions
683+ if self .patch_default_args :
684+ self ._find_def_values (module_items )
685+
686+ if not self .found_fs_module and self .use_cache :
676687 self .__class__ .CACHED_SKIPMODULES .add (module )
677688
678689 def _refresh (self ):
0 commit comments