Fix SF#1516184 and add a test to prevent regression.

This commit is contained in:
Phillip J. Eby 2006-07-10 19:03:29 +00:00
parent b9cb84fe96
commit 5d86bdb3ae
2 changed files with 19 additions and 12 deletions

View File

@ -355,40 +355,37 @@ def getsourcefile(object):
return None return None
if os.path.exists(filename): if os.path.exists(filename):
return filename return filename
# Ugly but necessary - '<stdin>' and '<string>' mean that getmodule() # only return a non-existent filename if the module has a PEP 302 loader
# would infinitely recurse, because they're not real files nor loadable if hasattr(getmodule(object, filename), '__loader__'):
# 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__'):
return filename return filename
def getabsfile(object): def getabsfile(object, _filename=None):
"""Return an absolute path to the source or compiled file for an object. """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 The idea is for each object to have a unique origin, so this routine
normalizes the result as much as possible.""" normalizes the result as much as possible."""
return os.path.normcase( return os.path.normcase(
os.path.abspath(getsourcefile(object) or getfile(object))) os.path.abspath(_filename or getsourcefile(object) or getfile(object)))
modulesbyfile = {} modulesbyfile = {}
def getmodule(object): def getmodule(object, _filename=None):
"""Return the module an object was defined in, or None if not found.""" """Return the module an object was defined in, or None if not found."""
if ismodule(object): if ismodule(object):
return object return object
if hasattr(object, '__module__'): if hasattr(object, '__module__'):
return sys.modules.get(object.__module__) return sys.modules.get(object.__module__)
try: try:
file = getabsfile(object) file = getabsfile(object, _filename)
except TypeError: except TypeError:
return None return None
if file in modulesbyfile: if file in modulesbyfile:
return sys.modules.get(modulesbyfile[file]) return sys.modules.get(modulesbyfile[file])
for module in sys.modules.values(): for module in sys.modules.values():
if ismodule(module) and hasattr(module, '__file__'): if ismodule(module) and hasattr(module, '__file__'):
modulesbyfile[ f = getabsfile(module)
os.path.realpath( modulesbyfile[f] = modulesbyfile[
getabsfile(module))] = module.__name__ os.path.realpath(f)] = module.__name__
if file in modulesbyfile: if file in modulesbyfile:
return sys.modules.get(modulesbyfile[file]) return sys.modules.get(modulesbyfile[file])
main = sys.modules['__main__'] main = sys.modules['__main__']

View File

@ -178,6 +178,16 @@ class TestRetrievingSourceCode(GetSourceBase):
def test_getfile(self): def test_getfile(self):
self.assertEqual(inspect.getfile(mod.StupidGit), mod.__file__) 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__ = "<string>" # 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), '<string>')
del sys.modules[name]
class TestDecorators(GetSourceBase): class TestDecorators(GetSourceBase):
fodderFile = mod2 fodderFile = mod2