Index: modulefinder.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Tools/freeze/modulefinder.py,v retrieving revision 1.27 diff -c -r1.27 modulefinder.py *** modulefinder.py 26 Nov 2002 09:53:16 -0000 1.27 --- modulefinder.py 27 Nov 2002 10:54:12 -0000 *************** *** 15,26 **** # remain compatible with Python < 2.3 READ_MODE = "r" IMPORT_NAME = dis.opname.index('IMPORT_NAME') - IMPORT_FROM = dis.opname.index('IMPORT_FROM') STORE_NAME = dis.opname.index('STORE_NAME') - STORE_FAST = dis.opname.index('STORE_FAST') STORE_GLOBAL = dis.opname.index('STORE_GLOBAL') ! STORE_OPS = [STORE_NAME, STORE_FAST, STORE_GLOBAL] # Modulefinder does a good job at simulating Python's, but it can not # handle __path__ modifications packages make at runtime. Therefore there --- 15,25 ---- # remain compatible with Python < 2.3 READ_MODE = "r" + LOAD_CONST = dis.opname.index('LOAD_CONST') IMPORT_NAME = dis.opname.index('IMPORT_NAME') STORE_NAME = dis.opname.index('STORE_NAME') STORE_GLOBAL = dis.opname.index('STORE_GLOBAL') ! STORE_OPS = [STORE_NAME, STORE_GLOBAL] # Modulefinder does a good job at simulating Python's, but it can not # handle __path__ modifications packages make at runtime. Therefore there *************** *** 54,59 **** --- 53,66 ---- self.__file__ = file self.__path__ = path self.__code__ = None + # The set of global names that are assigned to in the module. + self.globals = {} + # The set of imports that failed. This is only used for packages in + # case it's not clear whether a name from a package is just a global + # name or an actual submodule. See ModuleFinder.any_missing_maybe() + self.badimports = {} + # XXX Might as well track succeeded imports and modules that + # imported us for cross referencing. def __repr__(self): s = "Module(%s" % `self.__name__` *************** *** 277,287 **** self.msgout(2, "load_module ->", m) return m def scan_code(self, co, m): code = co.co_code n = len(code) i = 0 ! lastname = None while i < n: c = code[i] i = i+1 --- 284,323 ---- self.msgout(2, "load_module ->", m) return m + def _add_badmodule(self, name, caller): + if not self.badmodules.has_key(name): + self.badmodules[name] = {} + self.badmodules[name][caller.__name__] = None + caller.badimports[name] = 1 + + def _safe_import_hook(self, name, caller=None, fromlist=None): + # wrapper for self.import_hook() that won't raise ImportError + if name in self.badmodules: + caller.badimports[name] = 1 + return + try: + self.import_hook(name, caller) + except ImportError, msg: + self.msg(2, "ImportError:", str(msg)) + self._add_badmodule(name, caller) + else: + if fromlist: + for sub in fromlist: + if sub in self.badmodules: + caller.badimports[sub] = 1 + continue + try: + self.import_hook(name, caller, [sub]) + except ImportError, msg: + self.msg(2, "ImportError:", str(msg)) + fullname = name + "." + sub + self._add_badmodule(fullname, caller) + def scan_code(self, co, m): code = co.co_code n = len(code) i = 0 ! fromlist = None while i < n: c = code[i] i = i+1 *************** *** 289,321 **** if op >= dis.HAVE_ARGUMENT: oparg = ord(code[i]) + ord(code[i+1])*256 i = i+2 ! if op == IMPORT_NAME: ! name = lastname = co.co_names[oparg] ! if not self.badmodules.has_key(lastname): ! try: ! self.import_hook(name, m) ! except ImportError, msg: ! self.msg(2, "ImportError:", str(msg)) ! if not self.badmodules.has_key(name): ! self.badmodules[name] = {} ! self.badmodules[name][m.__name__] = None ! elif op == IMPORT_FROM: name = co.co_names[oparg] ! assert lastname is not None ! if not self.badmodules.has_key(lastname): ! try: ! self.import_hook(lastname, m, [name]) ! except ImportError, msg: ! self.msg(2, "ImportError:", str(msg)) ! fullname = lastname + "." + name ! if not self.badmodules.has_key(fullname): ! self.badmodules[fullname] = {} ! self.badmodules[fullname][m.__name__] = None elif op in STORE_OPS: ! # Skip; each IMPORT_FROM is followed by a STORE_* opcode ! pass ! else: ! lastname = None for c in co.co_consts: if isinstance(c, type(co)): self.scan_code(c, m) --- 325,362 ---- if op >= dis.HAVE_ARGUMENT: oparg = ord(code[i]) + ord(code[i+1])*256 i = i+2 ! if op == LOAD_CONST: ! # An IMPORT_NAME is always preceded by a LOAD_CONST, it's ! # a tuple of "from" names, or None for a regular import. ! # The tuple may contain "*" for "from import *" ! fromlist = co.co_consts[oparg] ! elif op == IMPORT_NAME: ! assert fromlist is None or type(fromlist) is tuple name = co.co_names[oparg] ! have_star = 0 ! if fromlist is not None: ! if "*" in fromlist: ! have_star = 1 ! fromlist = [f for f in fromlist if f != "*"] ! self._safe_import_hook(name, m, fromlist) ! if have_star: ! # We've encountered an "import *". If it is a Python module, ! # the code has already been parsed and we can suck out the ! # global names. ! mm = None ! if m.__path__: ! # At this point we don't know whether 'name' is a ! # submodule of 'm' or a global module. Let's just try ! # the full name first. ! mm = self.modules.get(m.__name__ + "." + name) ! if mm is None: ! mm = self.modules.get(name) ! if mm is not None: ! m.globals.update(mm.globals) elif op in STORE_OPS: ! # keep track of all global names that are assigned to ! name = co.co_names[oparg] ! m.globals[name] = 1 for c in co.co_consts: if isinstance(c, type(co)): self.scan_code(c, m) *************** *** 360,365 **** --- 401,409 ---- return imp.find_module(name, path) def report(self): + """Print a report to stdout, listing the found modules with their + paths, as well as modules that are missing, or seem to be missing. + """ print print " %-25s %s" % ("Name", "File") print " %-25s %s" % ("----", "----") *************** *** 375,397 **** print "%-25s" % key, m.__file__ or "" # Print missing modules ! keys = self.badmodules.keys() ! keys.sort() ! for key in keys: ! # ... but not if they were explicitly excluded. ! if key not in self.excludes: ! mods = self.badmodules[key].keys() mods.sort() ! print "?", key, "from", ', '.join(mods) def any_missing(self): ! keys = self.badmodules.keys() missing = [] ! for key in keys: ! if key not in self.excludes: ! # Missing, and its not supposed to be ! missing.append(key) ! return missing def replace_paths_in_code(self, co): new_filename = original_filename = os.path.normpath(co.co_filename) --- 419,493 ---- print "%-25s" % key, m.__file__ or "" # Print missing modules ! missing, maybe = self.any_missing_maybe() ! if missing: ! print ! print "Missing modules:" ! for name in missing: ! mods = self.badmodules[name].keys() mods.sort() ! print "?", name, "imported from", ', '.join(mods) ! # Print modules that may be missing, but then again, maybe not... ! if maybe: ! print ! print "Submodules thay appear to be missing, but could also be", ! print "global names in the parent package:" ! for name in maybe: ! mods = self.badmodules[name].keys() ! mods.sort() ! print "?", name, "imported from", ', '.join(mods) def any_missing(self): ! """Return a list of modules that appear to be missing. Use ! any_missing_maybe() if you want to know which modules are ! certain to be missing, and which *may* be missing. ! """ ! missing, maybe = self.any_missing_maybe() ! return missing + maybe ! ! def any_missing_maybe(self): ! """Return two lists, one with modules that are certainly missing ! and one with modules that *may* be missing. The latter names could ! either be submodules *or* just global names in the package. ! ! The reason it can't always be determined is that it's impossible to ! tell which names are imported when "from module import *" is done ! with an extension module, short of actually importing it. ! """ ! # XXX Would it be worthwhile to (optionally) *do* an actual import ! # on extension modules? We could just remove it afterward from ! # sys.modules. missing = [] ! maybe = [] ! for name in self.badmodules.keys(): ! if name in self.excludes: ! continue ! i = name.rfind(".") ! if i < 0: ! missing.append(name) ! continue ! subname = name[i+1:] ! pkgname = name[:i] ! pkg = self.modules.get(pkgname) ! if pkg is not None: ! if name in pkg.badimports: ! # The package tried to import this module itself and ! # failed. It's definitely missing. ! missing.append(name) ! elif subname in pkg.globals: ! # It's a global in the package: definitely not missing. ! pass ! else: ! # It could be missing, but it also could be that the ! # package did an "import *" from a non-Python module, ! # or that the symbol gets inserted from another module. ! # We simply can't be sure. ! maybe.append(name) ! else: ! missing.append(name) ! missing.sort() ! maybe.sort() ! return missing, maybe def replace_paths_in_code(self, co): new_filename = original_filename = os.path.normpath(co.co_filename) *************** *** 477,486 **** mf.load_file(arg) mf.run_script(script) mf.report() if __name__ == '__main__': try: ! test() except KeyboardInterrupt: print "\n[interrupt]" --- 573,583 ---- mf.load_file(arg) mf.run_script(script) mf.report() + return mf # for -i debugging if __name__ == '__main__': try: ! mf = test() except KeyboardInterrupt: print "\n[interrupt]"