Tor Norbye | 3a2425a5 | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 1 | import sys |
| 2 | from types import ModuleType |
| 3 | import os, imp |
| 4 | class ImpLoader: |
| 5 | code = source = None |
| 6 | |
| 7 | def __init__(self, fullname, file, filename, etc): |
| 8 | self.file = file |
| 9 | self.filename = filename |
| 10 | self.fullname = fullname |
| 11 | self.etc = etc |
| 12 | |
| 13 | def load_module(self, fullname): |
| 14 | self._reopen() |
| 15 | try: |
| 16 | mod = imp.load_module(fullname, self.file, self.filename, self.etc) |
| 17 | finally: |
| 18 | if self.file: |
| 19 | self.file.close() |
| 20 | return mod |
| 21 | |
| 22 | def get_data(self, pathname): |
| 23 | return open(pathname, "rb").read() |
| 24 | |
| 25 | def _reopen(self): |
| 26 | if self.file and self.file.closed: |
| 27 | mod_type = self.etc[2] |
| 28 | if mod_type==imp.PY_SOURCE: |
| 29 | self.file = open(self.filename, 'rU') |
| 30 | elif mod_type in (imp.PY_COMPILED, imp.C_EXTENSION): |
| 31 | self.file = open(self.filename, 'rb') |
| 32 | |
| 33 | def _fix_name(self, fullname): |
| 34 | if fullname is None: |
| 35 | fullname = self.fullname |
| 36 | elif fullname != self.fullname: |
| 37 | raise ImportError("Loader for module %s cannot handle " |
| 38 | "module %s" % (self.fullname, fullname)) |
| 39 | return fullname |
| 40 | |
| 41 | def is_package(self, fullname): |
| 42 | fullname = self._fix_name(fullname) |
| 43 | return self.etc[2]==imp.PKG_DIRECTORY |
| 44 | |
| 45 | def get_code(self, fullname=None): |
| 46 | fullname = self._fix_name(fullname) |
| 47 | if self.code is None: |
| 48 | mod_type = self.etc[2] |
| 49 | if mod_type==imp.PY_SOURCE: |
| 50 | source = self.get_source(fullname) |
| 51 | self.code = compile(source, self.filename, 'exec') |
| 52 | elif mod_type==imp.PY_COMPILED: |
| 53 | self._reopen() |
| 54 | try: |
| 55 | self.code = read_code(self.file) |
| 56 | finally: |
| 57 | self.file.close() |
| 58 | elif mod_type==imp.PKG_DIRECTORY: |
| 59 | self.code = self._get_delegate().get_code() |
| 60 | return self.code |
| 61 | |
| 62 | def get_source(self, fullname=None): |
| 63 | fullname = self._fix_name(fullname) |
| 64 | if self.source is None: |
| 65 | mod_type = self.etc[2] |
| 66 | if mod_type==imp.PY_SOURCE: |
| 67 | self._reopen() |
| 68 | try: |
| 69 | self.source = self.file.read() |
| 70 | finally: |
| 71 | self.file.close() |
| 72 | elif mod_type==imp.PY_COMPILED: |
| 73 | if os.path.exists(self.filename[:-1]): |
| 74 | f = open(self.filename[:-1], 'rU') |
| 75 | self.source = f.read() |
| 76 | f.close() |
| 77 | elif mod_type==imp.PKG_DIRECTORY: |
| 78 | self.source = self._get_delegate().get_source() |
| 79 | return self.source |
| 80 | |
| 81 | |
| 82 | def _get_delegate(self): |
| 83 | return ImpImporter(self.filename).find_module('__init__') |
| 84 | |
| 85 | def get_filename(self, fullname=None): |
| 86 | fullname = self._fix_name(fullname) |
| 87 | mod_type = self.etc[2] |
| 88 | if self.etc[2]==imp.PKG_DIRECTORY: |
| 89 | return self._get_delegate().get_filename() |
| 90 | elif self.etc[2] in (imp.PY_SOURCE, imp.PY_COMPILED, imp.C_EXTENSION): |
| 91 | return self.filename |
| 92 | return None |
| 93 | |
| 94 | |
| 95 | class ImpImporter: |
| 96 | def __init__(self, path=None): |
| 97 | self.path = path |
| 98 | |
| 99 | def find_module(self, fullname, path=None): |
| 100 | # Note: we ignore 'path' argument since it is only used via meta_path |
| 101 | subname = fullname.split(".")[-1] |
| 102 | if subname != fullname and self.path is None: |
| 103 | return None |
| 104 | if self.path is None: |
| 105 | path = None |
| 106 | else: |
| 107 | path = [os.path.realpath(self.path)] |
| 108 | try: |
| 109 | file, filename, etc = imp.find_module(subname, path) |
| 110 | except ImportError: |
| 111 | return None |
| 112 | return ImpLoader(fullname, file, filename, etc) |
| 113 | |
| 114 | def iter_modules(self, prefix=''): |
| 115 | if self.path is None or not os.path.isdir(self.path): |
| 116 | return |
| 117 | |
| 118 | yielded = {} |
| 119 | import inspect |
| 120 | |
| 121 | filenames = os.listdir(self.path) |
| 122 | filenames.sort() # handle packages before same-named modules |
| 123 | |
| 124 | for fn in filenames: |
| 125 | modname = inspect.getmodulename(fn) |
| 126 | if modname=='__init__' or modname in yielded: |
| 127 | continue |
| 128 | |
| 129 | path = os.path.join(self.path, fn) |
| 130 | ispkg = False |
| 131 | |
| 132 | if not modname and os.path.isdir(path) and '.' not in fn: |
| 133 | modname = fn |
| 134 | for fn in os.listdir(path): |
| 135 | subname = inspect.getmodulename(fn) |
| 136 | if subname=='__init__': |
| 137 | ispkg = True |
| 138 | break |
| 139 | else: |
| 140 | continue # not a package |
| 141 | |
| 142 | if modname and '.' not in modname: |
| 143 | yielded[modname] = 1 |
| 144 | yield prefix + modname, ispkg |
| 145 | |
| 146 | def get_importer(path_item): |
| 147 | try: |
| 148 | importer = sys.path_importer_cache[path_item] |
| 149 | except KeyError: |
| 150 | for path_hook in sys.path_hooks: |
| 151 | try: |
| 152 | importer = path_hook(path_item) |
| 153 | break |
| 154 | except ImportError: |
| 155 | pass |
| 156 | else: |
| 157 | importer = None |
| 158 | sys.path_importer_cache.setdefault(path_item, importer) |
| 159 | |
| 160 | if importer is None: |
| 161 | try: |
| 162 | importer = ImpImporter(path_item) |
| 163 | except ImportError: |
| 164 | importer = None |
| 165 | return importer |
| 166 | |
| 167 | def iter_importers(fullname=""): |
| 168 | if fullname.startswith('.'): |
| 169 | raise ImportError("Relative module names not supported") |
| 170 | if '.' in fullname: |
| 171 | # Get the containing package's __path__ |
| 172 | pkg = '.'.join(fullname.split('.')[:-1]) |
| 173 | if pkg not in sys.modules: |
| 174 | __import__(pkg) |
| 175 | path = getattr(sys.modules[pkg], '__path__', None) or [] |
| 176 | else: |
| 177 | for importer in sys.meta_path: |
| 178 | yield importer |
| 179 | path = sys.path |
| 180 | for item in path: |
| 181 | yield get_importer(item) |
| 182 | if '.' not in fullname: |
| 183 | yield ImpImporter() |
| 184 | |
| 185 | def find_loader(fullname): |
| 186 | for importer in iter_importers(fullname): |
| 187 | loader = importer.find_module(fullname) |
| 188 | if loader is not None: |
| 189 | return loader |
| 190 | |
| 191 | return None |
| 192 | |
| 193 | def get_loader(module_or_name): |
| 194 | if module_or_name in sys.modules: |
| 195 | module_or_name = sys.modules[module_or_name] |
| 196 | if isinstance(module_or_name, ModuleType): |
| 197 | module = module_or_name |
| 198 | loader = getattr(module, '__loader__', None) |
| 199 | if loader is not None: |
| 200 | return loader |
| 201 | fullname = module.__name__ |
| 202 | else: |
| 203 | fullname = module_or_name |
| 204 | return find_loader(fullname) |
| 205 | |
| 206 | |
| 207 | def _get_filename(loader, mod_name): |
| 208 | for attr in ("get_filename", "_get_filename"): |
| 209 | meth = getattr(loader, attr, None) |
| 210 | if meth is not None: |
| 211 | return meth(mod_name) |
| 212 | return None |
| 213 | |
| 214 | def _get_module_details(mod_name): |
| 215 | loader = get_loader(mod_name) |
| 216 | if loader is None: |
| 217 | raise ImportError("No module named %s" % mod_name) |
| 218 | if loader.is_package(mod_name): |
| 219 | if mod_name == "__main__" or mod_name.endswith(".__main__"): |
| 220 | raise ImportError("Cannot use package as __main__ module") |
| 221 | try: |
| 222 | pkg_main_name = mod_name + ".__main__" |
| 223 | return _get_module_details(pkg_main_name) |
| 224 | except ImportError, e: |
| 225 | raise ImportError(("%s; %r is a package and cannot " + |
| 226 | "be directly executed") %(e, mod_name)) |
| 227 | code = loader.get_code(mod_name) |
| 228 | if code is None: |
| 229 | raise ImportError("No code object available for %s" % mod_name) |
| 230 | filename = _get_filename(loader, mod_name) |
| 231 | return mod_name, loader, code, filename |
| 232 | |
| 233 | def _run_code(code, run_globals, init_globals=None, |
| 234 | mod_name=None, mod_fname=None, |
| 235 | mod_loader=None, pkg_name=None): |
| 236 | if init_globals is not None: |
| 237 | run_globals.update(init_globals) |
| 238 | run_globals.update(__name__ = mod_name, |
| 239 | __file__ = mod_fname, |
| 240 | __loader__ = mod_loader, |
| 241 | __package__ = pkg_name) |
| 242 | exec code in run_globals |
| 243 | return run_globals |
| 244 | |
| 245 | def run_module(mod_name, init_globals=None, |
| 246 | run_name=None): |
| 247 | mod_name, loader, code, fname = _get_module_details(mod_name) |
| 248 | if run_name is None: |
| 249 | run_name = mod_name |
| 250 | |
| 251 | ind = mod_name.rfind(".") |
| 252 | if ind != -1: |
| 253 | pkg_name = mod_name[:ind] |
| 254 | else: |
| 255 | pkg_name = mod_name |
| 256 | return _run_code(code, {}, init_globals, run_name, |
| 257 | fname, loader, pkg_name) |