https://github.com/python/cpython/commit/142711a31dc1469570aa5b5e76450314d6777bb2
commit: 142711a31dc1469570aa5b5e76450314d6777bb2
branch: 3.11
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2024-02-20T18:18:16Z
summary:

[3.11] gh-86291: linecache: get module name from __spec__ if available 
(GH-22908) (GH-115732)

This allows getting source code for the __main__ module when a custom
loader is used.
(cherry picked from commit e976baba99a5c243ff3a3b5ef2fd14608a398338)

Co-authored-by: Eugene Toder <[email protected]>

files:
A Misc/NEWS.d/next/Library/2020-12-15-22-30-49.bpo-42125.UGyseY.rst
M Lib/linecache.py
M Lib/test/test_linecache.py

diff --git a/Lib/linecache.py b/Lib/linecache.py
index 97644a8e3794e1..ed4c9700dcbd85 100644
--- a/Lib/linecache.py
+++ b/Lib/linecache.py
@@ -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:
diff --git a/Lib/test/test_linecache.py b/Lib/test/test_linecache.py
index 72dd40136cfdb2..e42df3d9496bc8 100644
--- a/Lib/test/test_linecache.py
+++ b/Lib/test/test_linecache.py
@@ -5,6 +5,7 @@
 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 @@ def raise_memoryerror(*args, **kwargs):
         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
index 00000000000000..49d4462e257702
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-12-15-22-30-49.bpo-42125.UGyseY.rst
@@ -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.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to