GeezerVison is showing github.com — gh-60115: Support frozen modules for linecache.getline() (#131638) · python/cpython@6bd9689
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: Doc/library/linecache.rst +8Lines changed: 8 additions & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -30,6 +30,10 @@ The :mod:`linecache` module defines the following functions: | 30 | 30 |
| 31 | 31 | .. index:: triple: module; search; path | 32 | 32 |
| | 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 | + | 33 | 37 | If a file named *filename* is not found, the function first checks | 34 | 38 | for a :pep:`302` ``__loader__`` in *module_globals*. | 35 | 39 | 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: | 38 | 42 | Finally, if *filename* is a relative filename, | 39 | 43 | it is looked up relative to the entries in the module search path, ``sys.path``. | 40 | 44 |
| | 45 | + .. versionchanged:: 3.14 | | 46 | + | | 47 | + Support *filename* of frozen modules. | | 48 | + | 41 | 49 |
| 42 | 50 | .. function:: clearcache() | 43 | 51 |
| |
Collapse file Copy file name to clipboardExpand all lines: Doc/whatsnew/3.14.rst +7Lines changed: 7 additions & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -706,6 +706,13 @@ json | 706 | 706 | (Contributed by Trey Hunner in :gh:`122873`.) | 707 | 707 |
| 708 | 708 |
| | 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 | + | 709 | 716 | mimetypes | 710 | 717 | --------- | 711 | 718 |
| |
Collapse file Copy file name to clipboardExpand all lines: Lib/linecache.py +19-2Lines changed: 19 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -63,6 +63,16 @@ def _getlines_from_code(code): | 63 | 63 | return [] | 64 | 64 |
| 65 | 65 |
| | 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 | + | 66 | 76 | def checkcache(filename=None): | 67 | 77 | """Discard cache entries that are out of date. | 68 | 78 | (This is not checked upon each call!)""" | @@ -118,10 +128,17 @@ def updatecache(filename, module_globals=None): | 118 | 128 | if filename in cache: | 119 | 129 | if len(cache[filename]) != 1: | 120 | 130 | cache.pop(filename, None) | 121 | | - if not filename or (filename.startswith('<') and filename.endswith('>')): | | 131 | + if _source_unavailable(filename): | 122 | 132 | return [] | 123 | 133 |
| 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 | 125 | 142 | try: | 126 | 143 | stat = os.stat(fullname) | 127 | 144 | except OSError: | |
Collapse file Copy file name to clipboardExpand all lines: Lib/test/test_linecache.py +13Lines changed: 13 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -281,6 +281,19 @@ def test_loader(self): | 281 | 281 | self.assertEqual(linecache.getlines(filename, module_globals), | 282 | 282 | ['source for x.y.z\n']) | 283 | 283 |
| | 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 | + | 284 | 297 | def test_invalid_names(self): | 285 | 298 | for name, desc in [ | 286 | 299 | ('\x00', 'NUL bytes filename'), | |
Collapse file Copy file name to clipboard +1Lines changed: 1 addition & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +Support frozen modules for :func:`linecache.getline`. |
|