summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/test.rst42
-rw-r--r--Lib/test/support.py62
-rw-r--r--Lib/test/test_heapq.py2
-rw-r--r--Lib/test/test_warnings.py10
-rw-r--r--Misc/NEWS9
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):
diff --git a/Misc/NEWS b/Misc/NEWS
index ca4a864..cc70d78 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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?
=================================