util.py 3.18 KB
Newer Older
1
from .. import util
2
import contextlib
Barry Warsaw's avatar
Barry Warsaw committed
3
import errno
4
import functools
5 6 7 8 9
import imp
import os
import os.path
import sys
import tempfile
10
from test import support
11 12


13 14 15 16 17
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
18 19 20 21
    @functools.wraps(fxn)
    def wrapper(*args, **kwargs):
        original = sys.dont_write_bytecode
        sys.dont_write_bytecode = False
22 23 24 25
        try:
            to_return = fxn(*args, **kwargs)
        finally:
            sys.dont_write_bytecode = original
26 27 28 29
        return to_return
    return wrapper


Barry Warsaw's avatar
Barry Warsaw committed
30 31 32 33 34 35 36 37 38 39
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
40 41 42 43 44 45 46 47


@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.

48 49
    All files are created in a temporary directory returned by
    tempfile.mkdtemp(). This directory is inserted at the beginning of
50 51 52 53 54 55 56 57 58 59 60
    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 = {}
61 62
    state_manager = None
    uncache_manager = None
63
    try:
64
        temp_dir = tempfile.mkdtemp()
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
        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:
93 94 95 96
        if state_manager is not None:
            state_manager.__exit__(None, None, None)
        if uncache_manager is not None:
            uncache_manager.__exit__(None, None, None)
97
        support.rmtree(temp_dir)