Welcome, guest | Sign In | My Account | Store | Cart

A rough implementation of PEP 405.  This module centers on manipulating
the normal Python import machinery through its defined state.  Any other
approach, such as replacing builtins.__import__ is certainly legal, but
not supported here.


__all__ = ['ImportState', 'default_import_state', 'globalstate']

import sys
import builtins
import site
import importlib
import _imp
from collections import namedtuple

class GlobalImportLock:
    # no need for a generic ImportLock type, since all import states
    # use the same lock
    def acquire(self):
    def release(self):
    def lock_held(self):

_ImportState = namedtuple('_ImportState', (

class ImportState(_ImportState):
    """A container for the import state (a la PEP 406).

    The dictionary in sys.modules is a special case, since it is part
    of the CPython interpreter state.  Binding a different dict there
    is problematic, since the import machinery may use the internal
    reference to the original dict, rather than looking up sys.modules.
    The consequence is that the _contents_ of sys.modules must be
    swapped in and out, rather than simply binding something else there.

    ImportState objects may be used as context managers, to activate the
    state temporarily.  During a with statement the dict in self.modules
    may not reflect the actual state.  However, it _will_ be correct
    before and after the with statement.

    # all import states use the same lock
    lock = GlobalImportLock()

    def __init__(self, *args, **kwargs):
        self._saved = None

    def __enter__(self):

    def __exit__(self, *args, **kwargs):

    def copy(self):
        """Return a shallow copy of the import state."""
        return type(self)(self.modules.copy(), self.meta_path[:],
                          self.path[:], self.path_hooks[:],

    def activate(self, force=False):
        """Have the interpreter use this import state, saving the old."""
        if self._saved is not None and not force:
            raise TypeError("Already activated; try using a copy")

        self._saved = _ImportState(
                sys.modules.copy(),  # saving away the contents

        #sys.modules = self.modules
        sys.meta_path = self.meta_path
        sys.path = self.path
        sys.path_hooks = self.meta_path
        sys.path_importer_cache = self.path_importer_cache

        # accommodate sys.module's quirkiness

    def deactivate(self):
        """Restore the import state saved when this one activated."""
        if not self._saved:
            raise TypeError("Not activated yet")

        # sys.modules = self.modules
        sys.meta_path = self._saved.meta_path
        sys.path = self._saved.path
        sys.path_hooks = self._saved.path_hooks
        sys.path_importer_cache = self._saved.path_importer_cache

        # accommodate sys.module's quirkiness

        self._saved = None

def default_import_state(**overrides):
    """Return an ImportState with defaults to the initial import state."""
    state = {
            'modules': {},
            'meta_path': [],
            'path': site.getsitepackages(),
            'path_hooks': [],
            'path_importer_cache': {},
    return ImportState(**state)

class GlobalImportState(ImportState):
    """An ImportState that wraps the current state"""
    # The underlying ImportState values will be ignored.
    def __new__(cls):
        return super(GlobalImportState, cls).__new__(cls, *([None]*5))
    def modules(self):
        """The cache of modules that have already been imported."""
        return sys.modules
    def meta_path(self):
        """The PEP 302 finders queried before 'path' is traversed."""
        return sys.meta_path
    def path(self):
        """The directories in which top-level packages are located."""
        return sys.path
    def path_hooks(self):
        """The PEP 302 path importers that are queried for a path."""
        return sys.path_hooks
    def path_importer_cache(self):
        """The cache of finders previously found through path_hooks."""
        return sys.path_importer_cache

globalstate = GlobalImportState()