https://github.com/python/cpython/commit/e976baba99a5c243ff3a3b5ef2fd14608a398338
commit: e976baba99a5c243ff3a3b5ef2fd14608a398338
branch: main
author: Eugene Toder <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2024-02-20T16:47:41Z
summary:
gh-86291: linecache: get module name from __spec__ if available (GH-22908)
This allows getting source code for the __main__ module when a custom
loader is used.
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 329a14053458b7..04c8f45a6c60ca 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]