diff --git a/Lib/inspect.py b/Lib/inspect.py index bf7f006bad2..311fe7ec697 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -355,40 +355,37 @@ def getsourcefile(object): return None if os.path.exists(filename): return filename - # Ugly but necessary - '' and '' mean that getmodule() - # would infinitely recurse, because they're not real files nor loadable - # Note that this means that writing a PEP 302 loader that uses '<' - # at the start of a filename is now not a good idea. :( - if filename[:1]!='<' and hasattr(getmodule(object), '__loader__'): + # only return a non-existent filename if the module has a PEP 302 loader + if hasattr(getmodule(object, filename), '__loader__'): return filename -def getabsfile(object): +def getabsfile(object, _filename=None): """Return an absolute path to the source or compiled file for an object. The idea is for each object to have a unique origin, so this routine normalizes the result as much as possible.""" return os.path.normcase( - os.path.abspath(getsourcefile(object) or getfile(object))) + os.path.abspath(_filename or getsourcefile(object) or getfile(object))) modulesbyfile = {} -def getmodule(object): +def getmodule(object, _filename=None): """Return the module an object was defined in, or None if not found.""" if ismodule(object): return object if hasattr(object, '__module__'): return sys.modules.get(object.__module__) try: - file = getabsfile(object) + file = getabsfile(object, _filename) except TypeError: return None if file in modulesbyfile: return sys.modules.get(modulesbyfile[file]) for module in sys.modules.values(): if ismodule(module) and hasattr(module, '__file__'): - modulesbyfile[ - os.path.realpath( - getabsfile(module))] = module.__name__ + f = getabsfile(module) + modulesbyfile[f] = modulesbyfile[ + os.path.realpath(f)] = module.__name__ if file in modulesbyfile: return sys.modules.get(modulesbyfile[file]) main = sys.modules['__main__'] diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index 62c40eba4d7..d100d22ab56 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -178,6 +178,16 @@ class TestRetrievingSourceCode(GetSourceBase): def test_getfile(self): self.assertEqual(inspect.getfile(mod.StupidGit), mod.__file__) + def test_getmodule_recursion(self): + from new import module + name = '__inspect_dummy' + m = sys.modules[name] = module(name) + m.__file__ = "" # hopefully not a real filename... + m.__loader__ = "dummy" # pretend the filename is understood by a loader + exec "def x(): pass" in m.__dict__ + self.assertEqual(inspect.getsourcefile(m.x.func_code), '') + del sys.modules[name] + class TestDecorators(GetSourceBase): fodderFile = mod2