Nick Coghlan avatar Nick Coghlan committed 0298cc6

Backport inspect.py fix from rev 51803

Comments (0)

Files changed (3)

     return os.path.normcase(os.path.abspath(_filename))
 
 modulesbyfile = {}
+_filesbymodname = {}
 
 def getmodule(object, _filename=None):
     """Return the module an object was defined in, or None if not found."""
         return object
     if hasattr(object, '__module__'):
         return sys.modules.get(object.__module__)
+    # Try the filename to modulename cache
+    if _filename is not None and _filename in modulesbyfile:
+        return sys.modules.get(modulesbyfile[_filename])
+    # Try the cache again with the absolute file name
     try:
         file = getabsfile(object, _filename)
     except TypeError:
         return None
     if file in modulesbyfile:
         return sys.modules.get(modulesbyfile[file])
-    for module in sys.modules.values():
+    # Update the filename to module name cache and check yet again
+    # Copy sys.modules in order to cope with changes while iterating
+    for modname, module in sys.modules.items():
         if ismodule(module) and hasattr(module, '__file__'):
+            f = module.__file__
+            if f == _filesbymodname.get(modname, None):
+                # Have already mapped this module, so skip it
+                continue
+            _filesbymodname[modname] = f
             f = getabsfile(module)
+            # Always map to the name the module knows itself by
             modulesbyfile[f] = modulesbyfile[
                 os.path.realpath(f)] = module.__name__
     if file in modulesbyfile:
         return sys.modules.get(modulesbyfile[file])
+    # Check the main module
     main = sys.modules['__main__']
     if not hasattr(object, '__name__'):
         return None
         mainobject = getattr(main, object.__name__)
         if mainobject is object:
             return main
+    # Check builtins
     builtin = sys.modules['__builtin__']
     if hasattr(builtin, object.__name__):
         builtinobject = getattr(builtin, object.__name__)
     in the file and the line number indexes a line in that list.  An IOError
     is raised if the source code cannot be retrieved."""
     file = getsourcefile(object) or getfile(object)
-    module = getmodule(object)
+    module = getmodule(object, file)
     if module:
         lines = linecache.getlines(file, module.__dict__)
     else:

Lib/test/test_inspect.py

         self.assertEqual(inspect.getcomments(mod.StupidGit), '# line 20\n')
 
     def test_getmodule(self):
+        # Check actual module
+        self.assertEqual(inspect.getmodule(mod), mod)
+        # Check class (uses __module__ attribute)
         self.assertEqual(inspect.getmodule(mod.StupidGit), mod)
+        # Check a method (no __module__ attribute, falls back to filename)
+        self.assertEqual(inspect.getmodule(mod.StupidGit.abuse), mod)
+        # Do it again (check the caching isn't broken)
+        self.assertEqual(inspect.getmodule(mod.StupidGit.abuse), mod)
+        # Check a builtin
+        self.assertEqual(inspect.getmodule(str), sys.modules["__builtin__"])
+        # Check filename override
+        self.assertEqual(inspect.getmodule(None, modfile), mod)
 
     def test_getsource(self):
         self.assertSourceEqual(git.abuse, 29, 39)
 Library
 -------
 
+- Patch #1553314: Fix the inspect.py slowdown that was hurting IPython & SAGE
+  by adding smarter caching in inspect.getmodule()
+
 - Fix missing import of the types module in logging.config.
 
 - Patch #1550886: Fix decimal module context management implementation
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.