blob: b6b36e20f6a489c6acef553da4a27eeb7c64bef2 [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
11
12IMPORT_NAME = dis.opname.index('IMPORT_NAME')
13IMPORT_FROM = dis.opname.index('IMPORT_FROM')
14
15
16class Module:
17
18 def __init__(self, name, file=None, path=None):
Guido van Rossum912a14c1998-03-05 04:56:37 +000019 self.__name__ = name
20 self.__file__ = file
21 self.__path__ = path
22 self.__code__ = None
Guido van Rossum75dc4961998-03-05 03:42:00 +000023
24 def __repr__(self):
Guido van Rossum912a14c1998-03-05 04:56:37 +000025 s = "Module(%s" % `self.__name__`
26 if self.__file__ is not None:
27 s = s + ", %s" % `self.__file__`
28 if self.__path__ is not None:
29 s = s + ", %s" % `self.__path__`
30 s = s + ")"
31 return s
Guido van Rossum75dc4961998-03-05 03:42:00 +000032
33
34class ModuleFinder:
35
36 def __init__(self, path=None, debug=0):
Guido van Rossum912a14c1998-03-05 04:56:37 +000037 if path is None:
38 path = sys.path
39 self.path = path
40 self.modules = {}
41 self.badmodules = {}
42 self.debug = debug
43 self.indent = 0
Guido van Rossum75dc4961998-03-05 03:42:00 +000044
45 def msg(self, level, str, *args):
Guido van Rossum912a14c1998-03-05 04:56:37 +000046 if level <= self.debug:
47 for i in range(self.indent):
48 print " ",
49 print str,
50 for arg in args:
51 print repr(arg),
52 print
Guido van Rossum75dc4961998-03-05 03:42:00 +000053
54 def msgin(self, *args):
Guido van Rossum912a14c1998-03-05 04:56:37 +000055 level = args[0]
56 if level <= self.debug:
57 self.indent = self.indent + 1
58 apply(self.msg, args)
Guido van Rossum75dc4961998-03-05 03:42:00 +000059
60 def msgout(self, *args):
Guido van Rossum912a14c1998-03-05 04:56:37 +000061 level = args[0]
62 if level <= self.debug:
63 self.indent = self.indent - 1
64 apply(self.msg, args)
Guido van Rossum75dc4961998-03-05 03:42:00 +000065
66 def run_script(self, pathname):
Guido van Rossum912a14c1998-03-05 04:56:37 +000067 self.msg(2, "run_script", pathname)
68 fp = open(pathname)
69 stuff = ("", "r", imp.PY_SOURCE)
70 self.load_module('__main__', fp, pathname, stuff)
Guido van Rossum75dc4961998-03-05 03:42:00 +000071
72 def load_file(self, pathname):
Guido van Rossum912a14c1998-03-05 04:56:37 +000073 dir, name = os.path.split(pathname)
74 name, ext = os.path.splitext(name)
75 fp = open(pathname)
76 stuff = (ext, "r", imp.PY_SOURCE)
77 self.load_module(name, fp, pathname, stuff)
Guido van Rossum75dc4961998-03-05 03:42:00 +000078
79 def import_hook(self, name, caller=None, fromlist=None):
Guido van Rossum912a14c1998-03-05 04:56:37 +000080 self.msg(3, "import_hook", name, caller, fromlist)
81 parent = self.determine_parent(caller)
82 q, tail = self.find_head_package(parent, name)
83 m = self.load_tail(q, tail)
84 if not fromlist:
85 return q
86 if m.__path__:
87 self.ensure_fromlist(m, fromlist)
Guido van Rossum75dc4961998-03-05 03:42:00 +000088
89 def determine_parent(self, caller):
Guido van Rossum912a14c1998-03-05 04:56:37 +000090 self.msgin(4, "determine_parent", caller)
91 if not caller:
92 self.msgout(4, "determine_parent -> None")
93 return None
94 pname = caller.__name__
95 if caller.__path__:
96 parent = self.modules[pname]
97 assert caller is parent
98 self.msgout(4, "determine_parent ->", parent)
99 return parent
100 if '.' in pname:
101 i = string.rfind(pname, '.')
102 pname = pname[:i]
103 parent = self.modules[pname]
104 assert parent.__name__ == pname
105 self.msgout(4, "determine_parent ->", parent)
106 return parent
107 self.msgout(4, "determine_parent -> None")
108 return None
Guido van Rossum75dc4961998-03-05 03:42:00 +0000109
110 def find_head_package(self, parent, name):
Guido van Rossum912a14c1998-03-05 04:56:37 +0000111 self.msgin(4, "find_head_package", parent, name)
112 if '.' in name:
113 i = string.find(name, '.')
114 head = name[:i]
115 tail = name[i+1:]
116 else:
117 head = name
118 tail = ""
119 if parent:
120 qname = "%s.%s" % (parent.__name__, head)
121 else:
122 qname = head
123 q = self.import_module(head, qname, parent)
124 if q:
125 self.msgout(4, "find_head_package ->", (q, tail))
126 return q, tail
127 if parent:
128 qname = head
129 parent = None
130 q = self.import_module(head, qname, parent)
131 if q:
132 self.msgout(4, "find_head_package ->", (q, tail))
133 return q, tail
134 self.msgout(4, "raise ImportError: No module named", qname)
135 raise ImportError, "No module named " + qname
Guido van Rossum75dc4961998-03-05 03:42:00 +0000136
137 def load_tail(self, q, tail):
Guido van Rossum912a14c1998-03-05 04:56:37 +0000138 self.msgin(4, "load_tail", q, tail)
139 m = q
140 while tail:
141 i = string.find(tail, '.')
142 if i < 0: i = len(tail)
143 head, tail = tail[:i], tail[i+1:]
144 mname = "%s.%s" % (m.__name__, head)
145 m = self.import_module(head, mname, m)
146 if not m:
147 self.msgout(4, "raise ImportError: No module named", mname)
148 raise ImportError, "No module named " + mname
149 self.msgout(4, "load_tail ->", m)
150 return m
Guido van Rossum75dc4961998-03-05 03:42:00 +0000151
152 def ensure_fromlist(self, m, fromlist, recursive=0):
Guido van Rossum912a14c1998-03-05 04:56:37 +0000153 self.msg(4, "ensure_fromlist", m, fromlist, recursive)
154 for sub in fromlist:
155 if sub == "*":
156 if not recursive:
157 all = self.find_all_submodules(m)
158 if all:
159 self.ensure_fromlist(m, all, 1)
160 elif not hasattr(m, sub):
161 subname = "%s.%s" % (m.__name__, sub)
162 submod = self.import_module(sub, subname, m)
163 if not submod:
164 raise ImportError, "No module named " + subname
Guido van Rossum75dc4961998-03-05 03:42:00 +0000165
166 def find_all_submodules(self, m):
Guido van Rossum912a14c1998-03-05 04:56:37 +0000167 if not m.__path__:
168 return
169 modules = {}
170 suffixes = [".py", ".pyc", ".pyo"]
171 for dir in m.__path__:
172 try:
173 names = os.listdir(dir)
174 except os.error:
175 self.msg(2, "can't list directory", dir)
176 continue
177 for name in names:
178 mod = None
179 for suff in suffixes:
180 n = len(suff)
181 if name[-n:] == suff:
182 mod = name[:-n]
183 break
184 if mod and mod != "__init__":
185 modules[mod] = mod
186 return modules.keys()
Guido van Rossum75dc4961998-03-05 03:42:00 +0000187
188 def import_module(self, partname, fqname, parent):
Guido van Rossum912a14c1998-03-05 04:56:37 +0000189 self.msgin(3, "import_module", partname, fqname, parent)
190 try:
191 m = self.modules[fqname]
192 except KeyError:
193 pass
194 else:
195 self.msgout(3, "import_module ->", m)
196 return m
197 if self.badmodules.has_key(fqname):
198 self.msgout(3, "import_module -> None")
199 return None
200 try:
201 fp, pathname, stuff = self.find_module(partname,
202 parent and parent.__path__)
203 except ImportError:
204 self.msgout(3, "import_module ->", None)
205 return None
206 try:
207 m = self.load_module(fqname, fp, pathname, stuff)
208 finally:
209 if fp: fp.close()
210 if parent:
211 setattr(parent, partname, m)
212 self.msgout(3, "import_module ->", m)
213 return m
Guido van Rossum75dc4961998-03-05 03:42:00 +0000214
215 def load_module(self, fqname, fp, pathname, (suffix, mode, type)):
Guido van Rossum912a14c1998-03-05 04:56:37 +0000216 self.msgin(2, "load_module", fqname, fp and "fp", pathname)
217 if type == imp.PKG_DIRECTORY:
218 m = self.load_package(fqname, pathname)
219 self.msgout(2, "load_module ->", m)
220 return m
221 if type == imp.PY_SOURCE:
222 co = compile(fp.read(), pathname, 'exec')
223 elif type == imp.PY_COMPILED:
224 if fp.read(4) != imp.get_magic():
225 self.msgout(2, "raise ImportError: Bad magic number", pathname)
226 raise ImportError, "Bad magic number in %s", pathname
227 fp.read(4)
228 co = marshal.load(fp)
229 else:
230 co = None
231 m = self.add_module(fqname)
232 if co:
233 m.__file__ = pathname
234 m.__code__ = co
235 code = co.co_code
236 n = len(code)
237 i = 0
238 lastname = None
239 while i < n:
240 c = code[i]
241 i = i+1
242 op = ord(c)
243 if op >= dis.HAVE_ARGUMENT:
244 oparg = ord(code[i]) + ord(code[i+1])*256
245 i = i+2
246 if op == IMPORT_NAME:
247 name = lastname = co.co_names[oparg]
248 if not self.badmodules.has_key(lastname):
249 try:
250 self.import_hook(name, m)
251 except ImportError, msg:
252 self.msg(2, "ImportError:", str(msg))
253 self.badmodules[name] = None
254 elif op == IMPORT_FROM:
255 name = co.co_names[oparg]
256 assert lastname is not None
257 if not self.badmodules.has_key(lastname):
258 try:
259 self.import_hook(lastname, m, [name])
260 except ImportError, msg:
261 self.msg(2, "ImportError:", str(msg))
262 fullname = lastname + "." + name
263 self.badmodules[fullname] = None
264 else:
265 lastname = None
266 self.msgout(2, "load_module ->", m)
267 return m
Guido van Rossum75dc4961998-03-05 03:42:00 +0000268
269 def load_package(self, fqname, pathname):
Guido van Rossum912a14c1998-03-05 04:56:37 +0000270 self.msgin(2, "load_package", fqname, pathname)
271 m = self.add_module(fqname)
272 m.__file__ = pathname
273 m.__path__ = [pathname]
274 fp, buf, stuff = self.find_module("__init__", m.__path__)
275 self.load_module(fqname, fp, buf, stuff)
276 self.msgout(2, "load_package ->", m)
277 return m
Guido van Rossum75dc4961998-03-05 03:42:00 +0000278
279 def add_module(self, fqname):
Guido van Rossum912a14c1998-03-05 04:56:37 +0000280 if self.modules.has_key(fqname):
281 return self.modules[fqname]
282 self.modules[fqname] = m = Module(fqname)
283 return m
Guido van Rossum75dc4961998-03-05 03:42:00 +0000284
285 def find_module(self, name, path):
Guido van Rossum912a14c1998-03-05 04:56:37 +0000286 if path is None:
287 if name in sys.builtin_module_names:
288 return (None, None, ("", "", imp.C_BUILTIN))
289 path = self.path
290 return imp.find_module(name, path)
Guido van Rossum75dc4961998-03-05 03:42:00 +0000291
292 def report(self):
Guido van Rossum912a14c1998-03-05 04:56:37 +0000293 print
294 print " %-25s %s" % ("Name", "File")
295 print " %-25s %s" % ("----", "----")
296 # Print modules found
297 keys = self.modules.keys()
298 keys.sort()
299 for key in keys:
300 m = self.modules[key]
301 if m.__path__:
302 print "P",
303 else:
304 print "m",
305 print "%-25s" % key, m.__file__ or ""
Guido van Rossum75dc4961998-03-05 03:42:00 +0000306
Guido van Rossum912a14c1998-03-05 04:56:37 +0000307 # Print missing modules
308 keys = self.badmodules.keys()
309 keys.sort()
310 for key in keys:
311 print "?", key
Guido van Rossum75dc4961998-03-05 03:42:00 +0000312
313
314def test():
315 # Parse command line
316 import getopt
317 try:
Guido van Rossum912a14c1998-03-05 04:56:37 +0000318 opts, args = getopt.getopt(sys.argv[1:], "dmp:q")
Guido van Rossum75dc4961998-03-05 03:42:00 +0000319 except getopt.error, msg:
Guido van Rossum912a14c1998-03-05 04:56:37 +0000320 print msg
321 return
Guido van Rossum75dc4961998-03-05 03:42:00 +0000322
323 # Process options
324 debug = 1
325 domods = 0
326 addpath = []
327 for o, a in opts:
Guido van Rossum912a14c1998-03-05 04:56:37 +0000328 if o == '-d':
329 debug = debug + 1
330 if o == '-m':
331 domods = 1
332 if o == '-p':
333 addpath = addpath + string.split(a, os.pathsep)
334 if o == '-q':
335 debug = 0
Guido van Rossum75dc4961998-03-05 03:42:00 +0000336
337 # Provide default arguments
338 if not args:
Guido van Rossum912a14c1998-03-05 04:56:37 +0000339 script = "hello.py"
Guido van Rossum75dc4961998-03-05 03:42:00 +0000340 else:
Guido van Rossum912a14c1998-03-05 04:56:37 +0000341 script = args[0]
Guido van Rossum75dc4961998-03-05 03:42:00 +0000342
343 # Set the path based on sys.path and the script directory
344 path = sys.path[:]
345 path[0] = os.path.dirname(script)
346 path = addpath + path
347 if debug > 1:
Guido van Rossum912a14c1998-03-05 04:56:37 +0000348 print "path:"
349 for item in path:
350 print " ", `item`
Guido van Rossum75dc4961998-03-05 03:42:00 +0000351
352 # Create the module finder and turn its crank
353 mf = ModuleFinder(path, debug)
354 for arg in args[1:]:
Guido van Rossum912a14c1998-03-05 04:56:37 +0000355 if arg == '-m':
356 domods = 1
357 continue
358 if domods:
359 if arg[-2:] == '.*':
360 mf.import_hook(arg[:-2], None, ["*"])
361 else:
362 mf.import_hook(arg)
Guido van Rossum75dc4961998-03-05 03:42:00 +0000363 else:
Guido van Rossum912a14c1998-03-05 04:56:37 +0000364 mf.load_file(arg)
Guido van Rossum75dc4961998-03-05 03:42:00 +0000365 mf.run_script(script)
366 mf.report()
367
368
369if __name__ == '__main__':
370 try:
Guido van Rossum912a14c1998-03-05 04:56:37 +0000371 test()
Guido van Rossum75dc4961998-03-05 03:42:00 +0000372 except KeyboardInterrupt:
Guido van Rossum912a14c1998-03-05 04:56:37 +0000373 print "\n[interrupt]"
374
375# Local Variables:
376# indent-tabs-mode: nil
377# End: