Issue 5354: Change API for import_fresh_module() to better support test_warnings use case (also fixes some bugs in the original implementation)
This commit is contained in:
parent
aca19e6a74
commit
5533ff6a2e
|
@ -351,15 +351,39 @@ The :mod:`test.test_support` module defines the following functions:
|
||||||
.. versionadded:: 2.7
|
.. versionadded:: 2.7
|
||||||
|
|
||||||
|
|
||||||
.. function:: import_fresh_module(name, blocked_names=None, deprecated=False)
|
.. function:: import_fresh_module(name, fresh=(), blocked=(), deprecated=False)
|
||||||
|
|
||||||
This function imports and returns a fresh copy of the named Python module. The
|
This function imports and returns a fresh copy of the named Python module
|
||||||
``sys.modules`` cache is bypassed temporarily, and the ability to import the
|
by removing the named module from ``sys.modules`` before doing the import.
|
||||||
modules named in *blocked_names* is suppressed for the duration of the import.
|
Note that unlike :func:`reload`, the original module is not affected by
|
||||||
|
this operation.
|
||||||
|
|
||||||
|
*fresh* is an iterable of additional module names that are also removed
|
||||||
|
from the ``sys.modules`` cache before doing the import.
|
||||||
|
|
||||||
|
*blocked* is an iterable of module names that are replaced with :const:`0`
|
||||||
|
in the module cache during the import to ensure that attempts to import
|
||||||
|
them raise :exc:`ImportError`.
|
||||||
|
|
||||||
|
The named module and any modules named in the *fresh* and *blocked*
|
||||||
|
parameters are saved before starting the import and then reinserted into
|
||||||
|
``sys.modules`` when the fresh import is complete.
|
||||||
|
|
||||||
Module and package deprecation messages are suppressed during this import
|
Module and package deprecation messages are suppressed during this import
|
||||||
if *deprecated* is :const:`True`.
|
if *deprecated* is :const:`True`.
|
||||||
|
|
||||||
|
This function will raise :exc:`unittest.SkipTest` is the named module
|
||||||
|
cannot be imported.
|
||||||
|
|
||||||
|
Example use::
|
||||||
|
|
||||||
|
# Get copies of the warnings module for testing without
|
||||||
|
# affecting the version being used by the rest of the test suite
|
||||||
|
# One copy uses the C implementation, the other is forced to use
|
||||||
|
# the pure Python fallback implementation
|
||||||
|
py_warnings = import_fresh_module('warnings', blocked=['_warnings'])
|
||||||
|
c_warnings = import_fresh_module('warnings', fresh=['_warnings'])
|
||||||
|
|
||||||
.. versionadded:: 2.7
|
.. versionadded:: 2.7
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import sys
|
||||||
# We do a bit of trickery here to be able to test both the C implementation
|
# We do a bit of trickery here to be able to test both the C implementation
|
||||||
# and the Python implementation of the module.
|
# and the Python implementation of the module.
|
||||||
import heapq as c_heapq
|
import heapq as c_heapq
|
||||||
py_heapq = test_support.import_fresh_module('heapq', ['_heapq'])
|
py_heapq = test_support.import_fresh_module('heapq', blocked=['_heapq'])
|
||||||
|
|
||||||
class TestHeap(unittest.TestCase):
|
class TestHeap(unittest.TestCase):
|
||||||
module = None
|
module = None
|
||||||
|
|
|
@ -70,12 +70,43 @@ def import_module(name, deprecated=False):
|
||||||
raise unittest.SkipTest(str(msg))
|
raise unittest.SkipTest(str(msg))
|
||||||
|
|
||||||
|
|
||||||
def import_fresh_module(name, blocked_names=None, deprecated=False):
|
def _save_and_remove_module(name, orig_modules):
|
||||||
|
"""Helper function to save and remove a module from sys.modules
|
||||||
|
|
||||||
|
Return value is True if the module was in sys.modules and
|
||||||
|
False otherwise."""
|
||||||
|
saved = True
|
||||||
|
try:
|
||||||
|
orig_modules[name] = sys.modules[name]
|
||||||
|
except KeyError:
|
||||||
|
saved = False
|
||||||
|
else:
|
||||||
|
del sys.modules[name]
|
||||||
|
return saved
|
||||||
|
|
||||||
|
|
||||||
|
def _save_and_block_module(name, orig_modules):
|
||||||
|
"""Helper function to save and block a module in sys.modules
|
||||||
|
|
||||||
|
Return value is True if the module was in sys.modules and
|
||||||
|
False otherwise."""
|
||||||
|
saved = True
|
||||||
|
try:
|
||||||
|
orig_modules[name] = sys.modules[name]
|
||||||
|
except KeyError:
|
||||||
|
saved = False
|
||||||
|
sys.modules[name] = 0
|
||||||
|
return saved
|
||||||
|
|
||||||
|
|
||||||
|
def import_fresh_module(name, fresh=(), blocked=(), deprecated=False):
|
||||||
"""Imports and returns a module, deliberately bypassing the sys.modules cache
|
"""Imports and returns a module, deliberately bypassing the sys.modules cache
|
||||||
and importing a fresh copy of the module. Once the import is complete,
|
and importing a fresh copy of the module. Once the import is complete,
|
||||||
the sys.modules cache is restored to its original state.
|
the sys.modules cache is restored to its original state.
|
||||||
|
|
||||||
Importing of modules named in blocked_names is prevented while the fresh import
|
Modules named in fresh are also imported anew if needed by the import.
|
||||||
|
|
||||||
|
Importing of modules named in blocked is prevented while the fresh import
|
||||||
takes place.
|
takes place.
|
||||||
|
|
||||||
If deprecated is True, any module or package deprecation messages
|
If deprecated is True, any module or package deprecation messages
|
||||||
|
@ -83,21 +114,24 @@ def import_fresh_module(name, blocked_names=None, deprecated=False):
|
||||||
# NOTE: test_heapq and test_warnings include extra sanity checks to make
|
# NOTE: test_heapq and test_warnings include extra sanity checks to make
|
||||||
# sure that this utility function is working as expected
|
# sure that this utility function is working as expected
|
||||||
with _ignore_deprecated_imports(deprecated):
|
with _ignore_deprecated_imports(deprecated):
|
||||||
if blocked_names is None:
|
# Keep track of modules saved for later restoration as well
|
||||||
blocked_names = ()
|
# as those which just need a blocking entry removed
|
||||||
orig_modules = {}
|
orig_modules = {}
|
||||||
if name in sys.modules:
|
names_to_remove = []
|
||||||
orig_modules[name] = sys.modules[name]
|
_save_and_remove_module(name, orig_modules)
|
||||||
del sys.modules[name]
|
|
||||||
try:
|
try:
|
||||||
for blocked in blocked_names:
|
for fresh_name in fresh:
|
||||||
orig_modules[blocked] = sys.modules[blocked]
|
_save_and_remove_module(fresh_name, orig_modules)
|
||||||
sys.modules[blocked] = 0
|
for blocked_name in blocked:
|
||||||
py_module = importlib.import_module(name)
|
if not _save_and_block_module(blocked_name, orig_modules):
|
||||||
|
names_to_remove.append(blocked_name)
|
||||||
|
fresh_module = importlib.import_module(name)
|
||||||
finally:
|
finally:
|
||||||
for blocked, module in orig_modules.items():
|
for orig_name, module in orig_modules.items():
|
||||||
sys.modules[blocked] = module
|
sys.modules[orig_name] = module
|
||||||
return py_module
|
for name_to_remove in names_to_remove:
|
||||||
|
del sys.modules[name_to_remove]
|
||||||
|
return fresh_module
|
||||||
|
|
||||||
|
|
||||||
def get_attribute(obj, name):
|
def get_attribute(obj, name):
|
||||||
|
|
|
@ -10,8 +10,8 @@ import warning_tests
|
||||||
|
|
||||||
import warnings as original_warnings
|
import warnings as original_warnings
|
||||||
|
|
||||||
py_warnings = test_support.import_fresh_module('warnings', ['_warnings'])
|
py_warnings = test_support.import_fresh_module('warnings', blocked=['_warnings'])
|
||||||
c_warnings = test_support.import_fresh_module('warnings')
|
c_warnings = test_support.import_fresh_module('warnings', fresh=['_warnings'])
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def warnings_state(module):
|
def warnings_state(module):
|
||||||
|
|
|
@ -839,6 +839,12 @@ Extension Modules
|
||||||
Tests
|
Tests
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
- Issue #5354: New test support function import_fresh_module() makes
|
||||||
|
it easy to import both normal and optimised versions of modules.
|
||||||
|
test_heapq and test_warnings have been adjusted to use it, tests for
|
||||||
|
other modules with both C and Python implementations in the stdlib
|
||||||
|
can be adjusted to use it over time.
|
||||||
|
|
||||||
- Fix test_warnings to no longer reset the warnings filter.
|
- Fix test_warnings to no longer reset the warnings filter.
|
||||||
|
|
||||||
- Fix test_logging to no longer reset the warnings filter.
|
- Fix test_logging to no longer reset the warnings filter.
|
||||||
|
|
Loading…
Reference in New Issue