gh-60115: Support frozen modules for linecache.getline() (#131638)

This commit is contained in:
Tian Gao 2025-04-02 19:50:01 -04:00 committed by GitHub
parent 06822bfbf6
commit 6bd9689426
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 48 additions and 2 deletions

View file

@ -30,6 +30,10 @@ The :mod:`linecache` module defines the following functions:
.. index:: triple: module; search; path
If *filename* indicates a frozen module (starting with ``'<frozen '``), the function
will attepmt to get the real file name from ``module_globals['__file__']`` if
*module_globals* is not ``None``.
If a file named *filename* is not found, the function first checks
for a :pep:`302` ``__loader__`` in *module_globals*.
If there is such a loader and it defines a ``get_source`` method,
@ -38,6 +42,10 @@ The :mod:`linecache` module defines the following functions:
Finally, if *filename* is a relative filename,
it is looked up relative to the entries in the module search path, ``sys.path``.
.. versionchanged:: 3.14
Support *filename* of frozen modules.
.. function:: clearcache()

View file

@ -706,6 +706,13 @@ json
(Contributed by Trey Hunner in :gh:`122873`.)
linecache
---------
* :func:`linecache.getline` can retrieve source code for frozen modules.
(Contributed by Tian Gao in :gh:`131638`.)
mimetypes
---------

View file

@ -63,6 +63,16 @@ def _getlines_from_code(code):
return []
def _source_unavailable(filename):
"""Return True if the source code is unavailable for such file name."""
return (
not filename
or (filename.startswith('<')
and filename.endswith('>')
and not filename.startswith('<frozen '))
)
def checkcache(filename=None):
"""Discard cache entries that are out of date.
(This is not checked upon each call!)"""
@ -118,10 +128,17 @@ def updatecache(filename, module_globals=None):
if filename in cache:
if len(cache[filename]) != 1:
cache.pop(filename, None)
if not filename or (filename.startswith('<') and filename.endswith('>')):
if _source_unavailable(filename):
return []
fullname = filename
if filename.startswith('<frozen ') and module_globals is not None:
# This is a frozen module, so we need to use the filename
# from the module globals.
fullname = module_globals.get('__file__')
if fullname is None:
return []
else:
fullname = filename
try:
stat = os.stat(fullname)
except OSError:

View file

@ -281,6 +281,19 @@ class LineCacheTests(unittest.TestCase):
self.assertEqual(linecache.getlines(filename, module_globals),
['source for x.y.z\n'])
def test_frozen(self):
filename = '<frozen fakemodule>'
module_globals = {'__file__': FILENAME}
empty = linecache.getlines(filename)
self.assertEqual(empty, [])
lines = linecache.getlines(filename, module_globals)
self.assertGreater(len(lines), 0)
lines_cached = linecache.getlines(filename)
self.assertEqual(lines, lines_cached)
linecache.clearcache()
empty = linecache.getlines(filename)
self.assertEqual(empty, [])
def test_invalid_names(self):
for name, desc in [
('\x00', 'NUL bytes filename'),

View file

@ -0,0 +1 @@
Support frozen modules for :func:`linecache.getline`.