Issue #19721: Consolidate test_importlib utility code into a single

module.
This commit is contained in:
Brett Cannon 2014-05-09 14:32:57 -04:00
parent 91795c8e34
commit 732ac654c8
25 changed files with 249 additions and 270 deletions

View file

@ -1,6 +1,5 @@
"""Test case-sensitivity (PEP 235)."""
from .. import util
from . import util as source_util
importlib = util.import_importlib('importlib')
machinery = util.import_importlib('importlib.machinery')
@ -32,7 +31,7 @@ class CaseSensitivityTest:
"""Look for a module with matching and non-matching sensitivity."""
sensitive_pkg = 'sensitive.{0}'.format(self.name)
insensitive_pkg = 'insensitive.{0}'.format(self.name.lower())
context = source_util.create_modules(insensitive_pkg, sensitive_pkg)
context = util.create_modules(insensitive_pkg, sensitive_pkg)
with context as mapping:
sensitive_path = os.path.join(mapping['.root'], 'sensitive')
insensitive_path = os.path.join(mapping['.root'], 'insensitive')

View file

@ -1,6 +1,5 @@
from .. import abc
from .. import util
from . import util as source_util
importlib = util.import_importlib('importlib')
importlib_abc = util.import_importlib('importlib.abc')
@ -71,7 +70,7 @@ class SimpleTest(abc.LoaderTests):
# [basic]
def test_module(self):
with source_util.create_modules('_temp') as mapping:
with util.create_modules('_temp') as mapping:
loader = self.machinery.SourceFileLoader('_temp', mapping['_temp'])
with warnings.catch_warnings():
warnings.simplefilter('ignore', DeprecationWarning)
@ -83,7 +82,7 @@ class SimpleTest(abc.LoaderTests):
self.assertEqual(getattr(module, attr), value)
def test_package(self):
with source_util.create_modules('_pkg.__init__') as mapping:
with util.create_modules('_pkg.__init__') as mapping:
loader = self.machinery.SourceFileLoader('_pkg',
mapping['_pkg.__init__'])
with warnings.catch_warnings():
@ -98,7 +97,7 @@ class SimpleTest(abc.LoaderTests):
def test_lacking_parent(self):
with source_util.create_modules('_pkg.__init__', '_pkg.mod')as mapping:
with util.create_modules('_pkg.__init__', '_pkg.mod')as mapping:
loader = self.machinery.SourceFileLoader('_pkg.mod',
mapping['_pkg.mod'])
with warnings.catch_warnings():
@ -115,7 +114,7 @@ class SimpleTest(abc.LoaderTests):
return lambda name: fxn(name) + 1
def test_module_reuse(self):
with source_util.create_modules('_temp') as mapping:
with util.create_modules('_temp') as mapping:
loader = self.machinery.SourceFileLoader('_temp', mapping['_temp'])
with warnings.catch_warnings():
warnings.simplefilter('ignore', DeprecationWarning)
@ -139,7 +138,7 @@ class SimpleTest(abc.LoaderTests):
attributes = ('__file__', '__path__', '__package__')
value = '<test>'
name = '_temp'
with source_util.create_modules(name) as mapping:
with util.create_modules(name) as mapping:
orig_module = types.ModuleType(name)
for attr in attributes:
setattr(orig_module, attr, value)
@ -159,7 +158,7 @@ class SimpleTest(abc.LoaderTests):
# [syntax error]
def test_bad_syntax(self):
with source_util.create_modules('_temp') as mapping:
with util.create_modules('_temp') as mapping:
with open(mapping['_temp'], 'w') as file:
file.write('=')
loader = self.machinery.SourceFileLoader('_temp', mapping['_temp'])
@ -190,11 +189,11 @@ class SimpleTest(abc.LoaderTests):
if os.path.exists(pycache):
shutil.rmtree(pycache)
@source_util.writes_bytecode_files
@util.writes_bytecode_files
def test_timestamp_overflow(self):
# When a modification timestamp is larger than 2**32, it should be
# truncated rather than raise an OverflowError.
with source_util.create_modules('_temp') as mapping:
with util.create_modules('_temp') as mapping:
source = mapping['_temp']
compiled = self.util.cache_from_source(source)
with open(source, 'w') as f:
@ -275,45 +274,45 @@ class BadBytecodeTest:
return bytecode_path
def _test_empty_file(self, test, *, del_source=False):
with source_util.create_modules('_temp') as mapping:
with util.create_modules('_temp') as mapping:
bc_path = self.manipulate_bytecode('_temp', mapping,
lambda bc: b'',
del_source=del_source)
test('_temp', mapping, bc_path)
@source_util.writes_bytecode_files
@util.writes_bytecode_files
def _test_partial_magic(self, test, *, del_source=False):
# When their are less than 4 bytes to a .pyc, regenerate it if
# possible, else raise ImportError.
with source_util.create_modules('_temp') as mapping:
with util.create_modules('_temp') as mapping:
bc_path = self.manipulate_bytecode('_temp', mapping,
lambda bc: bc[:3],
del_source=del_source)
test('_temp', mapping, bc_path)
def _test_magic_only(self, test, *, del_source=False):
with source_util.create_modules('_temp') as mapping:
with util.create_modules('_temp') as mapping:
bc_path = self.manipulate_bytecode('_temp', mapping,
lambda bc: bc[:4],
del_source=del_source)
test('_temp', mapping, bc_path)
def _test_partial_timestamp(self, test, *, del_source=False):
with source_util.create_modules('_temp') as mapping:
with util.create_modules('_temp') as mapping:
bc_path = self.manipulate_bytecode('_temp', mapping,
lambda bc: bc[:7],
del_source=del_source)
test('_temp', mapping, bc_path)
def _test_partial_size(self, test, *, del_source=False):
with source_util.create_modules('_temp') as mapping:
with util.create_modules('_temp') as mapping:
bc_path = self.manipulate_bytecode('_temp', mapping,
lambda bc: bc[:11],
del_source=del_source)
test('_temp', mapping, bc_path)
def _test_no_marshal(self, *, del_source=False):
with source_util.create_modules('_temp') as mapping:
with util.create_modules('_temp') as mapping:
bc_path = self.manipulate_bytecode('_temp', mapping,
lambda bc: bc[:12],
del_source=del_source)
@ -322,7 +321,7 @@ class BadBytecodeTest:
self.import_(file_path, '_temp')
def _test_non_code_marshal(self, *, del_source=False):
with source_util.create_modules('_temp') as mapping:
with util.create_modules('_temp') as mapping:
bytecode_path = self.manipulate_bytecode('_temp', mapping,
lambda bc: bc[:12] + marshal.dumps(b'abcd'),
del_source=del_source)
@ -333,7 +332,7 @@ class BadBytecodeTest:
self.assertEqual(cm.exception.path, bytecode_path)
def _test_bad_marshal(self, *, del_source=False):
with source_util.create_modules('_temp') as mapping:
with util.create_modules('_temp') as mapping:
bytecode_path = self.manipulate_bytecode('_temp', mapping,
lambda bc: bc[:12] + b'<test>',
del_source=del_source)
@ -342,7 +341,7 @@ class BadBytecodeTest:
self.import_(file_path, '_temp')
def _test_bad_magic(self, test, *, del_source=False):
with source_util.create_modules('_temp') as mapping:
with util.create_modules('_temp') as mapping:
bc_path = self.manipulate_bytecode('_temp', mapping,
lambda bc: b'\x00\x00\x00\x00' + bc[4:])
test('_temp', mapping, bc_path)
@ -371,7 +370,7 @@ class SourceLoaderBadBytecodeTest:
def setUpClass(cls):
cls.loader = cls.machinery.SourceFileLoader
@source_util.writes_bytecode_files
@util.writes_bytecode_files
def test_empty_file(self):
# When a .pyc is empty, regenerate it if possible, else raise
# ImportError.
@ -390,7 +389,7 @@ class SourceLoaderBadBytecodeTest:
self._test_partial_magic(test)
@source_util.writes_bytecode_files
@util.writes_bytecode_files
def test_magic_only(self):
# When there is only the magic number, regenerate the .pyc if possible,
# else raise EOFError.
@ -401,7 +400,7 @@ class SourceLoaderBadBytecodeTest:
self._test_magic_only(test)
@source_util.writes_bytecode_files
@util.writes_bytecode_files
def test_bad_magic(self):
# When the magic number is different, the bytecode should be
# regenerated.
@ -413,7 +412,7 @@ class SourceLoaderBadBytecodeTest:
self._test_bad_magic(test)
@source_util.writes_bytecode_files
@util.writes_bytecode_files
def test_partial_timestamp(self):
# When the timestamp is partial, regenerate the .pyc, else
# raise EOFError.
@ -424,7 +423,7 @@ class SourceLoaderBadBytecodeTest:
self._test_partial_timestamp(test)
@source_util.writes_bytecode_files
@util.writes_bytecode_files
def test_partial_size(self):
# When the size is partial, regenerate the .pyc, else
# raise EOFError.
@ -435,29 +434,29 @@ class SourceLoaderBadBytecodeTest:
self._test_partial_size(test)
@source_util.writes_bytecode_files
@util.writes_bytecode_files
def test_no_marshal(self):
# When there is only the magic number and timestamp, raise EOFError.
self._test_no_marshal()
@source_util.writes_bytecode_files
@util.writes_bytecode_files
def test_non_code_marshal(self):
self._test_non_code_marshal()
# XXX ImportError when sourceless
# [bad marshal]
@source_util.writes_bytecode_files
@util.writes_bytecode_files
def test_bad_marshal(self):
# Bad marshal data should raise a ValueError.
self._test_bad_marshal()
# [bad timestamp]
@source_util.writes_bytecode_files
@util.writes_bytecode_files
def test_old_timestamp(self):
# When the timestamp is older than the source, bytecode should be
# regenerated.
zeros = b'\x00\x00\x00\x00'
with source_util.create_modules('_temp') as mapping:
with util.create_modules('_temp') as mapping:
py_compile.compile(mapping['_temp'])
bytecode_path = self.util.cache_from_source(mapping['_temp'])
with open(bytecode_path, 'r+b') as bytecode_file:
@ -471,10 +470,10 @@ class SourceLoaderBadBytecodeTest:
self.assertEqual(bytecode_file.read(4), source_timestamp)
# [bytecode read-only]
@source_util.writes_bytecode_files
@util.writes_bytecode_files
def test_read_only_bytecode(self):
# When bytecode is read-only but should be rewritten, fail silently.
with source_util.create_modules('_temp') as mapping:
with util.create_modules('_temp') as mapping:
# Create bytecode that will need to be re-created.
py_compile.compile(mapping['_temp'])
bytecode_path = self.util.cache_from_source(mapping['_temp'])

