]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-86291: linecache: get module name from __spec__ if available (GH-22908)
authorEugene Toder <eltoder@users.noreply.github.com>
Tue, 20 Feb 2024 16:47:41 +0000 (11:47 -0500)
committerGitHub <noreply@github.com>
Tue, 20 Feb 2024 16:47:41 +0000 (16:47 +0000)
This allows getting source code for the __main__ module when a custom
loader is used.

Lib/linecache.py
Lib/test/test_linecache.py
Misc/NEWS.d/next/Library/2020-12-15-22-30-49.bpo-42125.UGyseY.rst [new file with mode: 0644]

index 329a14053458b7bb2803bd7634bf5602bfda07e2..04c8f45a6c60ca27cf8eb6be1c0ffb25e25a575e 100644 (file)
@@ -166,13 +166,11 @@ def lazycache(filename, module_globals):
         return False
     # Try for a __loader__, if available
     if module_globals and '__name__' in module_globals:
-        name = module_globals['__name__']
-        if (loader := module_globals.get('__loader__')) is None:
-            if spec := module_globals.get('__spec__'):
-                try:
-                    loader = spec.loader
-                except AttributeError:
-                    pass
+        spec = module_globals.get('__spec__')
+        name = getattr(spec, 'name', None) or module_globals['__name__']
+        loader = getattr(spec, 'loader', None)
+        if loader is None:
+            loader = module_globals.get('__loader__')
         get_source = getattr(loader, 'get_source', None)
 
         if name and get_source:
index 72dd40136cfdb24b7bec3ee8798c259b4597e397..e42df3d9496bc82e482eba0e101168e528cbb3c2 100644 (file)
@@ -5,6 +5,7 @@ import unittest
 import os.path
 import tempfile
 import tokenize
+from importlib.machinery import ModuleSpec
 from test import support
 from test.support import os_helper
 
@@ -97,6 +98,16 @@ class BadUnicode_WithDeclaration(GetLineTestsBadData, unittest.TestCase):
     file_byte_string = b'# coding=utf-8\n\x80abc'
 
 
+class FakeLoader:
+    def get_source(self, fullname):
+        return f'source for {fullname}'
+
+
+class NoSourceLoader:
+    def get_source(self, fullname):
+        return None
+
+
 class LineCacheTests(unittest.TestCase):
 
     def test_getline(self):
@@ -238,6 +249,33 @@ class LineCacheTests(unittest.TestCase):
         self.assertEqual(lines3, [])
         self.assertEqual(linecache.getlines(FILENAME), lines)
 
+    def test_loader(self):
+        filename = 'scheme://path'
+
+        for loader in (None, object(), NoSourceLoader()):
+            linecache.clearcache()
+            module_globals = {'__name__': 'a.b.c', '__loader__': loader}
+            self.assertEqual(linecache.getlines(filename, module_globals), [])
+
+        linecache.clearcache()
+        module_globals = {'__name__': 'a.b.c', '__loader__': FakeLoader()}
+        self.assertEqual(linecache.getlines(filename, module_globals),
+                         ['source for a.b.c\n'])
+
+        for spec in (None, object(), ModuleSpec('', FakeLoader())):
+            linecache.clearcache()
+            module_globals = {'__name__': 'a.b.c', '__loader__': FakeLoader(),
+                              '__spec__': spec}
+            self.assertEqual(linecache.getlines(filename, module_globals),
+                             ['source for a.b.c\n'])
+
+        linecache.clearcache()
+        spec = ModuleSpec('x.y.z', FakeLoader())
+        module_globals = {'__name__': 'a.b.c', '__loader__': spec.loader,
+                          '__spec__': spec}
+        self.assertEqual(linecache.getlines(filename, module_globals),
+                         ['source for x.y.z\n'])
+
 
 class LineCacheInvalidationTests(unittest.TestCase):
     def setUp(self):
diff --git a/Misc/NEWS.d/next/Library/2020-12-15-22-30-49.bpo-42125.UGyseY.rst b/Misc/NEWS.d/next/Library/2020-12-15-22-30-49.bpo-42125.UGyseY.rst
new file mode 100644 (file)
index 0000000..49d4462
--- /dev/null
@@ -0,0 +1,2 @@
+linecache: get module name from ``__spec__`` if available. This allows getting
+source code for the ``__main__`` module when a custom loader is used.