1import contextlib 2import _imp 3import importlib 4import importlib.util 5import os 6import shutil 7import sys 8import unittest 9import warnings 10 11from .os_helper import unlink, temp_dir 12 13 14@contextlib.contextmanager 15def _ignore_deprecated_imports(ignore=True): 16 """Context manager to suppress package and module deprecation 17 warnings when importing them. 18 19 If ignore is False, this context manager has no effect. 20 """ 21 if ignore: 22 with warnings.catch_warnings(): 23 warnings.filterwarnings("ignore", ".+ (module|package)", 24 DeprecationWarning) 25 yield 26 else: 27 yield 28 29 30def unload(name): 31 try: 32 del sys.modules[name] 33 except KeyError: 34 pass 35 36 37def forget(modname): 38 """'Forget' a module was ever imported. 39 40 This removes the module from sys.modules and deletes any PEP 3147/488 or 41 legacy .pyc files. 42 """ 43 unload(modname) 44 for dirname in sys.path: 45 source = os.path.join(dirname, modname + '.py') 46 # It doesn't matter if they exist or not, unlink all possible 47 # combinations of PEP 3147/488 and legacy pyc files. 48 unlink(source + 'c') 49 for opt in ('', 1, 2): 50 unlink(importlib.util.cache_from_source(source, optimization=opt)) 51 52 53def make_legacy_pyc(source): 54 """Move a PEP 3147/488 pyc file to its legacy pyc location. 55 56 :param source: The file system path to the source file. The source file 57 does not need to exist, however the PEP 3147/488 pyc file must exist. 58 :return: The file system path to the legacy pyc file. 59 """ 60 pyc_file = importlib.util.cache_from_source(source) 61 assert source.endswith('.py') 62 legacy_pyc = source + 'c' 63 shutil.move(pyc_file, legacy_pyc) 64 return legacy_pyc 65 66 67def import_module(name, deprecated=False, *, required_on=()): 68 """Import and return the module to be tested, raising SkipTest if 69 it is not available. 70 71 If deprecated is True, any module or package deprecation messages 72 will be suppressed. If a module is required on a platform but optional for 73 others, set required_on to an iterable of platform prefixes which will be 74 compared against sys.platform. 75 """ 76 with _ignore_deprecated_imports(deprecated): 77 try: 78 return importlib.import_module(name) 79 except ImportError as msg: 80 if sys.platform.startswith(tuple(required_on)): 81 raise 82 raise unittest.SkipTest(str(msg)) 83 84 85def _save_and_remove_modules(names): 86 orig_modules = {} 87 prefixes = tuple(name + '.' for name in names) 88 for modname in list(sys.modules): 89 if modname in names or modname.startswith(prefixes): 90 orig_modules[modname] = sys.modules.pop(modname) 91 return orig_modules 92 93 94@contextlib.contextmanager 95def frozen_modules(enabled=True): 96 """Force frozen modules to be used (or not). 97 98 This only applies to modules that haven't been imported yet. 99 Also, some essential modules will always be imported frozen. 100 """ 101 _imp._override_frozen_modules_for_tests(1 if enabled else -1) 102 try: 103 yield 104 finally: 105 _imp._override_frozen_modules_for_tests(0) 106 107 108@contextlib.contextmanager 109def multi_interp_extensions_check(enabled=True): 110 """Force legacy modules to be allowed in subinterpreters (or not). 111 112 ("legacy" == single-phase init) 113 114 This only applies to modules that haven't been imported yet. 115 It overrides the PyInterpreterConfig.check_multi_interp_extensions 116 setting (see support.run_in_subinterp_with_config() and 117 _interpreters.create()). 118 119 Also see importlib.utils.allowing_all_extensions(). 120 """ 121 old = _imp._override_multi_interp_extensions_check(1 if enabled else -1) 122 try: 123 yield 124 finally: 125 _imp._override_multi_interp_extensions_check(old) 126 127 128def import_fresh_module(name, fresh=(), blocked=(), *, 129 deprecated=False, 130 usefrozen=False, 131 ): 132 """Import and return a module, deliberately bypassing sys.modules. 133 134 This function imports and returns a fresh copy of the named Python module 135 by removing the named module from sys.modules before doing the import. 136 Note that unlike reload, the original module is not affected by 137 this operation. 138 139 *fresh* is an iterable of additional module names that are also removed 140 from the sys.modules cache before doing the import. If one of these 141 modules can't be imported, None is returned. 142 143 *blocked* is an iterable of module names that are replaced with None 144 in the module cache during the import to ensure that attempts to import 145 them raise ImportError. 146 147 The named module and any modules named in the *fresh* and *blocked* 148 parameters are saved before starting the import and then reinserted into 149 sys.modules when the fresh import is complete. 150 151 Module and package deprecation messages are suppressed during this import 152 if *deprecated* is True. 153 154 This function will raise ImportError if the named module cannot be 155 imported. 156 157 If "usefrozen" is False (the default) then the frozen importer is 158 disabled (except for essential modules like importlib._bootstrap). 159 """ 160 # NOTE: test_heapq, test_json and test_warnings include extra sanity checks 161 # to make sure that this utility function is working as expected 162 with _ignore_deprecated_imports(deprecated): 163 # Keep track of modules saved for later restoration as well 164 # as those which just need a blocking entry removed 165 fresh = list(fresh) 166 blocked = list(blocked) 167 names = {name, *fresh, *blocked} 168 orig_modules = _save_and_remove_modules(names) 169 for modname in blocked: 170 sys.modules[modname] = None 171 172 try: 173 with frozen_modules(usefrozen): 174 # Return None when one of the "fresh" modules can not be imported. 175 try: 176 for modname in fresh: 177 __import__(modname) 178 except ImportError: 179 return None 180 return importlib.import_module(name) 181 finally: 182 _save_and_remove_modules(names) 183 sys.modules.update(orig_modules) 184 185 186class CleanImport(object): 187 """Context manager to force import to return a new module reference. 188 189 This is useful for testing module-level behaviours, such as 190 the emission of a DeprecationWarning on import. 191 192 Use like this: 193 194 with CleanImport("foo"): 195 importlib.import_module("foo") # new reference 196 197 If "usefrozen" is False (the default) then the frozen importer is 198 disabled (except for essential modules like importlib._bootstrap). 199 """ 200 201 def __init__(self, *module_names, usefrozen=False): 202 self.original_modules = sys.modules.copy() 203 for module_name in module_names: 204 if module_name in sys.modules: 205 module = sys.modules[module_name] 206 # It is possible that module_name is just an alias for 207 # another module (e.g. stub for modules renamed in 3.x). 208 # In that case, we also need delete the real module to clear 209 # the import cache. 210 if module.__name__ != module_name: 211 del sys.modules[module.__name__] 212 del sys.modules[module_name] 213 self._frozen_modules = frozen_modules(usefrozen) 214 215 def __enter__(self): 216 self._frozen_modules.__enter__() 217 return self 218 219 def __exit__(self, *ignore_exc): 220 sys.modules.update(self.original_modules) 221 self._frozen_modules.__exit__(*ignore_exc) 222 223 224class DirsOnSysPath(object): 225 """Context manager to temporarily add directories to sys.path. 226 227 This makes a copy of sys.path, appends any directories given 228 as positional arguments, then reverts sys.path to the copied 229 settings when the context ends. 230 231 Note that *all* sys.path modifications in the body of the 232 context manager, including replacement of the object, 233 will be reverted at the end of the block. 234 """ 235 236 def __init__(self, *paths): 237 self.original_value = sys.path[:] 238 self.original_object = sys.path 239 sys.path.extend(paths) 240 241 def __enter__(self): 242 return self 243 244 def __exit__(self, *ignore_exc): 245 sys.path = self.original_object 246 sys.path[:] = self.original_value 247 248 249def modules_setup(): 250 return sys.modules.copy(), 251 252 253def modules_cleanup(oldmodules): 254 # Encoders/decoders are registered permanently within the internal 255 # codec cache. If we destroy the corresponding modules their 256 # globals will be set to None which will trip up the cached functions. 257 encodings = [(k, v) for k, v in sys.modules.items() 258 if k.startswith('encodings.')] 259 sys.modules.clear() 260 sys.modules.update(encodings) 261 # XXX: This kind of problem can affect more than just encodings. 262 # In particular extension modules (such as _ssl) don't cope 263 # with reloading properly. Really, test modules should be cleaning 264 # out the test specific modules they know they added (ala test_runpy) 265 # rather than relying on this function (as test_importhooks and test_pkg 266 # do currently). Implicitly imported *real* modules should be left alone 267 # (see issue 10556). 268 sys.modules.update(oldmodules) 269 270 271@contextlib.contextmanager 272def isolated_modules(): 273 """ 274 Save modules on entry and cleanup on exit. 275 """ 276 (saved,) = modules_setup() 277 try: 278 yield 279 finally: 280 modules_cleanup(saved) 281 282 283def mock_register_at_fork(func): 284 # bpo-30599: Mock os.register_at_fork() when importing the random module, 285 # since this function doesn't allow to unregister callbacks and would leak 286 # memory. 287 from unittest import mock 288 return mock.patch('os.register_at_fork', create=True)(func) 289 290 291@contextlib.contextmanager 292def ready_to_import(name=None, source=""): 293 from test.support import script_helper 294 295 # 1. Sets up a temporary directory and removes it afterwards 296 # 2. Creates the module file 297 # 3. Temporarily clears the module from sys.modules (if any) 298 # 4. Reverts or removes the module when cleaning up 299 name = name or "spam" 300 with temp_dir() as tempdir: 301 path = script_helper.make_script(tempdir, name, source) 302 old_module = sys.modules.pop(name, None) 303 try: 304 sys.path.insert(0, tempdir) 305 yield name, path 306 sys.path.remove(tempdir) 307 finally: 308 if old_module is not None: 309 sys.modules[name] = old_module 310 else: 311 sys.modules.pop(name, None) 312