View file

@ -1,6 +1,5 @@
from .. import abc
from .. import util
from . import util as source_util
machinery = util.import_importlib('importlib.machinery')
@ -60,7 +59,7 @@ class FinderTests(abc.FinderTests):
"""
if create is None:
create = {test}
with source_util.create_modules(*create) as mapping:
with util.create_modules(*create) as mapping:
if compile_:
for name in compile_:
py_compile.compile(mapping[name])
@ -100,14 +99,14 @@ class FinderTests(abc.FinderTests):
# [sub module]
def test_module_in_package(self):
with source_util.create_modules('pkg.__init__', 'pkg.sub') as mapping:
with util.create_modules('pkg.__init__', 'pkg.sub') as mapping:
pkg_dir = os.path.dirname(mapping['pkg.__init__'])
loader = self.import_(pkg_dir, 'pkg.sub')
self.assertTrue(hasattr(loader, 'load_module'))
# [sub package]
def test_package_in_package(self):
context = source_util.create_modules('pkg.__init__', 'pkg.sub.__init__')
context = util.create_modules('pkg.__init__', 'pkg.sub.__init__')
with context as mapping:
pkg_dir = os.path.dirname(mapping['pkg.__init__'])
loader = self.import_(pkg_dir, 'pkg.sub')
@ -120,7 +119,7 @@ class FinderTests(abc.FinderTests):
self.assertIn('__init__', loader.get_filename(name))
def test_failure(self):
with source_util.create_modules('blah') as mapping:
with util.create_modules('blah') as mapping:
nothing = self.import_(mapping['.root'], 'sdfsadsadf')
self.assertIsNone(nothing)
@ -147,7 +146,7 @@ class FinderTests(abc.FinderTests):
# Regression test for http://bugs.python.org/issue14846
def test_dir_removal_handling(self):
mod = 'mod'
with source_util.create_modules(mod) as mapping:
with util.create_modules(mod) as mapping:
finder = self.get_finder(mapping['.root'])
found = self._find(finder, 'mod', loader_only=True)
self.assertIsNotNone(found)

View file

@ -1,5 +1,4 @@
from .. import util
from . import util as source_util
machinery = util.import_importlib('importlib.machinery')
@ -15,7 +14,7 @@ class PathHookTest:
self.machinery.SOURCE_SUFFIXES))
def test_success(self):
with source_util.create_modules('dummy') as mapping:
with util.create_modules('dummy') as mapping:
self.assertTrue(hasattr(self.path_hook()(mapping['.root']),
'find_module'))

View file

@ -1,5 +1,4 @@
from .. import util
from . import util as source_util
machinery = util.import_importlib('importlib.machinery')
@ -37,7 +36,7 @@ class EncodingTest:
module_name = '_temp'
def run_test(self, source):
with source_util.create_modules(self.module_name) as mapping:
with util.create_modules(self.module_name) as mapping:
with open(mapping[self.module_name], 'wb') as file:
file.write(source)
loader = self.machinery.SourceFileLoader(self.module_name,
@ -120,7 +119,7 @@ class LineEndingTest:
module_name = '_temp'
source_lines = [b"a = 42", b"b = -13", b'']
source = line_ending.join(source_lines)
with source_util.create_modules(module_name) as mapping:
with util.create_modules(module_name) as mapping:
with open(mapping[module_name], 'wb') as file:
file.write(source)
loader = self.machinery.SourceFileLoader(module_name,

View file

@ -1,96 +0,0 @@
from .. import util
import contextlib
import errno
import functools
import os
import os.path
import sys
import tempfile
from test import support
def writes_bytecode_files(fxn):
"""Decorator to protect sys.dont_write_bytecode from mutation and to skip
tests that require it to be set to False."""
if sys.dont_write_bytecode:
return lambda *args, **kwargs: None
@functools.wraps(fxn)
def wrapper(*args, **kwargs):
original = sys.dont_write_bytecode
sys.dont_write_bytecode = False
try:
to_return = fxn(*args, **kwargs)
finally:
sys.dont_write_bytecode = original
return to_return
return wrapper
def ensure_bytecode_path(bytecode_path):
"""Ensure that the __pycache__ directory for PEP 3147 pyc file exists.
:param bytecode_path: File system path to PEP 3147 pyc file.
"""
try:
os.mkdir(os.path.dirname(bytecode_path))
except OSError as error:
if error.errno != errno.EEXIST:
raise
@contextlib.contextmanager
def create_modules(*names):
"""Temporarily create each named module with an attribute (named 'attr')
that contains the name passed into the context manager that caused the
creation of the module.
All files are created in a temporary directory returned by
tempfile.mkdtemp(). This directory is inserted at the beginning of
sys.path. When the context manager exits all created files (source and
bytecode) are explicitly deleted.
No magic is performed when creating packages! This means that if you create
a module within a package you must also create the package's __init__ as
well.
"""
source = 'attr = {0!r}'
created_paths = []
mapping = {}
state_manager = None
uncache_manager = None
try:
temp_dir = tempfile.mkdtemp()
mapping['.root'] = temp_dir
import_names = set()
for name in names:
if not name.endswith('__init__'):
import_name = name
else:
import_name = name[:-len('.__init__')]
import_names.add(import_name)
if import_name in sys.modules:
del sys.modules[import_name]
name_parts = name.split('.')
file_path = temp_dir
for directory in name_parts[:-1]:
file_path = os.path.join(file_path, directory)
if not os.path.exists(file_path):
os.mkdir(file_path)
created_paths.append(file_path)
file_path = os.path.join(file_path, name_parts[-1] + '.py')
with open(file_path, 'w') as file:
file.write(source.format(name))
created_paths.append(file_path)
mapping[name] = file_path
uncache_manager = util.uncache(*import_names)
uncache_manager.__enter__()
state_manager = util.import_state(path=[temp_dir])
state_manager.__enter__()
yield mapping
finally:
if state_manager is not None:
state_manager.__exit__(None, None, None)
if uncache_manager is not None:
uncache_manager.__exit__(None, None, None)
support.rmtree(temp_dir)