Skip to content

Commit db3b312

Browse files
authored
Merge pull request RustPython#3488 from deantvv/frozen-import-helper
import_helper: refactor `frozen_modules`
2 parents 38513cb + 45d0285 commit db3b312

File tree

1 file changed

+64
-160
lines changed

1 file changed

+64
-160
lines changed

Lib/test/support/import_helper.py

Lines changed: 64 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -91,204 +91,105 @@ def _save_and_remove_modules(names):
9191
return orig_modules
9292

9393

94-
# XXX RUSTPYTHON: need _imp._override_frozen_modules_for_tests
95-
# @contextlib.contextmanager
96-
# def frozen_modules(enabled=True):
97-
# """Force frozen modules to be used (or not).
98-
99-
# This only applies to modules that haven't been imported yet.
100-
# Also, some essential modules will always be imported frozen.
101-
# """
102-
# _imp._override_frozen_modules_for_tests(1 if enabled else -1)
103-
# try:
104-
# yield
105-
# finally:
106-
# _imp._override_frozen_modules_for_tests(0)
107-
108-
109-
# XXX RUSTPYTHON: new implementation needs fronzen_modules
110-
# def import_fresh_module(name, fresh=(), blocked=(), *,
111-
# deprecated=False,
112-
# usefrozen=False,
113-
# ):
114-
# """Import and return a module, deliberately bypassing sys.modules.
115-
116-
# This function imports and returns a fresh copy of the named Python module
117-
# by removing the named module from sys.modules before doing the import.
118-
# Note that unlike reload, the original module is not affected by
119-
# this operation.
120-
121-
# *fresh* is an iterable of additional module names that are also removed
122-
# from the sys.modules cache before doing the import. If one of these
123-
# modules can't be imported, None is returned.
124-
125-
# *blocked* is an iterable of module names that are replaced with None
126-
# in the module cache during the import to ensure that attempts to import
127-
# them raise ImportError.
128-
129-
# The named module and any modules named in the *fresh* and *blocked*
130-
# parameters are saved before starting the import and then reinserted into
131-
# sys.modules when the fresh import is complete.
132-
133-
# Module and package deprecation messages are suppressed during this import
134-
# if *deprecated* is True.
135-
136-
# This function will raise ImportError if the named module cannot be
137-
# imported.
138-
139-
# If "usefrozen" is False (the default) then the frozen importer is
140-
# disabled (except for essential modules like importlib._bootstrap).
141-
# """
142-
# # NOTE: test_heapq, test_json and test_warnings include extra sanity checks
143-
# # to make sure that this utility function is working as expected
144-
# with _ignore_deprecated_imports(deprecated):
145-
# # Keep track of modules saved for later restoration as well
146-
# # as those which just need a blocking entry removed
147-
# fresh = list(fresh)
148-
# blocked = list(blocked)
149-
# names = {name, *fresh, *blocked}
150-
# orig_modules = _save_and_remove_modules(names)
151-
# for modname in blocked:
152-
# sys.modules[modname] = None
153-
154-
# try:
155-
# with frozen_modules(usefrozen):
156-
# # Return None when one of the "fresh" modules can not be imported.
157-
# try:
158-
# for modname in fresh:
159-
# __import__(modname)
160-
# except ImportError:
161-
# return None
162-
# return importlib.import_module(name)
163-
# finally:
164-
# _save_and_remove_modules(names)
165-
# sys.modules.update(orig_modules)
166-
167-
168-
# TODO RUSTPYTHON: old implementation
169-
def _save_and_remove_module(name, orig_modules):
170-
"""Helper function to save and remove a module from sys.modules
171-
Raise ImportError if the module can't be imported.
94+
# TODO RUSTPYTHON: need _imp._override_frozen_modules_for_tests
95+
# The following implementation is NOT correct and only raise
96+
# exception when it needs enabled=True
97+
@contextlib.contextmanager
98+
def frozen_modules(enabled=True):
99+
"""Force frozen modules to be used (or not).
100+
101+
This only applies to modules that haven't been imported yet.
102+
Also, some essential modules will always be imported frozen.
172103
"""
173-
# try to import the module and raise an error if it can't be imported
174-
if name not in sys.modules:
175-
__import__(name)
176-
del sys.modules[name]
177-
for modname in list(sys.modules):
178-
if modname == name or modname.startswith(name + '.'):
179-
orig_modules[modname] = sys.modules[modname]
180-
del sys.modules[modname]
104+
if enabled:
105+
raise NotImplemented("frozen_modules is not implemented on RustPython")
181106

107+
yield
182108

183-
# TODO RUSTPYTHON: old implementation
184-
def _save_and_block_module(name, orig_modules):
185-
"""Helper function to save and block a module in sys.modules
186-
187-
Return True if the module was in sys.modules, False otherwise.
188-
"""
189-
saved = True
190-
try:
191-
orig_modules[name] = sys.modules[name]
192-
except KeyError:
193-
saved = False
194-
sys.modules[name] = None
195-
return saved
109+
# TODO: original implementation
110+
# _imp._override_frozen_modules_for_tests(1 if enabled else -1)
111+
# try:
112+
# yield
113+
# finally:
114+
# _imp._override_frozen_modules_for_tests(0)
196115

197116

198-
# TODO RUSTPYTHON: old implementation
199-
def import_fresh_module(name, fresh=(), blocked=(), deprecated=False):
117+
# TODO: `frozen_modules` is not supported
118+
def import_fresh_module(name, fresh=(), blocked=(), *,
119+
deprecated=False,
120+
usefrozen=False,
121+
):
200122
"""Import and return a module, deliberately bypassing sys.modules.
123+
201124
This function imports and returns a fresh copy of the named Python module
202125
by removing the named module from sys.modules before doing the import.
203126
Note that unlike reload, the original module is not affected by
204127
this operation.
128+
205129
*fresh* is an iterable of additional module names that are also removed
206-
from the sys.modules cache before doing the import.
130+
from the sys.modules cache before doing the import. If one of these
131+
modules can't be imported, None is returned.
132+
207133
*blocked* is an iterable of module names that are replaced with None
208134
in the module cache during the import to ensure that attempts to import
209135
them raise ImportError.
136+
210137
The named module and any modules named in the *fresh* and *blocked*
211138
parameters are saved before starting the import and then reinserted into
212139
sys.modules when the fresh import is complete.
140+
213141
Module and package deprecation messages are suppressed during this import
214142
if *deprecated* is True.
143+
215144
This function will raise ImportError if the named module cannot be
216145
imported.
146+
147+
If "usefrozen" is False (the default) then the frozen importer is
148+
disabled (except for essential modules like importlib._bootstrap).
217149
"""
218150
# NOTE: test_heapq, test_json and test_warnings include extra sanity checks
219151
# to make sure that this utility function is working as expected
220152
with _ignore_deprecated_imports(deprecated):
221153
# Keep track of modules saved for later restoration as well
222154
# as those which just need a blocking entry removed
223-
orig_modules = {}
224-
names_to_remove = []
225-
_save_and_remove_module(name, orig_modules)
155+
fresh = list(fresh)
156+
blocked = list(blocked)
157+
names = {name, *fresh, *blocked}
158+
orig_modules = _save_and_remove_modules(names)
159+
for modname in blocked:
160+
sys.modules[modname] = None
161+
226162
try:
227-
for fresh_name in fresh:
228-
_save_and_remove_module(fresh_name, orig_modules)
229-
for blocked_name in blocked:
230-
if not _save_and_block_module(blocked_name, orig_modules):
231-
names_to_remove.append(blocked_name)
232-
fresh_module = importlib.import_module(name)
233-
except ImportError:
234-
fresh_module = None
163+
with frozen_modules(usefrozen):
164+
# Return None when one of the "fresh" modules can not be imported.
165+
try:
166+
for modname in fresh:
167+
__import__(modname)
168+
except ImportError:
169+
return None
170+
return importlib.import_module(name)
235171
finally:
236-
for orig_name, module in orig_modules.items():
237-
sys.modules[orig_name] = module
238-
for name_to_remove in names_to_remove:
239-
del sys.modules[name_to_remove]
240-
return fresh_module
241-
242-
# TODO RUSTPYTHON: new implementation needs fronzen_modules
243-
# class CleanImport(object):
244-
# """Context manager to force import to return a new module reference.
245-
246-
# This is useful for testing module-level behaviours, such as
247-
# the emission of a DeprecationWarning on import.
248-
249-
# Use like this:
250-
251-
# with CleanImport("foo"):
252-
# importlib.import_module("foo") # new reference
253-
254-
# If "usefrozen" is False (the default) then the frozen importer is
255-
# disabled (except for essential modules like importlib._bootstrap).
256-
# """
257-
258-
# def __init__(self, *module_names, usefrozen=False):
259-
# self.original_modules = sys.modules.copy()
260-
# for module_name in module_names:
261-
# if module_name in sys.modules:
262-
# module = sys.modules[module_name]
263-
# # It is possible that module_name is just an alias for
264-
# # another module (e.g. stub for modules renamed in 3.x).
265-
# # In that case, we also need delete the real module to clear
266-
# # the import cache.
267-
# if module.__name__ != module_name:
268-
# del sys.modules[module.__name__]
269-
# del sys.modules[module_name]
270-
# self._frozen_modules = frozen_modules(usefrozen)
271-
272-
# def __enter__(self):
273-
# self._frozen_modules.__enter__()
274-
# return self
275-
276-
# def __exit__(self, *ignore_exc):
277-
# sys.modules.update(self.original_modules)
278-
# self._frozen_modules.__exit__(*ignore_exc)
279-
280-
281-
# TODO RUSTPYTHON: old implementation
172+
_save_and_remove_modules(names)
173+
sys.modules.update(orig_modules)
174+
175+
176+
# TODO: `frozen_modules` is not supported
282177
class CleanImport(object):
283178
"""Context manager to force import to return a new module reference.
179+
284180
This is useful for testing module-level behaviours, such as
285181
the emission of a DeprecationWarning on import.
182+
286183
Use like this:
184+
287185
with CleanImport("foo"):
288186
importlib.import_module("foo") # new reference
187+
188+
If "usefrozen" is False (the default) then the frozen importer is
189+
disabled (except for essential modules like importlib._bootstrap).
289190
"""
290191

291-
def __init__(self, *module_names):
192+
def __init__(self, *module_names, usefrozen=False):
292193
self.original_modules = sys.modules.copy()
293194
for module_name in module_names:
294195
if module_name in sys.modules:
@@ -300,12 +201,15 @@ def __init__(self, *module_names):
300201
if module.__name__ != module_name:
301202
del sys.modules[module.__name__]
302203
del sys.modules[module_name]
204+
self._frozen_modules = frozen_modules(usefrozen)
303205

304206
def __enter__(self):
207+
self._frozen_modules.__enter__()
305208
return self
306209

307210
def __exit__(self, *ignore_exc):
308211
sys.modules.update(self.original_modules)
212+
self._frozen_modules.__exit__(*ignore_exc)
309213

310214

311215
class DirsOnSysPath(object):

0 commit comments

Comments
 (0)