diff --git a/Lib/importlib/test/import_/test_path.py b/Lib/importlib/test/import_/test_path.py --- a/Lib/importlib/test/import_/test_path.py +++ b/Lib/importlib/test/import_/test_path.py @@ -84,7 +84,7 @@ module = '' importer = util.mock_modules(module) hook = import_util.mock_path_hook(os.curdir, importer=importer) - with util.import_state(path=[path], path_hooks=[hook]): + with util.import_state(path=[path], path_hooks=[hook], path_importer_cache={}): loader = machinery.PathFinder.find_module(module) self.assertIs(loader, importer) self.assertIn(os.curdir, sys.path_importer_cache) diff --git a/Lib/importlib/test/util.py b/Lib/importlib/test/util.py --- a/Lib/importlib/test/util.py +++ b/Lib/importlib/test/util.py @@ -49,35 +49,57 @@ except KeyError: pass +# Helpers to save, set, and then restore values in a namespace +def _save_list(src_ns, dst_ns, name, new_value): + obj = getattr(src_ns, name) + dst_ns[name + '_object'] = obj + dst_ns[name + '_value'] = obj[:] + if new_value is not None: + obj[:] = new_value + +def _save_dict(src_ns, dst_ns, name, new_value): + obj = getattr(src_ns, name) + dst_ns[name + '_object'] = obj + dst_ns[name + '_value'] = obj.copy() + if new_value is not None: + obj.clear() + obj.update(new_value) + +def _restore_list(dst_ns, src_ns, name): + setattr(dst_ns, name, src_ns[name + '_object']) + getattr(dst_ns, name)[:] = src_ns[name + '_value'] + +def _restore_dict(dst_ns, src_ns, name): + setattr(dst_ns, name, src_ns[name + '_object']) + getattr(dst_ns, name).clear() + getattr(dst_ns, name).update(src_ns[name + '_value']) + + @contextmanager -def import_state(**kwargs): +def import_state(meta_path=None, path=None, path_hooks=None, + path_importer_cache=None, modules=None): """Context manager to manage the various importers and stored state in the sys module. + """ - The 'modules' attribute is not supported as the interpreter state stores a - pointer to the dict that the interpreter uses internally; - reassigning to sys.modules does not have the desired effect. + # Save the current objects and values, and set them to their new, + # temporary values. + save_ns = {} + _save_list(sys, save_ns, 'meta_path', meta_path) + _save_list(sys, save_ns, 'path', path) + _save_list(sys, save_ns, 'path_hooks', path_hooks) + _save_dict(sys, save_ns, 'path_importer_cache', path_importer_cache) + _save_dict(sys, save_ns, 'modules', modules) - """ - originals = {} try: - for attr, default in (('meta_path', []), ('path', []), - ('path_hooks', []), - ('path_importer_cache', {})): - originals[attr] = getattr(sys, attr) - if attr in kwargs: - new_value = kwargs[attr] - del kwargs[attr] - else: - new_value = default - setattr(sys, attr, new_value) - if len(kwargs): - raise ValueError( - 'unrecognized arguments: {0}'.format(kwargs.keys())) yield finally: - for attr, value in originals.items(): - setattr(sys, attr, value) + # Restore the objects and the values + _restore_list(sys, save_ns, 'meta_path') + _restore_list(sys, save_ns, 'path') + _restore_list(sys, save_ns, 'path_hooks') + _restore_dict(sys, save_ns, 'path_importer_cache') + _restore_dict(sys, save_ns, 'modules') class mock_modules: