]>
Commit | Line | Data |
---|---|---|
3257aa99 DM |
1 | """Find modules used by a script, using introspection."""\r |
2 | # This module should be kept compatible with Python 2.2, see PEP 291.\r | |
3 | \r | |
4 | from __future__ import generators\r | |
5 | import dis\r | |
6 | import imp\r | |
7 | import marshal\r | |
8 | import os\r | |
9 | import sys\r | |
10 | import types\r | |
11 | import struct\r | |
12 | \r | |
13 | if hasattr(sys.__stdout__, "newlines"):\r | |
14 | READ_MODE = "U" # universal line endings\r | |
15 | else:\r | |
16 | # remain compatible with Python < 2.3\r | |
17 | READ_MODE = "r"\r | |
18 | \r | |
19 | LOAD_CONST = chr(dis.opname.index('LOAD_CONST'))\r | |
20 | IMPORT_NAME = chr(dis.opname.index('IMPORT_NAME'))\r | |
21 | STORE_NAME = chr(dis.opname.index('STORE_NAME'))\r | |
22 | STORE_GLOBAL = chr(dis.opname.index('STORE_GLOBAL'))\r | |
23 | STORE_OPS = [STORE_NAME, STORE_GLOBAL]\r | |
24 | HAVE_ARGUMENT = chr(dis.HAVE_ARGUMENT)\r | |
25 | \r | |
26 | # Modulefinder does a good job at simulating Python's, but it can not\r | |
27 | # handle __path__ modifications packages make at runtime. Therefore there\r | |
28 | # is a mechanism whereby you can register extra paths in this map for a\r | |
29 | # package, and it will be honored.\r | |
30 | \r | |
31 | # Note this is a mapping is lists of paths.\r | |
32 | packagePathMap = {}\r | |
33 | \r | |
34 | # A Public interface\r | |
35 | def AddPackagePath(packagename, path):\r | |
36 | paths = packagePathMap.get(packagename, [])\r | |
37 | paths.append(path)\r | |
38 | packagePathMap[packagename] = paths\r | |
39 | \r | |
40 | replacePackageMap = {}\r | |
41 | \r | |
42 | # This ReplacePackage mechanism allows modulefinder to work around the\r | |
43 | # way the _xmlplus package injects itself under the name "xml" into\r | |
44 | # sys.modules at runtime by calling ReplacePackage("_xmlplus", "xml")\r | |
45 | # before running ModuleFinder.\r | |
46 | \r | |
47 | def ReplacePackage(oldname, newname):\r | |
48 | replacePackageMap[oldname] = newname\r | |
49 | \r | |
50 | \r | |
51 | class Module:\r | |
52 | \r | |
53 | def __init__(self, name, file=None, path=None):\r | |
54 | self.__name__ = name\r | |
55 | self.__file__ = file\r | |
56 | self.__path__ = path\r | |
57 | self.__code__ = None\r | |
58 | # The set of global names that are assigned to in the module.\r | |
59 | # This includes those names imported through starimports of\r | |
60 | # Python modules.\r | |
61 | self.globalnames = {}\r | |
62 | # The set of starimports this module did that could not be\r | |
63 | # resolved, ie. a starimport from a non-Python module.\r | |
64 | self.starimports = {}\r | |
65 | \r | |
66 | def __repr__(self):\r | |
67 | s = "Module(%r" % (self.__name__,)\r | |
68 | if self.__file__ is not None:\r | |
69 | s = s + ", %r" % (self.__file__,)\r | |
70 | if self.__path__ is not None:\r | |
71 | s = s + ", %r" % (self.__path__,)\r | |
72 | s = s + ")"\r | |
73 | return s\r | |
74 | \r | |
75 | class ModuleFinder:\r | |
76 | \r | |
77 | def __init__(self, path=None, debug=0, excludes=[], replace_paths=[]):\r | |
78 | if path is None:\r | |
79 | path = sys.path\r | |
80 | self.path = path\r | |
81 | self.modules = {}\r | |
82 | self.badmodules = {}\r | |
83 | self.debug = debug\r | |
84 | self.indent = 0\r | |
85 | self.excludes = excludes\r | |
86 | self.replace_paths = replace_paths\r | |
87 | self.processed_paths = [] # Used in debugging only\r | |
88 | \r | |
89 | def msg(self, level, str, *args):\r | |
90 | if level <= self.debug:\r | |
91 | for i in range(self.indent):\r | |
92 | print " ",\r | |
93 | print str,\r | |
94 | for arg in args:\r | |
95 | print repr(arg),\r | |
96 | print\r | |
97 | \r | |
98 | def msgin(self, *args):\r | |
99 | level = args[0]\r | |
100 | if level <= self.debug:\r | |
101 | self.indent = self.indent + 1\r | |
102 | self.msg(*args)\r | |
103 | \r | |
104 | def msgout(self, *args):\r | |
105 | level = args[0]\r | |
106 | if level <= self.debug:\r | |
107 | self.indent = self.indent - 1\r | |
108 | self.msg(*args)\r | |
109 | \r | |
110 | def run_script(self, pathname):\r | |
111 | self.msg(2, "run_script", pathname)\r | |
112 | with open(pathname, READ_MODE) as fp:\r | |
113 | stuff = ("", "r", imp.PY_SOURCE)\r | |
114 | self.load_module('__main__', fp, pathname, stuff)\r | |
115 | \r | |
116 | def load_file(self, pathname):\r | |
117 | dir, name = os.path.split(pathname)\r | |
118 | name, ext = os.path.splitext(name)\r | |
119 | with open(pathname, READ_MODE) as fp:\r | |
120 | stuff = (ext, "r", imp.PY_SOURCE)\r | |
121 | self.load_module(name, fp, pathname, stuff)\r | |
122 | \r | |
123 | def import_hook(self, name, caller=None, fromlist=None, level=-1):\r | |
124 | self.msg(3, "import_hook", name, caller, fromlist, level)\r | |
125 | parent = self.determine_parent(caller, level=level)\r | |
126 | q, tail = self.find_head_package(parent, name)\r | |
127 | m = self.load_tail(q, tail)\r | |
128 | if not fromlist:\r | |
129 | return q\r | |
130 | if m.__path__:\r | |
131 | self.ensure_fromlist(m, fromlist)\r | |
132 | return None\r | |
133 | \r | |
134 | def determine_parent(self, caller, level=-1):\r | |
135 | self.msgin(4, "determine_parent", caller, level)\r | |
136 | if not caller or level == 0:\r | |
137 | self.msgout(4, "determine_parent -> None")\r | |
138 | return None\r | |
139 | pname = caller.__name__\r | |
140 | if level >= 1: # relative import\r | |
141 | if caller.__path__:\r | |
142 | level -= 1\r | |
143 | if level == 0:\r | |
144 | parent = self.modules[pname]\r | |
145 | assert parent is caller\r | |
146 | self.msgout(4, "determine_parent ->", parent)\r | |
147 | return parent\r | |
148 | if pname.count(".") < level:\r | |
149 | raise ImportError, "relative importpath too deep"\r | |
150 | pname = ".".join(pname.split(".")[:-level])\r | |
151 | parent = self.modules[pname]\r | |
152 | self.msgout(4, "determine_parent ->", parent)\r | |
153 | return parent\r | |
154 | if caller.__path__:\r | |
155 | parent = self.modules[pname]\r | |
156 | assert caller is parent\r | |
157 | self.msgout(4, "determine_parent ->", parent)\r | |
158 | return parent\r | |
159 | if '.' in pname:\r | |
160 | i = pname.rfind('.')\r | |
161 | pname = pname[:i]\r | |
162 | parent = self.modules[pname]\r | |
163 | assert parent.__name__ == pname\r | |
164 | self.msgout(4, "determine_parent ->", parent)\r | |
165 | return parent\r | |
166 | self.msgout(4, "determine_parent -> None")\r | |
167 | return None\r | |
168 | \r | |
169 | def find_head_package(self, parent, name):\r | |
170 | self.msgin(4, "find_head_package", parent, name)\r | |
171 | if '.' in name:\r | |
172 | i = name.find('.')\r | |
173 | head = name[:i]\r | |
174 | tail = name[i+1:]\r | |
175 | else:\r | |
176 | head = name\r | |
177 | tail = ""\r | |
178 | if parent:\r | |
179 | qname = "%s.%s" % (parent.__name__, head)\r | |
180 | else:\r | |
181 | qname = head\r | |
182 | q = self.import_module(head, qname, parent)\r | |
183 | if q:\r | |
184 | self.msgout(4, "find_head_package ->", (q, tail))\r | |
185 | return q, tail\r | |
186 | if parent:\r | |
187 | qname = head\r | |
188 | parent = None\r | |
189 | q = self.import_module(head, qname, parent)\r | |
190 | if q:\r | |
191 | self.msgout(4, "find_head_package ->", (q, tail))\r | |
192 | return q, tail\r | |
193 | self.msgout(4, "raise ImportError: No module named", qname)\r | |
194 | raise ImportError, "No module named " + qname\r | |
195 | \r | |
196 | def load_tail(self, q, tail):\r | |
197 | self.msgin(4, "load_tail", q, tail)\r | |
198 | m = q\r | |
199 | while tail:\r | |
200 | i = tail.find('.')\r | |
201 | if i < 0: i = len(tail)\r | |
202 | head, tail = tail[:i], tail[i+1:]\r | |
203 | mname = "%s.%s" % (m.__name__, head)\r | |
204 | m = self.import_module(head, mname, m)\r | |
205 | if not m:\r | |
206 | self.msgout(4, "raise ImportError: No module named", mname)\r | |
207 | raise ImportError, "No module named " + mname\r | |
208 | self.msgout(4, "load_tail ->", m)\r | |
209 | return m\r | |
210 | \r | |
211 | def ensure_fromlist(self, m, fromlist, recursive=0):\r | |
212 | self.msg(4, "ensure_fromlist", m, fromlist, recursive)\r | |
213 | for sub in fromlist:\r | |
214 | if sub == "*":\r | |
215 | if not recursive:\r | |
216 | all = self.find_all_submodules(m)\r | |
217 | if all:\r | |
218 | self.ensure_fromlist(m, all, 1)\r | |
219 | elif not hasattr(m, sub):\r | |
220 | subname = "%s.%s" % (m.__name__, sub)\r | |
221 | submod = self.import_module(sub, subname, m)\r | |
222 | if not submod:\r | |
223 | raise ImportError, "No module named " + subname\r | |
224 | \r | |
225 | def find_all_submodules(self, m):\r | |
226 | if not m.__path__:\r | |
227 | return\r | |
228 | modules = {}\r | |
229 | # 'suffixes' used to be a list hardcoded to [".py", ".pyc", ".pyo"].\r | |
230 | # But we must also collect Python extension modules - although\r | |
231 | # we cannot separate normal dlls from Python extensions.\r | |
232 | suffixes = []\r | |
233 | for triple in imp.get_suffixes():\r | |
234 | suffixes.append(triple[0])\r | |
235 | for dir in m.__path__:\r | |
236 | try:\r | |
237 | names = os.listdir(dir)\r | |
238 | except os.error:\r | |
239 | self.msg(2, "can't list directory", dir)\r | |
240 | continue\r | |
241 | for name in names:\r | |
242 | mod = None\r | |
243 | for suff in suffixes:\r | |
244 | n = len(suff)\r | |
245 | if name[-n:] == suff:\r | |
246 | mod = name[:-n]\r | |
247 | break\r | |
248 | if mod and mod != "__init__":\r | |
249 | modules[mod] = mod\r | |
250 | return modules.keys()\r | |
251 | \r | |
252 | def import_module(self, partname, fqname, parent):\r | |
253 | self.msgin(3, "import_module", partname, fqname, parent)\r | |
254 | try:\r | |
255 | m = self.modules[fqname]\r | |
256 | except KeyError:\r | |
257 | pass\r | |
258 | else:\r | |
259 | self.msgout(3, "import_module ->", m)\r | |
260 | return m\r | |
261 | if fqname in self.badmodules:\r | |
262 | self.msgout(3, "import_module -> None")\r | |
263 | return None\r | |
264 | if parent and parent.__path__ is None:\r | |
265 | self.msgout(3, "import_module -> None")\r | |
266 | return None\r | |
267 | try:\r | |
268 | fp, pathname, stuff = self.find_module(partname,\r | |
269 | parent and parent.__path__, parent)\r | |
270 | except ImportError:\r | |
271 | self.msgout(3, "import_module ->", None)\r | |
272 | return None\r | |
273 | try:\r | |
274 | m = self.load_module(fqname, fp, pathname, stuff)\r | |
275 | finally:\r | |
276 | if fp: fp.close()\r | |
277 | if parent:\r | |
278 | setattr(parent, partname, m)\r | |
279 | self.msgout(3, "import_module ->", m)\r | |
280 | return m\r | |
281 | \r | |
282 | def load_module(self, fqname, fp, pathname, file_info):\r | |
283 | suffix, mode, type = file_info\r | |
284 | self.msgin(2, "load_module", fqname, fp and "fp", pathname)\r | |
285 | if type == imp.PKG_DIRECTORY:\r | |
286 | m = self.load_package(fqname, pathname)\r | |
287 | self.msgout(2, "load_module ->", m)\r | |
288 | return m\r | |
289 | if type == imp.PY_SOURCE:\r | |
290 | co = compile(fp.read()+'\n', pathname, 'exec')\r | |
291 | elif type == imp.PY_COMPILED:\r | |
292 | if fp.read(4) != imp.get_magic():\r | |
293 | self.msgout(2, "raise ImportError: Bad magic number", pathname)\r | |
294 | raise ImportError, "Bad magic number in %s" % pathname\r | |
295 | fp.read(4)\r | |
296 | co = marshal.load(fp)\r | |
297 | else:\r | |
298 | co = None\r | |
299 | m = self.add_module(fqname)\r | |
300 | m.__file__ = pathname\r | |
301 | if co:\r | |
302 | if self.replace_paths:\r | |
303 | co = self.replace_paths_in_code(co)\r | |
304 | m.__code__ = co\r | |
305 | self.scan_code(co, m)\r | |
306 | self.msgout(2, "load_module ->", m)\r | |
307 | return m\r | |
308 | \r | |
309 | def _add_badmodule(self, name, caller):\r | |
310 | if name not in self.badmodules:\r | |
311 | self.badmodules[name] = {}\r | |
312 | if caller:\r | |
313 | self.badmodules[name][caller.__name__] = 1\r | |
314 | else:\r | |
315 | self.badmodules[name]["-"] = 1\r | |
316 | \r | |
317 | def _safe_import_hook(self, name, caller, fromlist, level=-1):\r | |
318 | # wrapper for self.import_hook() that won't raise ImportError\r | |
319 | if name in self.badmodules:\r | |
320 | self._add_badmodule(name, caller)\r | |
321 | return\r | |
322 | try:\r | |
323 | self.import_hook(name, caller, level=level)\r | |
324 | except ImportError, msg:\r | |
325 | self.msg(2, "ImportError:", str(msg))\r | |
326 | self._add_badmodule(name, caller)\r | |
327 | else:\r | |
328 | if fromlist:\r | |
329 | for sub in fromlist:\r | |
330 | if sub in self.badmodules:\r | |
331 | self._add_badmodule(sub, caller)\r | |
332 | continue\r | |
333 | try:\r | |
334 | self.import_hook(name, caller, [sub], level=level)\r | |
335 | except ImportError, msg:\r | |
336 | self.msg(2, "ImportError:", str(msg))\r | |
337 | fullname = name + "." + sub\r | |
338 | self._add_badmodule(fullname, caller)\r | |
339 | \r | |
340 | def scan_opcodes(self, co,\r | |
341 | unpack = struct.unpack):\r | |
342 | # Scan the code, and yield 'interesting' opcode combinations\r | |
343 | # Version for Python 2.4 and older\r | |
344 | code = co.co_code\r | |
345 | names = co.co_names\r | |
346 | consts = co.co_consts\r | |
347 | while code:\r | |
348 | c = code[0]\r | |
349 | if c in STORE_OPS:\r | |
350 | oparg, = unpack('<H', code[1:3])\r | |
351 | yield "store", (names[oparg],)\r | |
352 | code = code[3:]\r | |
353 | continue\r | |
354 | if c == LOAD_CONST and code[3] == IMPORT_NAME:\r | |
355 | oparg_1, oparg_2 = unpack('<xHxH', code[:6])\r | |
356 | yield "import", (consts[oparg_1], names[oparg_2])\r | |
357 | code = code[6:]\r | |
358 | continue\r | |
359 | if c >= HAVE_ARGUMENT:\r | |
360 | code = code[3:]\r | |
361 | else:\r | |
362 | code = code[1:]\r | |
363 | \r | |
364 | def scan_opcodes_25(self, co,\r | |
365 | unpack = struct.unpack):\r | |
366 | # Scan the code, and yield 'interesting' opcode combinations\r | |
367 | # Python 2.5 version (has absolute and relative imports)\r | |
368 | code = co.co_code\r | |
369 | names = co.co_names\r | |
370 | consts = co.co_consts\r | |
371 | LOAD_LOAD_AND_IMPORT = LOAD_CONST + LOAD_CONST + IMPORT_NAME\r | |
372 | while code:\r | |
373 | c = code[0]\r | |
374 | if c in STORE_OPS:\r | |
375 | oparg, = unpack('<H', code[1:3])\r | |
376 | yield "store", (names[oparg],)\r | |
377 | code = code[3:]\r | |
378 | continue\r | |
379 | if code[:9:3] == LOAD_LOAD_AND_IMPORT:\r | |
380 | oparg_1, oparg_2, oparg_3 = unpack('<xHxHxH', code[:9])\r | |
381 | level = consts[oparg_1]\r | |
382 | if level == -1: # normal import\r | |
383 | yield "import", (consts[oparg_2], names[oparg_3])\r | |
384 | elif level == 0: # absolute import\r | |
385 | yield "absolute_import", (consts[oparg_2], names[oparg_3])\r | |
386 | else: # relative import\r | |
387 | yield "relative_import", (level, consts[oparg_2], names[oparg_3])\r | |
388 | code = code[9:]\r | |
389 | continue\r | |
390 | if c >= HAVE_ARGUMENT:\r | |
391 | code = code[3:]\r | |
392 | else:\r | |
393 | code = code[1:]\r | |
394 | \r | |
395 | def scan_code(self, co, m):\r | |
396 | code = co.co_code\r | |
397 | if sys.version_info >= (2, 5):\r | |
398 | scanner = self.scan_opcodes_25\r | |
399 | else:\r | |
400 | scanner = self.scan_opcodes\r | |
401 | for what, args in scanner(co):\r | |
402 | if what == "store":\r | |
403 | name, = args\r | |
404 | m.globalnames[name] = 1\r | |
405 | elif what in ("import", "absolute_import"):\r | |
406 | fromlist, name = args\r | |
407 | have_star = 0\r | |
408 | if fromlist is not None:\r | |
409 | if "*" in fromlist:\r | |
410 | have_star = 1\r | |
411 | fromlist = [f for f in fromlist if f != "*"]\r | |
412 | if what == "absolute_import": level = 0\r | |
413 | else: level = -1\r | |
414 | self._safe_import_hook(name, m, fromlist, level=level)\r | |
415 | if have_star:\r | |
416 | # We've encountered an "import *". If it is a Python module,\r | |
417 | # the code has already been parsed and we can suck out the\r | |
418 | # global names.\r | |
419 | mm = None\r | |
420 | if m.__path__:\r | |
421 | # At this point we don't know whether 'name' is a\r | |
422 | # submodule of 'm' or a global module. Let's just try\r | |
423 | # the full name first.\r | |
424 | mm = self.modules.get(m.__name__ + "." + name)\r | |
425 | if mm is None:\r | |
426 | mm = self.modules.get(name)\r | |
427 | if mm is not None:\r | |
428 | m.globalnames.update(mm.globalnames)\r | |
429 | m.starimports.update(mm.starimports)\r | |
430 | if mm.__code__ is None:\r | |
431 | m.starimports[name] = 1\r | |
432 | else:\r | |
433 | m.starimports[name] = 1\r | |
434 | elif what == "relative_import":\r | |
435 | level, fromlist, name = args\r | |
436 | if name:\r | |
437 | self._safe_import_hook(name, m, fromlist, level=level)\r | |
438 | else:\r | |
439 | parent = self.determine_parent(m, level=level)\r | |
440 | self._safe_import_hook(parent.__name__, None, fromlist, level=0)\r | |
441 | else:\r | |
442 | # We don't expect anything else from the generator.\r | |
443 | raise RuntimeError(what)\r | |
444 | \r | |
445 | for c in co.co_consts:\r | |
446 | if isinstance(c, type(co)):\r | |
447 | self.scan_code(c, m)\r | |
448 | \r | |
449 | def load_package(self, fqname, pathname):\r | |
450 | self.msgin(2, "load_package", fqname, pathname)\r | |
451 | newname = replacePackageMap.get(fqname)\r | |
452 | if newname:\r | |
453 | fqname = newname\r | |
454 | m = self.add_module(fqname)\r | |
455 | m.__file__ = pathname\r | |
456 | m.__path__ = [pathname]\r | |
457 | \r | |
458 | # As per comment at top of file, simulate runtime __path__ additions.\r | |
459 | m.__path__ = m.__path__ + packagePathMap.get(fqname, [])\r | |
460 | \r | |
461 | fp, buf, stuff = self.find_module("__init__", m.__path__)\r | |
462 | self.load_module(fqname, fp, buf, stuff)\r | |
463 | self.msgout(2, "load_package ->", m)\r | |
464 | if fp:\r | |
465 | fp.close()\r | |
466 | return m\r | |
467 | \r | |
468 | def add_module(self, fqname):\r | |
469 | if fqname in self.modules:\r | |
470 | return self.modules[fqname]\r | |
471 | self.modules[fqname] = m = Module(fqname)\r | |
472 | return m\r | |
473 | \r | |
474 | def find_module(self, name, path, parent=None):\r | |
475 | if parent is not None:\r | |
476 | # assert path is not None\r | |
477 | fullname = parent.__name__+'.'+name\r | |
478 | else:\r | |
479 | fullname = name\r | |
480 | if fullname in self.excludes:\r | |
481 | self.msgout(3, "find_module -> Excluded", fullname)\r | |
482 | raise ImportError, name\r | |
483 | \r | |
484 | if path is None:\r | |
485 | if name in sys.builtin_module_names:\r | |
486 | return (None, None, ("", "", imp.C_BUILTIN))\r | |
487 | \r | |
488 | path = self.path\r | |
489 | return imp.find_module(name, path)\r | |
490 | \r | |
491 | def report(self):\r | |
492 | """Print a report to stdout, listing the found modules with their\r | |
493 | paths, as well as modules that are missing, or seem to be missing.\r | |
494 | """\r | |
495 | print\r | |
496 | print " %-25s %s" % ("Name", "File")\r | |
497 | print " %-25s %s" % ("----", "----")\r | |
498 | # Print modules found\r | |
499 | keys = self.modules.keys()\r | |
500 | keys.sort()\r | |
501 | for key in keys:\r | |
502 | m = self.modules[key]\r | |
503 | if m.__path__:\r | |
504 | print "P",\r | |
505 | else:\r | |
506 | print "m",\r | |
507 | print "%-25s" % key, m.__file__ or ""\r | |
508 | \r | |
509 | # Print missing modules\r | |
510 | missing, maybe = self.any_missing_maybe()\r | |
511 | if missing:\r | |
512 | print\r | |
513 | print "Missing modules:"\r | |
514 | for name in missing:\r | |
515 | mods = self.badmodules[name].keys()\r | |
516 | mods.sort()\r | |
517 | print "?", name, "imported from", ', '.join(mods)\r | |
518 | # Print modules that may be missing, but then again, maybe not...\r | |
519 | if maybe:\r | |
520 | print\r | |
521 | print "Submodules that appear to be missing, but could also be",\r | |
522 | print "global names in the parent package:"\r | |
523 | for name in maybe:\r | |
524 | mods = self.badmodules[name].keys()\r | |
525 | mods.sort()\r | |
526 | print "?", name, "imported from", ', '.join(mods)\r | |
527 | \r | |
528 | def any_missing(self):\r | |
529 | """Return a list of modules that appear to be missing. Use\r | |
530 | any_missing_maybe() if you want to know which modules are\r | |
531 | certain to be missing, and which *may* be missing.\r | |
532 | """\r | |
533 | missing, maybe = self.any_missing_maybe()\r | |
534 | return missing + maybe\r | |
535 | \r | |
536 | def any_missing_maybe(self):\r | |
537 | """Return two lists, one with modules that are certainly missing\r | |
538 | and one with modules that *may* be missing. The latter names could\r | |
539 | either be submodules *or* just global names in the package.\r | |
540 | \r | |
541 | The reason it can't always be determined is that it's impossible to\r | |
542 | tell which names are imported when "from module import *" is done\r | |
543 | with an extension module, short of actually importing it.\r | |
544 | """\r | |
545 | missing = []\r | |
546 | maybe = []\r | |
547 | for name in self.badmodules:\r | |
548 | if name in self.excludes:\r | |
549 | continue\r | |
550 | i = name.rfind(".")\r | |
551 | if i < 0:\r | |
552 | missing.append(name)\r | |
553 | continue\r | |
554 | subname = name[i+1:]\r | |
555 | pkgname = name[:i]\r | |
556 | pkg = self.modules.get(pkgname)\r | |
557 | if pkg is not None:\r | |
558 | if pkgname in self.badmodules[name]:\r | |
559 | # The package tried to import this module itself and\r | |
560 | # failed. It's definitely missing.\r | |
561 | missing.append(name)\r | |
562 | elif subname in pkg.globalnames:\r | |
563 | # It's a global in the package: definitely not missing.\r | |
564 | pass\r | |
565 | elif pkg.starimports:\r | |
566 | # It could be missing, but the package did an "import *"\r | |
567 | # from a non-Python module, so we simply can't be sure.\r | |
568 | maybe.append(name)\r | |
569 | else:\r | |
570 | # It's not a global in the package, the package didn't\r | |
571 | # do funny star imports, it's very likely to be missing.\r | |
572 | # The symbol could be inserted into the package from the\r | |
573 | # outside, but since that's not good style we simply list\r | |
574 | # it missing.\r | |
575 | missing.append(name)\r | |
576 | else:\r | |
577 | missing.append(name)\r | |
578 | missing.sort()\r | |
579 | maybe.sort()\r | |
580 | return missing, maybe\r | |
581 | \r | |
582 | def replace_paths_in_code(self, co):\r | |
583 | new_filename = original_filename = os.path.normpath(co.co_filename)\r | |
584 | for f, r in self.replace_paths:\r | |
585 | if original_filename.startswith(f):\r | |
586 | new_filename = r + original_filename[len(f):]\r | |
587 | break\r | |
588 | \r | |
589 | if self.debug and original_filename not in self.processed_paths:\r | |
590 | if new_filename != original_filename:\r | |
591 | self.msgout(2, "co_filename %r changed to %r" \\r | |
592 | % (original_filename,new_filename,))\r | |
593 | else:\r | |
594 | self.msgout(2, "co_filename %r remains unchanged" \\r | |
595 | % (original_filename,))\r | |
596 | self.processed_paths.append(original_filename)\r | |
597 | \r | |
598 | consts = list(co.co_consts)\r | |
599 | for i in range(len(consts)):\r | |
600 | if isinstance(consts[i], type(co)):\r | |
601 | consts[i] = self.replace_paths_in_code(consts[i])\r | |
602 | \r | |
603 | return types.CodeType(co.co_argcount, co.co_nlocals, co.co_stacksize,\r | |
604 | co.co_flags, co.co_code, tuple(consts), co.co_names,\r | |
605 | co.co_varnames, new_filename, co.co_name,\r | |
606 | co.co_firstlineno, co.co_lnotab,\r | |
607 | co.co_freevars, co.co_cellvars)\r | |
608 | \r | |
609 | \r | |
610 | def test():\r | |
611 | # Parse command line\r | |
612 | import getopt\r | |
613 | try:\r | |
614 | opts, args = getopt.getopt(sys.argv[1:], "dmp:qx:")\r | |
615 | except getopt.error, msg:\r | |
616 | print msg\r | |
617 | return\r | |
618 | \r | |
619 | # Process options\r | |
620 | debug = 1\r | |
621 | domods = 0\r | |
622 | addpath = []\r | |
623 | exclude = []\r | |
624 | for o, a in opts:\r | |
625 | if o == '-d':\r | |
626 | debug = debug + 1\r | |
627 | if o == '-m':\r | |
628 | domods = 1\r | |
629 | if o == '-p':\r | |
630 | addpath = addpath + a.split(os.pathsep)\r | |
631 | if o == '-q':\r | |
632 | debug = 0\r | |
633 | if o == '-x':\r | |
634 | exclude.append(a)\r | |
635 | \r | |
636 | # Provide default arguments\r | |
637 | if not args:\r | |
638 | script = "hello.py"\r | |
639 | else:\r | |
640 | script = args[0]\r | |
641 | \r | |
642 | # Set the path based on sys.path and the script directory\r | |
643 | path = sys.path[:]\r | |
644 | path[0] = os.path.dirname(script)\r | |
645 | path = addpath + path\r | |
646 | if debug > 1:\r | |
647 | print "path:"\r | |
648 | for item in path:\r | |
649 | print " ", repr(item)\r | |
650 | \r | |
651 | # Create the module finder and turn its crank\r | |
652 | mf = ModuleFinder(path, debug, exclude)\r | |
653 | for arg in args[1:]:\r | |
654 | if arg == '-m':\r | |
655 | domods = 1\r | |
656 | continue\r | |
657 | if domods:\r | |
658 | if arg[-2:] == '.*':\r | |
659 | mf.import_hook(arg[:-2], None, ["*"])\r | |
660 | else:\r | |
661 | mf.import_hook(arg)\r | |
662 | else:\r | |
663 | mf.load_file(arg)\r | |
664 | mf.run_script(script)\r | |
665 | mf.report()\r | |
666 | return mf # for -i debugging\r | |
667 | \r | |
668 | \r | |
669 | if __name__ == '__main__':\r | |
670 | try:\r | |
671 | mf = test()\r | |
672 | except KeyboardInterrupt:\r | |
673 | print "\n[interrupt]"\r |