blob: 408c2bafe34ec04ce92f588168e8b803b46148cc [file] [log] [blame]
Guido van Rossum75dc4961998-03-05 03:42:00 +00001"""Find modules used by a script, using introspection."""
2
3import dis
4import imp
5import marshal
6import os
7import re
8import string
9import sys
10
Guido van Rossum78fc3631998-03-20 17:37:24 +000011if sys.platform=="win32":
12 # On Windows, we can locate modules in the registry with
13 # the help of the win32api package.
14 try:
15 import win32api
16 except ImportError:
17 print "The win32api module is not available - modules listed"
18 print "in the registry will not be found."
19 win32api = None
20
Guido van Rossum75dc4961998-03-05 03:42:00 +000021
22IMPORT_NAME = dis.opname.index('IMPORT_NAME')
23IMPORT_FROM = dis.opname.index('IMPORT_FROM')
24
25
26class Module:
27
28 def __init__(self, name, file=None, path=None):
Guido van Rossum912a14c1998-03-05 04:56:37 +000029 self.__name__ = name
30 self.__file__ = file
31 self.__path__ = path
32 self.__code__ = None
Guido van Rossum75dc4961998-03-05 03:42:00 +000033
34 def __repr__(self):
Guido van Rossum912a14c1998-03-05 04:56:37 +000035 s = "Module(%s" % `self.__name__`
36 if self.__file__ is not None:
37 s = s + ", %s" % `self.__file__`
38 if self.__path__ is not None:
39 s = s + ", %s" % `self.__path__`
40 s = s + ")"
41 return s
Guido van Rossum75dc4961998-03-05 03:42:00 +000042
43
44class ModuleFinder:
45
Guido van Rossum78fc3631998-03-20 17:37:24 +000046 def __init__(self, path=None, debug=0, excludes = []):
Guido van Rossum912a14c1998-03-05 04:56:37 +000047 if path is None:
48 path = sys.path
49 self.path = path
50 self.modules = {}
51 self.badmodules = {}
52 self.debug = debug
53 self.indent = 0
Guido van Rossum78fc3631998-03-20 17:37:24 +000054 self.excludes = excludes
Guido van Rossum75dc4961998-03-05 03:42:00 +000055
56 def msg(self, level, str, *args):
Guido van Rossum912a14c1998-03-05 04:56:37 +000057 if level <= self.debug:
58 for i in range(self.indent):
59 print " ",
60 print str,
61 for arg in args:
62 print repr(arg),
63 print
Guido van Rossum75dc4961998-03-05 03:42:00 +000064
65 def msgin(self, *args):
Guido van Rossum912a14c1998-03-05 04:56:37 +000066 level = args[0]
67 if level <= self.debug:
68 self.indent = self.indent + 1
69 apply(self.msg, args)
Guido van Rossum75dc4961998-03-05 03:42:00 +000070
71 def msgout(self, *args):
Guido van Rossum912a14c1998-03-05 04:56:37 +000072 level = args[0]
73 if level <= self.debug:
74 self.indent = self.indent - 1
75 apply(self.msg, args)
Guido van Rossum75dc4961998-03-05 03:42:00 +000076
77 def run_script(self, pathname):
Guido van Rossum912a14c1998-03-05 04:56:37 +000078 self.msg(2, "run_script", pathname)
79 fp = open(pathname)
80 stuff = ("", "r", imp.PY_SOURCE)
81 self.load_module('__main__', fp, pathname, stuff)
Guido van Rossum75dc4961998-03-05 03:42:00 +000082
83 def load_file(self, pathname):
Guido van Rossum912a14c1998-03-05 04:56:37 +000084 dir, name = os.path.split(pathname)
85 name, ext = os.path.splitext(name)
86 fp = open(pathname)
87 stuff = (ext, "r", imp.PY_SOURCE)
88 self.load_module(name, fp, pathname, stuff)
Guido van Rossum75dc4961998-03-05 03:42:00 +000089
90 def import_hook(self, name, caller=None, fromlist=None):
Guido van Rossum912a14c1998-03-05 04:56:37 +000091 self.msg(3, "import_hook", name, caller, fromlist)
92 parent = self.determine_parent(caller)
93 q, tail = self.find_head_package(parent, name)
94 m = self.load_tail(q, tail)
95 if not fromlist:
96 return q
97 if m.__path__:
98 self.ensure_fromlist(m, fromlist)
Guido van Rossum75dc4961998-03-05 03:42:00 +000099
100 def determine_parent(self, caller):
Guido van Rossum912a14c1998-03-05 04:56:37 +0000101 self.msgin(4, "determine_parent", caller)
102 if not caller:
103 self.msgout(4, "determine_parent -> None")
104 return None
105 pname = caller.__name__
106 if caller.__path__:
107 parent = self.modules[pname]
108 assert caller is parent
109 self.msgout(4, "determine_parent ->", parent)
110 return parent
111 if '.' in pname:
112 i = string.rfind(pname, '.')
113 pname = pname[:i]
114 parent = self.modules[pname]
115 assert parent.__name__ == pname
116 self.msgout(4, "determine_parent ->", parent)
117 return parent
118 self.msgout(4, "determine_parent -> None")
119 return None
Guido van Rossum75dc4961998-03-05 03:42:00 +0000120
121 def find_head_package(self, parent, name):
Guido van Rossum912a14c1998-03-05 04:56:37 +0000122 self.msgin(4, "find_head_package", parent, name)
123 if '.' in name:
124 i = string.find(name, '.')
125 head = name[:i]
126 tail = name[i+1:]
127 else:
128 head = name
129 tail = ""
130 if parent:
131 qname = "%s.%s" % (parent.__name__, head)
132 else:
133 qname = head
134 q = self.import_module(head, qname, parent)
135 if q:
136 self.msgout(4, "find_head_package ->", (q, tail))
137 return q, tail
138 if parent:
139 qname = head
140 parent = None
141 q = self.import_module(head, qname, parent)
142 if q:
143 self.msgout(4, "find_head_package ->", (q, tail))
144 return q, tail
145 self.msgout(4, "raise ImportError: No module named", qname)
146 raise ImportError, "No module named " + qname
Guido van Rossum75dc4961998-03-05 03:42:00 +0000147
148 def load_tail(self, q, tail):
Guido van Rossum912a14c1998-03-05 04:56:37 +0000149 self.msgin(4, "load_tail", q, tail)
150 m = q
151 while tail:
152 i = string.find(tail, '.')
153 if i < 0: i = len(tail)
154 head, tail = tail[:i], tail[i+1:]
155 mname = "%s.%s" % (m.__name__, head)
156 m = self.import_module(head, mname, m)
157 if not m:
158 self.msgout(4, "raise ImportError: No module named", mname)
159 raise ImportError, "No module named " + mname
160 self.msgout(4, "load_tail ->", m)
161 return m
Guido van Rossum75dc4961998-03-05 03:42:00 +0000162
163 def ensure_fromlist(self, m, fromlist, recursive=0):
Guido van Rossum912a14c1998-03-05 04:56:37 +0000164 self.msg(4, "ensure_fromlist", m, fromlist, recursive)
165 for sub in fromlist:
166 if sub == "*":
167 if not recursive:
168 all = self.find_all_submodules(m)
169 if all:
170 self.ensure_fromlist(m, all, 1)
171 elif not hasattr(m, sub):
172 subname = "%s.%s" % (m.__name__, sub)
173 submod = self.import_module(sub, subname, m)
174 if not submod:
175 raise ImportError, "No module named " + subname
Guido van Rossum75dc4961998-03-05 03:42:00 +0000176
177 def find_all_submodules(self, m):
Guido van Rossum912a14c1998-03-05 04:56:37 +0000178 if not m.__path__:
179 return
180 modules = {}
181 suffixes = [".py", ".pyc", ".pyo"]
182 for dir in m.__path__:
183 try:
184 names = os.listdir(dir)
185 except os.error:
186 self.msg(2, "can't list directory", dir)
187 continue
188 for name in names:
189 mod = None
190 for suff in suffixes:
191 n = len(suff)
192 if name[-n:] == suff:
193 mod = name[:-n]
194 break
195 if mod and mod != "__init__":
196 modules[mod] = mod
197 return modules.keys()
Guido van Rossum75dc4961998-03-05 03:42:00 +0000198
199 def import_module(self, partname, fqname, parent):
Guido van Rossum912a14c1998-03-05 04:56:37 +0000200 self.msgin(3, "import_module", partname, fqname, parent)
201 try:
202 m = self.modules[fqname]
203 except KeyError:
204 pass
205 else:
206 self.msgout(3, "import_module ->", m)
207 return m
208 if self.badmodules.has_key(fqname):
209 self.msgout(3, "import_module -> None")
210 return None
211 try:
212 fp, pathname, stuff = self.find_module(partname,
213 parent and parent.__path__)
214 except ImportError:
215 self.msgout(3, "import_module ->", None)
216 return None
217 try:
218 m = self.load_module(fqname, fp, pathname, stuff)
219 finally:
220 if fp: fp.close()
221 if parent:
222 setattr(parent, partname, m)
223 self.msgout(3, "import_module ->", m)
224 return m
Guido van Rossum75dc4961998-03-05 03:42:00 +0000225
226 def load_module(self, fqname, fp, pathname, (suffix, mode, type)):
Guido van Rossum912a14c1998-03-05 04:56:37 +0000227 self.msgin(2, "load_module", fqname, fp and "fp", pathname)
228 if type == imp.PKG_DIRECTORY:
229 m = self.load_package(fqname, pathname)
230 self.msgout(2, "load_module ->", m)
231 return m
232 if type == imp.PY_SOURCE:
Guido van Rossum78fc3631998-03-20 17:37:24 +0000233 co = compile(fp.read()+'\n', pathname, 'exec')
Guido van Rossum912a14c1998-03-05 04:56:37 +0000234 elif type == imp.PY_COMPILED:
235 if fp.read(4) != imp.get_magic():
236 self.msgout(2, "raise ImportError: Bad magic number", pathname)
237 raise ImportError, "Bad magic number in %s", pathname
238 fp.read(4)
239 co = marshal.load(fp)
240 else:
241 co = None
242 m = self.add_module(fqname)
Guido van Rossumab045f91998-03-06 19:55:10 +0000243 m.__file__ = pathname
Guido van Rossum912a14c1998-03-05 04:56:37 +0000244 if co:
Guido van Rossum912a14c1998-03-05 04:56:37 +0000245 m.__code__ = co
Guido van Rossum3c51cf21998-03-05 05:15:07 +0000246 self.scan_code(co, m)
Guido van Rossum912a14c1998-03-05 04:56:37 +0000247 self.msgout(2, "load_module ->", m)
248 return m
Guido van Rossum75dc4961998-03-05 03:42:00 +0000249
Guido van Rossum3c51cf21998-03-05 05:15:07 +0000250 def scan_code(self, co, m):
251 code = co.co_code
252 n = len(code)
253 i = 0
254 lastname = None
255 while i < n:
256 c = code[i]
257 i = i+1
258 op = ord(c)
259 if op >= dis.HAVE_ARGUMENT:
260 oparg = ord(code[i]) + ord(code[i+1])*256
261 i = i+2
262 if op == IMPORT_NAME:
263 name = lastname = co.co_names[oparg]
264 if not self.badmodules.has_key(lastname):
265 try:
266 self.import_hook(name, m)
267 except ImportError, msg:
268 self.msg(2, "ImportError:", str(msg))
269 self.badmodules[name] = None
270 elif op == IMPORT_FROM:
271 name = co.co_names[oparg]
272 assert lastname is not None
273 if not self.badmodules.has_key(lastname):
274 try:
275 self.import_hook(lastname, m, [name])
276 except ImportError, msg:
277 self.msg(2, "ImportError:", str(msg))
278 fullname = lastname + "." + name
279 self.badmodules[fullname] = None
280 else:
281 lastname = None
282 for c in co.co_consts:
283 if isinstance(c, type(co)):
284 self.scan_code(c, m)
285
Guido van Rossum75dc4961998-03-05 03:42:00 +0000286 def load_package(self, fqname, pathname):
Guido van Rossum912a14c1998-03-05 04:56:37 +0000287 self.msgin(2, "load_package", fqname, pathname)
288 m = self.add_module(fqname)
289 m.__file__ = pathname
290 m.__path__ = [pathname]
291 fp, buf, stuff = self.find_module("__init__", m.__path__)
292 self.load_module(fqname, fp, buf, stuff)
293 self.msgout(2, "load_package ->", m)
294 return m
Guido van Rossum75dc4961998-03-05 03:42:00 +0000295
296 def add_module(self, fqname):
Guido van Rossum912a14c1998-03-05 04:56:37 +0000297 if self.modules.has_key(fqname):
298 return self.modules[fqname]
299 self.modules[fqname] = m = Module(fqname)
300 return m
Guido van Rossum75dc4961998-03-05 03:42:00 +0000301
302 def find_module(self, name, path):
Guido van Rossum78fc3631998-03-20 17:37:24 +0000303 if name in self.excludes:
304 self.msgout(3, "find_module -> Excluded")
305 raise ImportError, name
306
Guido van Rossum912a14c1998-03-05 04:56:37 +0000307 if path is None:
308 if name in sys.builtin_module_names:
309 return (None, None, ("", "", imp.C_BUILTIN))
Guido van Rossum78fc3631998-03-20 17:37:24 +0000310
311 # Emulate the Registered Module support on Windows.
312 if sys.platform=="win32" and win32api is not None:
313 HKEY_LOCAL_MACHINE = 0x80000002
314 try:
315 pathname = win32api.RegQueryValue(HKEY_LOCAL_MACHINE, "Software\\Python\\PythonCore\\%s\\Modules\\%s" % (sys.winver, name))
316 fp = open(pathname, "rb")
317 # XXX - To do - remove the hard code of C_EXTENSION.
318 stuff = "", "rb", imp.C_EXTENSION
319 return fp, pathname, stuff
320 except win32api.error:
321 pass
322
Guido van Rossum912a14c1998-03-05 04:56:37 +0000323 path = self.path
324 return imp.find_module(name, path)
Guido van Rossum75dc4961998-03-05 03:42:00 +0000325
326 def report(self):
Guido van Rossum912a14c1998-03-05 04:56:37 +0000327 print
328 print " %-25s %s" % ("Name", "File")
329 print " %-25s %s" % ("----", "----")
330 # Print modules found
331 keys = self.modules.keys()
332 keys.sort()
333 for key in keys:
334 m = self.modules[key]
335 if m.__path__:
336 print "P",
337 else:
338 print "m",
339 print "%-25s" % key, m.__file__ or ""
Guido van Rossum75dc4961998-03-05 03:42:00 +0000340
Guido van Rossum912a14c1998-03-05 04:56:37 +0000341 # Print missing modules
342 keys = self.badmodules.keys()
343 keys.sort()
344 for key in keys:
345 print "?", key
Guido van Rossum75dc4961998-03-05 03:42:00 +0000346
347
348def test():
349 # Parse command line
350 import getopt
351 try:
Guido van Rossum912a14c1998-03-05 04:56:37 +0000352 opts, args = getopt.getopt(sys.argv[1:], "dmp:q")
Guido van Rossum75dc4961998-03-05 03:42:00 +0000353 except getopt.error, msg:
Guido van Rossum912a14c1998-03-05 04:56:37 +0000354 print msg
355 return
Guido van Rossum75dc4961998-03-05 03:42:00 +0000356
357 # Process options
358 debug = 1
359 domods = 0
360 addpath = []
361 for o, a in opts:
Guido van Rossum912a14c1998-03-05 04:56:37 +0000362 if o == '-d':
363 debug = debug + 1
364 if o == '-m':
365 domods = 1
366 if o == '-p':
367 addpath = addpath + string.split(a, os.pathsep)
368 if o == '-q':
369 debug = 0
Guido van Rossum75dc4961998-03-05 03:42:00 +0000370
371 # Provide default arguments
372 if not args:
Guido van Rossum912a14c1998-03-05 04:56:37 +0000373 script = "hello.py"
Guido van Rossum75dc4961998-03-05 03:42:00 +0000374 else:
Guido van Rossum912a14c1998-03-05 04:56:37 +0000375 script = args[0]
Guido van Rossum75dc4961998-03-05 03:42:00 +0000376
377 # Set the path based on sys.path and the script directory
378 path = sys.path[:]
379 path[0] = os.path.dirname(script)
380 path = addpath + path
381 if debug > 1:
Guido van Rossum912a14c1998-03-05 04:56:37 +0000382 print "path:"
383 for item in path:
384 print " ", `item`
Guido van Rossum75dc4961998-03-05 03:42:00 +0000385
386 # Create the module finder and turn its crank
387 mf = ModuleFinder(path, debug)
388 for arg in args[1:]:
Guido van Rossum912a14c1998-03-05 04:56:37 +0000389 if arg == '-m':
390 domods = 1
391 continue
392 if domods:
393 if arg[-2:] == '.*':
394 mf.import_hook(arg[:-2], None, ["*"])
395 else:
396 mf.import_hook(arg)
Guido van Rossum75dc4961998-03-05 03:42:00 +0000397 else:
Guido van Rossum912a14c1998-03-05 04:56:37 +0000398 mf.load_file(arg)
Guido van Rossum75dc4961998-03-05 03:42:00 +0000399 mf.run_script(script)
400 mf.report()
401
402
403if __name__ == '__main__':
404 try:
Guido van Rossum912a14c1998-03-05 04:56:37 +0000405 test()
Guido van Rossum75dc4961998-03-05 03:42:00 +0000406 except KeyboardInterrupt:
Guido van Rossum912a14c1998-03-05 04:56:37 +0000407 print "\n[interrupt]"
408
409# Local Variables:
410# indent-tabs-mode: nil
411# End: