Skip to content

Commit 6bd9689

Browse files
gh-60115: Support frozen modules for linecache.getline() (#131638)
1 parent 06822bf commit 6bd9689

File tree

5 files changed

+48
-2
lines changed

5 files changed

+48
-2
lines changed

Doc/library/linecache.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ The :mod:`linecache` module defines the following functions:
3030

3131
.. index:: triple: module; search; path
3232

33+
If *filename* indicates a frozen module (starting with ``'<frozen '``), the function
34+
will attepmt to get the real file name from ``module_globals['__file__']`` if
35+
*module_globals* is not ``None``.
36+
3337
If a file named *filename* is not found, the function first checks
3438
for a :pep:`302` ``__loader__`` in *module_globals*.
3539
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:
3842
Finally, if *filename* is a relative filename,
3943
it is looked up relative to the entries in the module search path, ``sys.path``.
4044

45+
.. versionchanged:: 3.14
46+
47+
Support *filename* of frozen modules.
48+
4149

4250
.. function:: clearcache()
4351

Doc/whatsnew/3.14.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,13 @@ json
706706
(Contributed by Trey Hunner in :gh:`122873`.)
707707

708708

709+
linecache
710+
---------
711+
712+
* :func:`linecache.getline` can retrieve source code for frozen modules.
713+
(Contributed by Tian Gao in :gh:`131638`.)
714+
715+
709716
mimetypes
710717
---------
711718

Lib/linecache.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,16 @@ def _getlines_from_code(code):
6363
return []
6464

6565

66+
def _source_unavailable(filename):
67+
"""Return True if the source code is unavailable for such file name."""
68+
return (
69+
not filename
70+
or (filename.startswith('<')
71+
and filename.endswith('>')
72+
and not filename.startswith('<frozen '))
73+
)
74+
75+
6676
def checkcache(filename=None):
6777
"""Discard cache entries that are out of date.
6878
(This is not checked upon each call!)"""
@@ -118,10 +128,17 @@ def updatecache(filename, module_globals=None):
118128
if filename in cache:
119129
if len(cache[filename]) != 1:
120130
cache.pop(filename, None)
121-
if not filename or (filename.startswith('<') and filename.endswith('>')):
131+
if _source_unavailable(filename):
122132
return []
123133

124-
fullname = filename
134+
if filename.startswith('<frozen ') and module_globals is not None:
135+
# This is a frozen module, so we need to use the filename
136+
# from the module globals.
137+
fullname = module_globals.get('__file__')
138+
if fullname is None:
139+
return []
140+
else:
141+
fullname = filename
125142
try:
126143
stat = os.stat(fullname)
127144
except OSError:

Lib/test/test_linecache.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,19 @@ def test_loader(self):
281281
self.assertEqual(linecache.getlines(filename, module_globals),
282282
['source for x.y.z\n'])
283283

284+
def test_frozen(self):
285+
filename = '<frozen fakemodule>'
286+
module_globals = {'__file__': FILENAME}
287+
empty = linecache.getlines(filename)
288+
self.assertEqual(empty, [])
289+
lines = linecache.getlines(filename, module_globals)
290+
self.assertGreater(len(lines), 0)
291+
lines_cached = linecache.getlines(filename)
292+
self.assertEqual(lines, lines_cached)
293+
linecache.clearcache()
294+
empty = linecache.getlines(filename)
295+
self.assertEqual(empty, [])
296+
284297
def test_invalid_names(self):
285298
for name, desc in [
286299
('\x00', 'NUL bytes filename'),
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Support frozen modules for :func:`linecache.getline`.

0 commit comments

Comments
 (0)