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

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

Co-authored-by: Eugene Toder <eltoder@users.noreply.github.com>
This commit is contained in:
Miss Islington (bot) 2024-02-20 19:14:24 +01:00 committed by GitHub
parent 5ea86f496a
commit f1c1afd45b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 45 additions and 7 deletions

View file

@ -166,13 +166,11 @@ def lazycache(filename, module_globals):
return False return False
# Try for a __loader__, if available # Try for a __loader__, if available
if module_globals and '__name__' in module_globals: if module_globals and '__name__' in module_globals:
name = module_globals['__name__'] spec = module_globals.get('__spec__')
if (loader := module_globals.get('__loader__')) is None: name = getattr(spec, 'name', None) or module_globals['__name__']
if spec := module_globals.get('__spec__'): loader = getattr(spec, 'loader', None)
try: if loader is None:
loader = spec.loader loader = module_globals.get('__loader__')
except AttributeError:
pass
get_source = getattr(loader, 'get_source', None) get_source = getattr(loader, 'get_source', None)
if name and get_source: if name and get_source:

View file

@ -5,6 +5,7 @@ import unittest
import os.path import os.path
import tempfile import tempfile
import tokenize import tokenize
from importlib.machinery import ModuleSpec
from test import support from test import support
from test.support import os_helper 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' 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): class LineCacheTests(unittest.TestCase):
def test_getline(self): def test_getline(self):
@ -238,6 +249,33 @@ class LineCacheTests(unittest.TestCase):
self.assertEqual(lines3, []) self.assertEqual(lines3, [])
self.assertEqual(linecache.getlines(FILENAME), lines) 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): class LineCacheInvalidationTests(unittest.TestCase):
def setUp(self): def setUp(self):

View file

@ -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.