1 """Find modules used by a script, using introspection."""
12 IMPORT_NAME
= dis
.opname
.index('IMPORT_NAME')
13 IMPORT_FROM
= dis
.opname
.index('IMPORT_FROM')
14 STORE_NAME
= dis
.opname
.index('STORE_NAME')
15 STORE_FAST
= dis
.opname
.index('STORE_FAST')
16 STORE_GLOBAL
= dis
.opname
.index('STORE_GLOBAL')
17 STORE_OPS
= [STORE_NAME
, STORE_FAST
, STORE_GLOBAL
]
19 # Modulefinder does a good job at simulating Python's, but it can not
20 # handle __path__ modifications packages make at runtime. Therefore there
21 # is a mechanism whereby you can register extra paths in this map for a
22 # package, and it will be honored.
24 # Note this is a mapping is lists of paths.
28 def AddPackagePath(packagename
, path
):
29 paths
= packagePathMap
.get(packagename
, [])
31 packagePathMap
[packagename
] = paths
35 def __init__(self
, name
, file=None, path
=None):
42 s
= "Module(%s" % `self
.__name
__`
43 if self
.__file
__ is not None:
44 s
= s
+ ", %s" % `self
.__file
__`
45 if self
.__path
__ is not None:
46 s
= s
+ ", %s" % `self
.__path
__`
52 def _try_registry(name
):
53 # Emulate the Registered Module support on Windows.
56 RegQueryValue
= _winreg
.QueryValue
57 HKLM
= _winreg
.HKEY_LOCAL_MACHINE
58 exception
= _winreg
.error
62 RegQueryValue
= win32api
.RegQueryValue
63 HKLM
= 0x80000002 # HKEY_LOCAL_MACHINE
64 exception
= win32api
.error
69 print "Warning: Neither _winreg nor win32api is available - modules"
70 print "listed in the registry will not be found"
73 pathname
= RegQueryValue(HKLM
, \
74 r
"Software\Python\PythonCore\%s\Modules\%s" % (sys
.winver
, name
))
75 fp
= open(pathname
, "rb")
79 # XXX - To do - remove the hard code of C_EXTENSION.
80 stuff
= "", "rb", imp
.C_EXTENSION
81 return fp
, pathname
, stuff
85 def __init__(self
, path
=None, debug
=0, excludes
= [], replace_paths
= []):
93 self
.excludes
= excludes
94 self
.replace_paths
= replace_paths
95 self
.processed_paths
= [] # Used in debugging only
97 def msg(self
, level
, str, *args
):
98 if level
<= self
.debug
:
99 for i
in range(self
.indent
):
106 def msgin(self
, *args
):
108 if level
<= self
.debug
:
109 self
.indent
= self
.indent
+ 1
110 apply(self
.msg
, args
)
112 def msgout(self
, *args
):
114 if level
<= self
.debug
:
115 self
.indent
= self
.indent
- 1
116 apply(self
.msg
, args
)
118 def run_script(self
, pathname
):
119 self
.msg(2, "run_script", pathname
)
121 stuff
= ("", "r", imp
.PY_SOURCE
)
122 self
.load_module('__main__', fp
, pathname
, stuff
)
124 def load_file(self
, pathname
):
125 dir, name
= os
.path
.split(pathname
)
126 name
, ext
= os
.path
.splitext(name
)
128 stuff
= (ext
, "r", imp
.PY_SOURCE
)
129 self
.load_module(name
, fp
, pathname
, stuff
)
131 def import_hook(self
, name
, caller
=None, fromlist
=None):
132 self
.msg(3, "import_hook", name
, caller
, fromlist
)
133 parent
= self
.determine_parent(caller
)
134 q
, tail
= self
.find_head_package(parent
, name
)
135 m
= self
.load_tail(q
, tail
)
139 self
.ensure_fromlist(m
, fromlist
)
141 def determine_parent(self
, caller
):
142 self
.msgin(4, "determine_parent", caller
)
144 self
.msgout(4, "determine_parent -> None")
146 pname
= caller
.__name
__
148 parent
= self
.modules
[pname
]
149 assert caller
is parent
150 self
.msgout(4, "determine_parent ->", parent
)
153 i
= string
.rfind(pname
, '.')
155 parent
= self
.modules
[pname
]
156 assert parent
.__name
__ == pname
157 self
.msgout(4, "determine_parent ->", parent
)
159 self
.msgout(4, "determine_parent -> None")
162 def find_head_package(self
, parent
, name
):
163 self
.msgin(4, "find_head_package", parent
, name
)
165 i
= string
.find(name
, '.')
172 qname
= "%s.%s" % (parent
.__name
__, head
)
175 q
= self
.import_module(head
, qname
, parent
)
177 self
.msgout(4, "find_head_package ->", (q
, tail
))
182 q
= self
.import_module(head
, qname
, parent
)
184 self
.msgout(4, "find_head_package ->", (q
, tail
))
186 self
.msgout(4, "raise ImportError: No module named", qname
)
187 raise ImportError, "No module named " + qname
189 def load_tail(self
, q
, tail
):
190 self
.msgin(4, "load_tail", q
, tail
)
193 i
= string
.find(tail
, '.')
194 if i
< 0: i
= len(tail
)
195 head
, tail
= tail
[:i
], tail
[i
+1:]
196 mname
= "%s.%s" % (m
.__name
__, head
)
197 m
= self
.import_module(head
, mname
, m
)
199 self
.msgout(4, "raise ImportError: No module named", mname
)
200 raise ImportError, "No module named " + mname
201 self
.msgout(4, "load_tail ->", m
)
204 def ensure_fromlist(self
, m
, fromlist
, recursive
=0):
205 self
.msg(4, "ensure_fromlist", m
, fromlist
, recursive
)
209 all
= self
.find_all_submodules(m
)
211 self
.ensure_fromlist(m
, all
, 1)
212 elif not hasattr(m
, sub
):
213 subname
= "%s.%s" % (m
.__name
__, sub
)
214 submod
= self
.import_module(sub
, subname
, m
)
216 raise ImportError, "No module named " + subname
218 def find_all_submodules(self
, m
):
222 suffixes
= [".py", ".pyc", ".pyo"]
223 for dir in m
.__path
__:
225 names
= os
.listdir(dir)
227 self
.msg(2, "can't list directory", dir)
231 for suff
in suffixes
:
233 if name
[-n
:] == suff
:
236 if mod
and mod
!= "__init__":
238 return modules
.keys()
240 def import_module(self
, partname
, fqname
, parent
):
241 self
.msgin(3, "import_module", partname
, fqname
, parent
)
243 m
= self
.modules
[fqname
]
247 self
.msgout(3, "import_module ->", m
)
249 if self
.badmodules
.has_key(fqname
):
250 self
.msgout(3, "import_module -> None")
252 self
.badmodules
[fqname
][parent
.__name
__] = None
255 fp
, pathname
, stuff
= self
.find_module(partname
,
256 parent
and parent
.__path
__)
258 self
.msgout(3, "import_module ->", None)
261 m
= self
.load_module(fqname
, fp
, pathname
, stuff
)
265 setattr(parent
, partname
, m
)
266 self
.msgout(3, "import_module ->", m
)
269 def load_module(self
, fqname
, fp
, pathname
, (suffix
, mode
, type)):
270 self
.msgin(2, "load_module", fqname
, fp
and "fp", pathname
)
271 if type == imp
.PKG_DIRECTORY
:
272 m
= self
.load_package(fqname
, pathname
)
273 self
.msgout(2, "load_module ->", m
)
275 if type == imp
.PY_SOURCE
:
276 co
= compile(fp
.read()+'\n', pathname
, 'exec')
277 elif type == imp
.PY_COMPILED
:
278 if fp
.read(4) != imp
.get_magic():
279 self
.msgout(2, "raise ImportError: Bad magic number", pathname
)
280 raise ImportError, "Bad magic number in %s" % pathname
282 co
= marshal
.load(fp
)
285 m
= self
.add_module(fqname
)
286 m
.__file
__ = pathname
288 if self
.replace_paths
:
289 co
= self
.replace_paths_in_code(co
)
291 self
.scan_code(co
, m
)
292 self
.msgout(2, "load_module ->", m
)
295 def scan_code(self
, co
, m
):
304 if op
>= dis
.HAVE_ARGUMENT
:
305 oparg
= ord(code
[i
]) + ord(code
[i
+1])*256
307 if op
== IMPORT_NAME
:
308 name
= lastname
= co
.co_names
[oparg
]
309 if not self
.badmodules
.has_key(lastname
):
311 self
.import_hook(name
, m
)
312 except ImportError, msg
:
313 self
.msg(2, "ImportError:", str(msg
))
314 if not self
.badmodules
.has_key(name
):
315 self
.badmodules
[name
] = {}
316 self
.badmodules
[name
][m
.__name
__] = None
317 elif op
== IMPORT_FROM
:
318 name
= co
.co_names
[oparg
]
319 assert lastname
is not None
320 if not self
.badmodules
.has_key(lastname
):
322 self
.import_hook(lastname
, m
, [name
])
323 except ImportError, msg
:
324 self
.msg(2, "ImportError:", str(msg
))
325 fullname
= lastname
+ "." + name
326 if not self
.badmodules
.has_key(fullname
):
327 self
.badmodules
[fullname
] = {}
328 self
.badmodules
[fullname
][m
.__name
__] = None
329 elif op
in STORE_OPS
:
330 # Skip; each IMPORT_FROM is followed by a STORE_* opcode
334 for c
in co
.co_consts
:
335 if isinstance(c
, type(co
)):
338 def load_package(self
, fqname
, pathname
):
339 self
.msgin(2, "load_package", fqname
, pathname
)
340 m
= self
.add_module(fqname
)
341 m
.__file
__ = pathname
342 m
.__path
__ = [pathname
]
344 # As per comment at top of file, simulate runtime __path__ additions.
345 m
.__path
__ = m
.__path
__ + packagePathMap
.get(fqname
, [])
347 fp
, buf
, stuff
= self
.find_module("__init__", m
.__path
__)
348 self
.load_module(fqname
, fp
, buf
, stuff
)
349 self
.msgout(2, "load_package ->", m
)
352 def add_module(self
, fqname
):
353 if self
.modules
.has_key(fqname
):
354 return self
.modules
[fqname
]
355 self
.modules
[fqname
] = m
= Module(fqname
)
358 def find_module(self
, name
, path
):
360 fullname
= '.'.join(path
)+'.'+name
363 if fullname
in self
.excludes
:
364 self
.msgout(3, "find_module -> Excluded", fullname
)
365 raise ImportError, name
368 if name
in sys
.builtin_module_names
:
369 return (None, None, ("", "", imp
.C_BUILTIN
))
371 if sys
.platform
=="win32":
372 result
= _try_registry(name
)
377 return imp
.find_module(name
, path
)
381 print " %-25s %s" % ("Name", "File")
382 print " %-25s %s" % ("----", "----")
383 # Print modules found
384 keys
= self
.modules
.keys()
387 m
= self
.modules
[key
]
392 print "%-25s" % key
, m
.__file
__ or ""
394 # Print missing modules
395 keys
= self
.badmodules
.keys()
398 # ... but not if they were explicitly excluded.
399 if key
not in self
.excludes
:
400 mods
= self
.badmodules
[key
].keys()
402 print "?", key
, "from", string
.join(mods
, ', ')
404 def any_missing(self
):
405 keys
= self
.badmodules
.keys()
408 if key
not in self
.excludes
:
409 # Missing, and its not supposed to be
413 def replace_paths_in_code(self
, co
):
414 new_filename
= original_filename
= os
.path
.normpath(co
.co_filename
)
415 for f
,r
in self
.replace_paths
:
416 if original_filename
.startswith(f
):
417 new_filename
= r
+original_filename
[len(f
):]
420 if self
.debug
and original_filename
not in self
.processed_paths
:
421 if new_filename
!=original_filename
:
422 self
.msgout(2, "co_filename %r changed to %r" \
423 % (original_filename
,new_filename
,))
425 self
.msgout(2, "co_filename %r remains unchanged" \
426 % (original_filename
,))
427 self
.processed_paths
.append(original_filename
)
429 consts
= list(co
.co_consts
)
430 for i
in range(len(consts
)):
431 if isinstance(consts
[i
], type(co
)):
432 consts
[i
] = self
.replace_paths_in_code(consts
[i
])
434 return new
.code(co
.co_argcount
, co
.co_nlocals
, co
.co_stacksize
,
435 co
.co_flags
, co
.co_code
, tuple(consts
), co
.co_names
,
436 co
.co_varnames
, new_filename
, co
.co_name
,
437 co
.co_firstlineno
, co
.co_lnotab
)
444 opts
, args
= getopt
.getopt(sys
.argv
[1:], "dmp:qx:")
445 except getopt
.error
, msg
:
460 addpath
= addpath
+ string
.split(a
, os
.pathsep
)
466 # Provide default arguments
472 # Set the path based on sys.path and the script directory
474 path
[0] = os
.path
.dirname(script
)
475 path
= addpath
+ path
481 # Create the module finder and turn its crank
482 mf
= ModuleFinder(path
, debug
, exclude
)
489 mf
.import_hook(arg
[:-2], None, ["*"])
494 mf
.run_script(script
)
498 if __name__
== '__main__':
501 except KeyboardInterrupt:
502 print "\n[interrupt]"