diff --git a/lib/spack/spack/repository.py b/lib/spack/spack/repository.py index df21810a12..58747ba25d 100644 --- a/lib/spack/spack/repository.py +++ b/lib/spack/spack/repository.py @@ -33,6 +33,8 @@ import re import traceback from bisect import bisect_left +from types import ModuleType + import yaml import llnl.util.tty as tty @@ -73,12 +75,21 @@ def converter(self, spec_like, *args, **kwargs): return converter -def _make_namespace_module(ns): - module = imp.new_module(ns) - module.__file__ = "(spack namespace)" - module.__path__ = [] - module.__package__ = ns - return module +class SpackNamespace(ModuleType): + """ Allow lazy loading of modules.""" + def __init__(self, namespace): + super(ModuleType, self).__init__(self, namespace) + self.__file__ = "(spack namespace)" + self.__path__ = [] + self.__name__ = namespace + self.__package__ = namespace + self.__modules = {} + + def __getattr__(self, name): + """Getattr lazily loads modules if they're not already loaded.""" + submodule = self.__package__ + '.' + name + setattr(self, name, __import__(submodule)) + return getattr(self, name) def substitute_spack_prefix(path): @@ -287,13 +298,10 @@ def load_module(self, fullname): if fullname in sys.modules: return sys.modules[fullname] - # partition fullname into prefix and module name. - namespace, dot, module_name = fullname.rpartition('.') - if not self.by_namespace.is_prefix(fullname): raise ImportError("No such Spack repo: %s" % fullname) - module = _make_namespace_module(namespace) + module = SpackNamespace(fullname) module.__loader__ = self sys.modules[fullname] = module return module @@ -464,8 +472,9 @@ def _create_namespace(self): parent = None for l in range(1, len(self._names)+1): ns = '.'.join(self._names[:l]) + if not ns in sys.modules: - module = _make_namespace_module(ns) + module = SpackNamespace(ns) module.__loader__ = self sys.modules[ns] = module @@ -476,11 +485,12 @@ def _create_namespace(self): # import spack.pkg.builtin.mpich as mpich if parent: modname = self._names[l-1] - if not hasattr(parent, modname): - setattr(parent, modname, module) + setattr(parent, modname, module) else: - # no need to set up a module, but keep track of the parent. + # no need to set up a module module = sys.modules[ns] + + # but keep track of the parent in this loop parent = module @@ -543,7 +553,7 @@ def load_module(self, fullname): namespace, dot, module_name = fullname.rpartition('.') if self.is_prefix(fullname): - module = _make_namespace_module(fullname) + module = SpackNamespace(fullname) elif namespace == self.full_namespace: real_name = self.real_name(module_name) @@ -556,6 +566,11 @@ def load_module(self, fullname): module.__loader__ = self sys.modules[fullname] = module + if namespace != fullname: + parent = sys.modules[namespace] + if not hasattr(parent, module_name): + setattr(parent, module_name, module) + return module