diff options
-rw-r--r-- | Doc/library/test.rst | 42 | ||||
-rw-r--r-- | Lib/test/support.py | 62 | ||||
-rw-r--r-- | Lib/test/test_heapq.py | 2 | ||||
-rw-r--r-- | Lib/test/test_warnings.py | 10 | ||||
-rw-r--r-- | Misc/NEWS | 9 |
5 files changed, 93 insertions, 32 deletions
diff --git a/Doc/library/test.rst b/Doc/library/test.rst index 6add77d..d196678 100644 --- a/Doc/library/test.rst +++ b/Doc/library/test.rst @@ -8,8 +8,8 @@ The :mod:`test` package contains all regression tests for Python as well as the -modules :mod:`test.test_support` and :mod:`test.regrtest`. -:mod:`test.test_support` is used to enhance your tests while +modules :mod:`test.support` and :mod:`test.regrtest`. +:mod:`test.support` is used to enhance your tests while :mod:`test.regrtest` drives the testing suite. Each module in the :mod:`test` package whose name starts with ``test_`` is a @@ -47,7 +47,7 @@ stated. A basic boilerplate is often used:: import unittest - from test import test_support + from test import support class MyTestCase1(unittest.TestCase): @@ -75,7 +75,7 @@ A basic boilerplate is often used:: ... more test classes ... def test_main(): - test_support.run_unittest(MyTestCase1, + support.run_unittest(MyTestCase1, MyTestCase2, ... list other tests ... ) @@ -273,7 +273,7 @@ The :mod:`test.support` module defines the following functions: following :func:`test_main` function:: def test_main(): - test_support.run_unittest(__name__) + support.run_unittest(__name__) This will run all tests defined in the named module. @@ -334,15 +334,39 @@ The :mod:`test.support` module defines the following functions: .. versionadded:: 3.1 -.. 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 - ``sys.modules`` cache is bypassed temporarily, and the ability to import the - modules named in *blocked_names* is suppressed for the duration of the import. + This function imports and returns a fresh copy of the named Python module + by removing the named module from ``sys.modules`` before doing 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 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:: 3.1 diff --git a/Lib/test/support.py b/Lib/test/support.py index 28823ae..ebb3495 100644 --- a/Lib/test/support.py +++ b/Lib/test/support.py @@ -69,12 +69,43 @@ def import_module(name, deprecated=False): 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 and importing a fresh copy of the module. Once the import is complete, 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. If deprecated is True, any module or package deprecation messages @@ -82,21 +113,24 @@ def import_fresh_module(name, blocked_names=None, deprecated=False): # NOTE: test_heapq and test_warnings include extra sanity checks to make # sure that this utility function is working as expected with _ignore_deprecated_imports(deprecated): - if blocked_names is None: - blocked_names = () + # Keep track of modules saved for later restoration as well + # as those which just need a blocking entry removed orig_modules = {} - if name in sys.modules: - orig_modules[name] = sys.modules[name] - del sys.modules[name] + names_to_remove = [] + _save_and_remove_module(name, orig_modules) try: - for blocked in blocked_names: - orig_modules[blocked] = sys.modules[blocked] - sys.modules[blocked] = 0 - py_module = importlib.import_module(name) + for fresh_name in fresh: + _save_and_remove_module(fresh_name, orig_modules) + for blocked_name in blocked: + if not _save_and_block_module(blocked_name, orig_modules): + names_to_remove.append(blocked_name) + fresh_module = importlib.import_module(name) finally: - for blocked, module in orig_modules.items(): - sys.modules[blocked] = module - return py_module + for orig_name, module in orig_modules.items(): + sys.modules[orig_name] = module + for name_to_remove in names_to_remove: + del sys.modules[name_to_remove] + return fresh_module def get_attribute(obj, name): diff --git a/Lib/test/test_heapq.py b/Lib/test/test_heapq.py index bbb22bd..94c3992 100644 --- a/Lib/test/test_heapq.py +++ b/Lib/test/test_heapq.py @@ -8,7 +8,7 @@ import sys # We do a bit of trickery here to be able to test both the C implementation # and the Python implementation of the module. import heapq as c_heapq -py_heapq = support.import_fresh_module('heapq', ['_heapq']) +py_heapq = support.import_fresh_module('heapq', blocked=['_heapq']) class TestHeap(unittest.TestCase): module = None diff --git a/Lib/test/test_warnings.py b/Lib/test/test_warnings.py index 1f377ad..4bcc210 100644 --- a/Lib/test/test_warnings.py +++ b/Lib/test/test_warnings.py @@ -10,14 +10,8 @@ from test import warning_tests import warnings as original_warnings -py_warnings = support.import_fresh_module('warnings', ['_warnings']) -# XXX (ncoghlan 20090412): -# Something in Py3k doesn't like sharing the same instance of -# _warnings between original_warnings and c_warnings -# Will leave issue 5354 open until I understand why 3.x breaks -# without the next line, while 2.x doesn't care -del sys.modules['_warnings'] -c_warnings = support.import_fresh_module('warnings') +py_warnings = support.import_fresh_module('warnings', blocked=['_warnings']) +c_warnings = support.import_fresh_module('warnings', fresh=['_warnings']) @contextmanager def warnings_state(module): @@ -98,6 +98,15 @@ Extension Modules - Issue #5359: Readd the Berkley-DB detection code to allow _dbm be built using Berkley-DB. +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. + What's New in Python 3.1 alpha 2? ================================= |