From 52ef4430a9b3e212fe9200675cddede77b90785b Mon Sep 17 00:00:00 2001 From: kernc Date: Mon, 18 Mar 2024 16:13:02 +0100 Subject: [PATCH] gh-71765: Fix inspect.getsource() on empty file (GH-20809) * bpo-27578: Fix inspect.getsource() on empty file For modules from empty files, `inspect.getsource()` now returns an empty string, and `inspect.getsourcelines()` returns a list of one empty string, fixing the expected invariant. As indicated by `exec('')`, empty strings are valid Python source code. Co-authored-by: Oleg Iarygin --- Lib/linecache.py | 4 +++- Lib/test/test_inspect/test_inspect.py | 14 +++++++++++++- Lib/test/test_linecache.py | 4 ++++ .../2020-06-11-16-20-33.bpo-27578.CIA-fu.rst | 3 +++ 4 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2020-06-11-16-20-33.bpo-27578.CIA-fu.rst diff --git a/Lib/linecache.py b/Lib/linecache.py index 04c8f45a6c6..b97999fc1dc 100644 --- a/Lib/linecache.py +++ b/Lib/linecache.py @@ -137,7 +137,9 @@ def updatecache(filename, module_globals=None): lines = fp.readlines() except (OSError, UnicodeDecodeError, SyntaxError): return [] - if lines and not lines[-1].endswith('\n'): + if not lines: + lines = ['\n'] + elif not lines[-1].endswith('\n'): lines[-1] += '\n' size, mtime = stat.st_size, stat.st_mtime cache[filename] = size, mtime, lines, fullname diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index c3a9dc998e3..21d9f96c8c4 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -35,7 +35,7 @@ except ImportError: from test.support import cpython_only from test.support import MISSING_C_DOCSTRINGS, ALWAYS_EQ from test.support.import_helper import DirsOnSysPath, ready_to_import -from test.support.os_helper import TESTFN +from test.support.os_helper import TESTFN, temp_cwd from test.support.script_helper import assert_python_ok, assert_python_failure, kill_python from test.support import has_subprocess_support, SuppressCrashReport from test import support @@ -730,6 +730,18 @@ class TestRetrievingSourceCode(GetSourceBase): finally: del linecache.cache[co.co_filename] + def test_getsource_empty_file(self): + with temp_cwd() as cwd: + with open('empty_file.py', 'w'): + pass + sys.path.insert(0, cwd) + try: + import empty_file + self.assertEqual(inspect.getsource(empty_file), '\n') + self.assertEqual(inspect.getsourcelines(empty_file), (['\n'], 0)) + finally: + sys.path.remove(cwd) + def test_getfile(self): self.assertEqual(inspect.getfile(mod.StupidGit), mod.__file__) diff --git a/Lib/test/test_linecache.py b/Lib/test/test_linecache.py index e42df3d9496..8ac521d72ef 100644 --- a/Lib/test/test_linecache.py +++ b/Lib/test/test_linecache.py @@ -83,6 +83,10 @@ class GetLineTestsBadData(TempFile): class EmptyFile(GetLineTestsGoodData, unittest.TestCase): file_list = [] + def test_getlines(self): + lines = linecache.getlines(self.file_name) + self.assertEqual(lines, ['\n']) + class SingleEmptyLine(GetLineTestsGoodData, unittest.TestCase): file_list = ['\n'] diff --git a/Misc/NEWS.d/next/Library/2020-06-11-16-20-33.bpo-27578.CIA-fu.rst b/Misc/NEWS.d/next/Library/2020-06-11-16-20-33.bpo-27578.CIA-fu.rst new file mode 100644 index 00000000000..df58a7ede45 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-06-11-16-20-33.bpo-27578.CIA-fu.rst @@ -0,0 +1,3 @@ +:func:`inspect.getsource` (and related functions) work with +empty module files, returning ``'\n'`` (or reasonable equivalent) +instead of raising ``OSError``. Patch by Kernc.