1 """Find modules used by a script, using introspection."""
3 # This module should be kept compatible with Python 2.2, see PEP 291.
12 if hasattr(sys
.__stdout
__, "newlines"):
13 READ_MODE
= "U" # universal line endings
15 # remain compatible with Python < 2.3
18 LOAD_CONST
= dis
.opname
.index('LOAD_CONST')
19 IMPORT_NAME
= dis
.opname
.index('IMPORT_NAME')
20 STORE_NAME
= dis
.opname
.index('STORE_NAME')
21 STORE_GLOBAL
= dis
.opname
.index('STORE_GLOBAL')
22 STORE_OPS
= [STORE_NAME
, STORE_GLOBAL
]
24 # Modulefinder does a good job at simulating Python's, but it can not
25 # handle __path__ modifications packages make at runtime. Therefore there
26 # is a mechanism whereby you can register extra paths in this map for a
27 # package, and it will be honored.
29 # Note this is a mapping is lists of paths.
33 def AddPackagePath(packagename
, path
):
34 paths
= packagePathMap
.get(packagename
, [])
36 packagePathMap
[packagename
] = paths
38 replacePackageMap
= {}
40 # This ReplacePackage mechanism allows modulefinder to work around the
41 # way the _xmlplus package injects itself under the name "xml" into
42 # sys.modules at runtime by calling ReplacePackage("_xmlplus", "xml")
43 # before running ModuleFinder.
45 def ReplacePackage(oldname
, newname
):
46 replacePackageMap
[oldname
] = newname
51 def __init__(self
, name
, file=None, path
=None):
56 # The set of global names that are assigned to in the module.
57 # This includes those names imported through starimports of
60 # The set of starimports this module did that could not be
61 # resolved, ie. a starimport from a non-Python module.
65 s
= "Module(%r" % (self
.__name
__,)
66 if self
.__file
__ is not None:
67 s
= s
+ ", %r" % (self
.__file
__,)
68 if self
.__path
__ is not None:
69 s
= s
+ ", %r" % (self
.__path
__,)
75 def __init__(self
, path
=None, debug
=0, excludes
=[], replace_paths
=[]):
83 self
.excludes
= excludes
84 self
.replace_paths
= replace_paths
85 self
.processed_paths
= [] # Used in debugging only
87 def msg(self
, level
, str, *args
):
88 if level
<= self
.debug
:
89 for i
in range(self
.indent
):
96 def msgin(self
, *args
):
98 if level
<= self
.debug
:
99 self
.indent
= self
.indent
+ 1
102 def msgout(self
, *args
):
104 if level
<= self
.debug
:
105 self
.indent
= self
.indent
- 1
108 def run_script(self
, pathname
):
109 self
.msg(2, "run_script", pathname
)
110 fp
= open(pathname
, READ_MODE
)
111 stuff
= ("", "r", imp
.PY_SOURCE
)
112 self
.load_module('__main__', fp
, pathname
, stuff
)
114 def load_file(self
, pathname
):
115 dir, name
= os
.path
.split(pathname
)
116 name
, ext
= os
.path
.splitext(name
)
117 fp
= open(pathname
, READ_MODE
)
118 stuff
= (ext
, "r", imp
.PY_SOURCE
)
119 self
.load_module(name
, fp
, pathname
, stuff
)
121 def import_hook(self
, name
, caller
=None, fromlist
=None):
122 self
.msg(3, "import_hook", name
, caller
, fromlist
)
123 parent
= self
.determine_parent(caller
)
124 q
, tail
= self
.find_head_package(parent
, name
)
125 m
= self
.load_tail(q
, tail
)
129 self
.ensure_fromlist(m
, fromlist
)
132 def determine_parent(self
, caller
):
133 self
.msgin(4, "determine_parent", caller
)
135 self
.msgout(4, "determine_parent -> None")
137 pname
= caller
.__name
__
139 parent
= self
.modules
[pname
]
140 assert caller
is parent
141 self
.msgout(4, "determine_parent ->", parent
)
146 parent
= self
.modules
[pname
]
147 assert parent
.__name
__ == pname
148 self
.msgout(4, "determine_parent ->", parent
)
150 self
.msgout(4, "determine_parent -> None")
153 def find_head_package(self
, parent
, name
):
154 self
.msgin(4, "find_head_package", parent
, name
)
163 qname
= "%s.%s" % (parent
.__name
__, head
)
166 q
= self
.import_module(head
, qname
, parent
)
168 self
.msgout(4, "find_head_package ->", (q
, tail
))
173 q
= self
.import_module(head
, qname
, parent
)
175 self
.msgout(4, "find_head_package ->", (q
, tail
))
177 self
.msgout(4, "raise ImportError: No module named", qname
)
178 raise ImportError, "No module named " + qname
180 def load_tail(self
, q
, tail
):
181 self
.msgin(4, "load_tail", q
, tail
)
185 if i
< 0: i
= len(tail
)
186 head
, tail
= tail
[:i
], tail
[i
+1:]
187 mname
= "%s.%s" % (m
.__name
__, head
)
188 m
= self
.import_module(head
, mname
, m
)
190 self
.msgout(4, "raise ImportError: No module named", mname
)
191 raise ImportError, "No module named " + mname
192 self
.msgout(4, "load_tail ->", m
)
195 def ensure_fromlist(self
, m
, fromlist
, recursive
=0):
196 self
.msg(4, "ensure_fromlist", m
, fromlist
, recursive
)
200 all
= self
.find_all_submodules(m
)
202 self
.ensure_fromlist(m
, all
, 1)
203 elif not hasattr(m
, sub
):
204 subname
= "%s.%s" % (m
.__name
__, sub
)
205 submod
= self
.import_module(sub
, subname
, m
)
207 raise ImportError, "No module named " + subname
209 def find_all_submodules(self
, m
):
213 # 'suffixes' used to be a list hardcoded to [".py", ".pyc", ".pyo"].
214 # But we must also collect Python extension modules - although
215 # we cannot separate normal dlls from Python extensions.
217 for triple
in imp
.get_suffixes():
218 suffixes
.append(triple
[0])
219 for dir in m
.__path
__:
221 names
= os
.listdir(dir)
223 self
.msg(2, "can't list directory", dir)
227 for suff
in suffixes
:
229 if name
[-n
:] == suff
:
232 if mod
and mod
!= "__init__":
234 return modules
.keys()
236 def import_module(self
, partname
, fqname
, parent
):
237 self
.msgin(3, "import_module", partname
, fqname
, parent
)
239 m
= self
.modules
[fqname
]
243 self
.msgout(3, "import_module ->", m
)
245 if self
.badmodules
.has_key(fqname
):
246 self
.msgout(3, "import_module -> None")
248 if parent
and parent
.__path
__ is None:
249 self
.msgout(3, "import_module -> None")
252 fp
, pathname
, stuff
= self
.find_module(partname
,
253 parent
and parent
.__path
__, parent
)
255 self
.msgout(3, "import_module ->", None)
258 m
= self
.load_module(fqname
, fp
, pathname
, stuff
)
262 setattr(parent
, partname
, m
)
263 self
.msgout(3, "import_module ->", m
)
266 def load_module(self
, fqname
, fp
, pathname
, (suffix
, mode
, type)):
267 self
.msgin(2, "load_module", fqname
, fp
and "fp", pathname
)
268 if type == imp
.PKG_DIRECTORY
:
269 m
= self
.load_package(fqname
, pathname
)
270 self
.msgout(2, "load_module ->", m
)
272 if type == imp
.PY_SOURCE
:
273 co
= compile(fp
.read()+'\n', pathname
, 'exec')
274 elif type == imp
.PY_COMPILED
:
275 if fp
.read(4) != imp
.get_magic():
276 self
.msgout(2, "raise ImportError: Bad magic number", pathname
)
277 raise ImportError, "Bad magic number in %s" % pathname
279 co
= marshal
.load(fp
)
282 m
= self
.add_module(fqname
)
283 m
.__file
__ = pathname
285 if self
.replace_paths
:
286 co
= self
.replace_paths_in_code(co
)
288 self
.scan_code(co
, m
)
289 self
.msgout(2, "load_module ->", m
)
292 def _add_badmodule(self
, name
, caller
):
293 if name
not in self
.badmodules
:
294 self
.badmodules
[name
] = {}
295 self
.badmodules
[name
][caller
.__name
__] = 1
297 def _safe_import_hook(self
, name
, caller
, fromlist
):
298 # wrapper for self.import_hook() that won't raise ImportError
299 if name
in self
.badmodules
:
300 self
._add
_badmodule
(name
, caller
)
303 self
.import_hook(name
, caller
)
304 except ImportError, msg
:
305 self
.msg(2, "ImportError:", str(msg
))
306 self
._add
_badmodule
(name
, caller
)
310 if sub
in self
.badmodules
:
311 self
._add
_badmodule
(sub
, caller
)
314 self
.import_hook(name
, caller
, [sub
])
315 except ImportError, msg
:
316 self
.msg(2, "ImportError:", str(msg
))
317 fullname
= name
+ "." + sub
318 self
._add
_badmodule
(fullname
, caller
)
320 def scan_code(self
, co
, m
):
329 if op
>= dis
.HAVE_ARGUMENT
:
330 oparg
= ord(code
[i
]) + ord(code
[i
+1])*256
333 # An IMPORT_NAME is always preceded by a LOAD_CONST, it's
334 # a tuple of "from" names, or None for a regular import.
335 # The tuple may contain "*" for "from <mod> import *"
336 fromlist
= co
.co_consts
[oparg
]
337 elif op
== IMPORT_NAME
:
338 assert fromlist
is None or type(fromlist
) is tuple
339 name
= co
.co_names
[oparg
]
341 if fromlist
is not None:
344 fromlist
= [f
for f
in fromlist
if f
!= "*"]
345 self
._safe
_import
_hook
(name
, m
, fromlist
)
347 # We've encountered an "import *". If it is a Python module,
348 # the code has already been parsed and we can suck out the
352 # At this point we don't know whether 'name' is a
353 # submodule of 'm' or a global module. Let's just try
354 # the full name first.
355 mm
= self
.modules
.get(m
.__name
__ + "." + name
)
357 mm
= self
.modules
.get(name
)
359 m
.globalnames
.update(mm
.globalnames
)
360 m
.starimports
.update(mm
.starimports
)
361 if mm
.__code
__ is None:
362 m
.starimports
[name
] = 1
364 m
.starimports
[name
] = 1
365 elif op
in STORE_OPS
:
366 # keep track of all global names that are assigned to
367 name
= co
.co_names
[oparg
]
368 m
.globalnames
[name
] = 1
369 for c
in co
.co_consts
:
370 if isinstance(c
, type(co
)):
373 def load_package(self
, fqname
, pathname
):
374 self
.msgin(2, "load_package", fqname
, pathname
)
375 newname
= replacePackageMap
.get(fqname
)
378 m
= self
.add_module(fqname
)
379 m
.__file
__ = pathname
380 m
.__path
__ = [pathname
]
382 # As per comment at top of file, simulate runtime __path__ additions.
383 m
.__path
__ = m
.__path
__ + packagePathMap
.get(fqname
, [])
385 fp
, buf
, stuff
= self
.find_module("__init__", m
.__path
__)
386 self
.load_module(fqname
, fp
, buf
, stuff
)
387 self
.msgout(2, "load_package ->", m
)
390 def add_module(self
, fqname
):
391 if self
.modules
.has_key(fqname
):
392 return self
.modules
[fqname
]
393 self
.modules
[fqname
] = m
= Module(fqname
)
396 def find_module(self
, name
, path
, parent
=None):
397 if parent
is not None:
398 # assert path is not None
399 fullname
= parent
.__name
__+'.'+name
402 if fullname
in self
.excludes
:
403 self
.msgout(3, "find_module -> Excluded", fullname
)
404 raise ImportError, name
407 if name
in sys
.builtin_module_names
:
408 return (None, None, ("", "", imp
.C_BUILTIN
))
411 return imp
.find_module(name
, path
)
414 """Print a report to stdout, listing the found modules with their
415 paths, as well as modules that are missing, or seem to be missing.
418 print " %-25s %s" % ("Name", "File")
419 print " %-25s %s" % ("----", "----")
420 # Print modules found
421 keys
= self
.modules
.keys()
424 m
= self
.modules
[key
]
429 print "%-25s" % key
, m
.__file
__ or ""
431 # Print missing modules
432 missing
, maybe
= self
.any_missing_maybe()
435 print "Missing modules:"
437 mods
= self
.badmodules
[name
].keys()
439 print "?", name
, "imported from", ', '.join(mods
)
440 # Print modules that may be missing, but then again, maybe not...
443 print "Submodules thay appear to be missing, but could also be",
444 print "global names in the parent package:"
446 mods
= self
.badmodules
[name
].keys()
448 print "?", name
, "imported from", ', '.join(mods
)
450 def any_missing(self
):
451 """Return a list of modules that appear to be missing. Use
452 any_missing_maybe() if you want to know which modules are
453 certain to be missing, and which *may* be missing.
455 missing
, maybe
= self
.any_missing_maybe()
456 return missing
+ maybe
458 def any_missing_maybe(self
):
459 """Return two lists, one with modules that are certainly missing
460 and one with modules that *may* be missing. The latter names could
461 either be submodules *or* just global names in the package.
463 The reason it can't always be determined is that it's impossible to
464 tell which names are imported when "from module import *" is done
465 with an extension module, short of actually importing it.
469 for name
in self
.badmodules
:
470 if name
in self
.excludes
:
478 pkg
= self
.modules
.get(pkgname
)
480 if pkgname
in self
.badmodules
[name
]:
481 # The package tried to import this module itself and
482 # failed. It's definitely missing.
484 elif subname
in pkg
.globalnames
:
485 # It's a global in the package: definitely not missing.
487 elif pkg
.starimports
:
488 # It could be missing, but the package did an "import *"
489 # from a non-Python module, so we simply can't be sure.
492 # It's not a global in the package, the package didn't
493 # do funny star imports, it's very likely to be missing.
494 # The symbol could be inserted into the package from the
495 # outside, but since that's not good style we simply list
502 return missing
, maybe
504 def replace_paths_in_code(self
, co
):
505 new_filename
= original_filename
= os
.path
.normpath(co
.co_filename
)
506 for f
, r
in self
.replace_paths
:
507 if original_filename
.startswith(f
):
508 new_filename
= r
+ original_filename
[len(f
):]
511 if self
.debug
and original_filename
not in self
.processed_paths
:
512 if new_filename
!= original_filename
:
513 self
.msgout(2, "co_filename %r changed to %r" \
514 % (original_filename
,new_filename
,))
516 self
.msgout(2, "co_filename %r remains unchanged" \
517 % (original_filename
,))
518 self
.processed_paths
.append(original_filename
)
520 consts
= list(co
.co_consts
)
521 for i
in range(len(consts
)):
522 if isinstance(consts
[i
], type(co
)):
523 consts
[i
] = self
.replace_paths_in_code(consts
[i
])
525 return new
.code(co
.co_argcount
, co
.co_nlocals
, co
.co_stacksize
,
526 co
.co_flags
, co
.co_code
, tuple(consts
), co
.co_names
,
527 co
.co_varnames
, new_filename
, co
.co_name
,
528 co
.co_firstlineno
, co
.co_lnotab
,
529 co
.co_freevars
, co
.co_cellvars
)
536 opts
, args
= getopt
.getopt(sys
.argv
[1:], "dmp:qx:")
537 except getopt
.error
, msg
:
552 addpath
= addpath
+ a
.split(os
.pathsep
)
558 # Provide default arguments
564 # Set the path based on sys.path and the script directory
566 path
[0] = os
.path
.dirname(script
)
567 path
= addpath
+ path
571 print " ", repr(item
)
573 # Create the module finder and turn its crank
574 mf
= ModuleFinder(path
, debug
, exclude
)
581 mf
.import_hook(arg
[:-2], None, ["*"])
586 mf
.run_script(script
)
588 return mf
# for -i debugging
591 if __name__
== '__main__':
594 except KeyboardInterrupt:
595 print "\n[interrupt]"