Advertisement
Guest User

pep382.py module sketch 1

a guest
Jun 24th, 2011
107
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.16 KB | None | 0 0
  1. # Performance note: this slows import of all builtin/frozen modules, and adds
  2. # an isdir() check for each directory on sys.path that precedes the target.  If
  3. # you have a lot of (directory) path entries and import a lot of stuff from
  4. # far down the path, this may be very slow if (e.g.) those paths are over a
  5. # network.  So, we operate in an "explicit only" mode by default, where only
  6. # registered namespace packages get the extra processing this way.
  7.  
  8. import sys, new, zipimport, os
  9. try:
  10.     _make_set = set
  11. except NameError:
  12.     _make_set = dict.fromkeys
  13.  
  14. if not hasattr(sys, 'namespace_packages'):
  15.     sys.namespace_packages = _make_set([])
  16.  
  17. def is_namespace(pkgname):
  18.     return pkgname in sys.namespace_packages
  19.  
  20. def register_namespace(pkgname):
  21.     if not is_namespace(pkgname):
  22.         sys.namespace_packages.update(_make_set([pkgname]))
  23.     # Make sure we can process at least the registered namespace(s)
  24.     meta_importer.install(True)
  25.  
  26.  
  27. def get_importer(entry):
  28.     global ImpImporter, get_importer
  29.     try:
  30.         from pkgutil import get_importer, ImpImporter
  31.     except ImportError:
  32.         from pkg_resources import get_importer, ImpWrapper as ImpImporter
  33.     ImpImporter.namespace_subpath = _fs_namespace_subpath
  34.     return get_importer(entry)    
  35.  
  36. def namespace_subpath(finder, fullname):
  37.     if hasattr(finder, 'namespace_subpath'):
  38.         return finder.namespace_subpath(fullname)
  39.     elif isinstance(finder, zipimport.zipimporter):
  40.         return _zip_namespace_subpath(finder, fullname)
  41.        
  42. class NamespaceMetaImporter:
  43.     """Implement PEP 382 namespace protocol for Python 2.x"""
  44.  
  45.     registration_required = True
  46.  
  47.     def find_module(self, name, path):
  48.         if self.registration_required and not is_namespace(name):
  49.             # We're only running for explicitly-registered packages, fall back
  50.             # to normal import
  51.             return None
  52.         loader = None
  53.         nspth = []
  54.         parents = []
  55.         for entry in path:
  56.             finder = get_importer(entry)
  57.             subpath = namespace_subpath(finder, name)
  58.             if subpath is not None:
  59.                 parents.append(entry)
  60.                 nspth.append(subpath)
  61.             elif not nspth: # only search normal modules before first ns found!
  62.                 loader = finder.find_module(name)
  63.                 if not nspth and loader is not None:
  64.                     # Found regular module or package first, return it now
  65.                     return loader
  66.         if not nspth:
  67.             # Nothing found, fall through to normal import process
  68.             return loader
  69.  
  70.         register_namespace(name)    # make sure we're in sys.namespace_packages
  71.         nspkg = sys.modules.setdefault(name, new.module(name))
  72.         nspkg.__path__ = nspth
  73.  
  74.         for entry, subpath in zip(parents, nspth):
  75.             finder = get_importer(entry)            
  76.             loader = self._wrap_loader(finder.find_module(subname), subpath, name)
  77.             if loader is not None:
  78.                 return loader
  79.  
  80.         # Namespace package with no __init__ - return dummy loader
  81.         return self
  82.  
  83.     def load_module(self, name):
  84.         # We already initialized an empty namespace package, so just return it
  85.         return sys.modules[name]
  86.  
  87.     def wrap_loader(self, loader, subpath, name):
  88.         if hasattr(loader, 'is_package') and not loader.is_package(name):
  89.             # It's a module, not a package: ignore it
  90.             return None
  91.  
  92.         finder = get_importer(subpath)
  93.         subloader = finder.find_module(name+'.__init__')
  94.  
  95.         if subloader is None:
  96.             # Guess it must be a module after all; skip it
  97.             return None
  98.  
  99.         # Ok, now we have a parent loader (which we want to set as the
  100.         # __loader__, if we set one), and a subloader, which we'll use to fetch
  101.         # the __init__ code from, assuming it has a get_code() method:
  102.        
  103.         if isinstance(finder, ImpImporter) or not hasattr(subloader, 'get_code'):
  104.             # Use as-is.  This should work for pkgutil and pkg_resources'
  105.             # ImpLoader classes, or anything else that wraps imp in similar
  106.             # fashion, but could have weird consequences for specialized
  107.             # importers.  (OTOH, they probably don't support checking namespace
  108.             # existence, so we probably won't get here for them anyway!)
  109.             return subloader
  110.            
  111.         # This should works for zipimport and most other things with a get_code
  112.         return CodeLoader(loader, subloader, name+'.__init__')
  113.        
  114.     def install(self, require_registration=False):
  115.         """Put this meta-importer on sys.meta_path"""
  116.         if self not in sys.meta_path:
  117.             # We install at the end, so other meta-hooks take precedence
  118.             sys.meta_path.append(self)
  119.         if not require_registration:
  120.             self.require_registration = False
  121.  
  122. meta_importer = NamespaceMetaImporter()
  123.  
  124. class CodeLoader(object):
  125.     """Run __init__ code in package, without erasing __path__"""
  126.  
  127.     def __init__(self, loader, subloader, subname):
  128.         self.loader = loader
  129.         self.subloader = subloader
  130.         self.subname = subname
  131.  
  132.     def load_module(self, fullname):
  133.         pkg = sys.modules[fullname]
  134.         pkg.__loader__ = self.loader
  135.         code = self.subloader.get_code(self.subname)
  136.         fn = getattr(code, 'co_filename', None)
  137.         if fn:
  138.             pkg.__file__ = fn
  139.         exec code in pkg.__dict__
  140.         return pkg
  141.  
  142. def _zip_namespace_subpath(zipimporter, fullname):
  143.     subname = fullname.split('.')[-1]
  144.     prefix = zipimporter.prefix + subname
  145.     for filename in zipimport._zip_directory_cache[zipimporter.archive]:
  146.         if filename.endswith('.ns') and os.path.dirname(filename)==prefix:
  147.             return zipimporter.archive + os.sep + prefix
  148.  
  149. def _fs_namespace_subpath(imp_importer, fullname):
  150.     subname = fullname.split('.')[-1]
  151.     prefix = os.path.join(imp_importer.path, subname)
  152.     if os.path.isdir(prefix):
  153.         for filename in listdir(prefix):
  154.             if filename.endswith('.ns'):
  155.                 return prefix
  156.     return None # not a namespace
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement