Skip to content

Commit d202f7f

Browse files
committed
gh-141276: Return before reading source in _get_module_code
Move the compile_source=False early return above the archive read, so get_filename() no longer decompresses source it immediately discards. Also update the NEWS entry with itamaro's suggested wording and add tests covering the deferred-SyntaxError behavior: find_spec()/ get_filename() succeed and get_filename() emits no warnings for a module with a syntax error, a valid .pyc still wins over a bad sibling .py, and a package's __init__.py error doesn't fall through to a same-named sibling module.
1 parent 05f92d2 commit d202f7f

3 files changed

Lines changed: 63 additions & 4 deletions

File tree

‎Lib/test/test_zipimport.py‎

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,64 @@ def test_syntax_warning(self):
247247
self.assertEqual(len(caught), 1)
248248
self.assertIsInstance(caught[0].message, SyntaxWarning)
249249

250+
def test_syntax_error_defers_to_exec(self):
251+
files = {TESTMOD + ".py": "x =\n"}
252+
self.makeZip(files)
253+
zi = zipimport.zipimporter(TEMP_ZIP)
254+
expected_path = os.path.join(TEMP_ZIP, TESTMOD + ".py")
255+
256+
spec = zi.find_spec(TESTMOD)
257+
self.assertIsNotNone(spec)
258+
self.assertEqual(spec.origin, expected_path)
259+
self.assertEqual(zi.get_filename(TESTMOD), expected_path)
260+
261+
mod = importlib.util.module_from_spec(spec)
262+
with self.assertRaises(SyntaxError):
263+
spec.loader.exec_module(mod)
264+
265+
def test_get_filename_no_warnings(self):
266+
files = {TESTMOD + ".py": "x = 1 is 1\n"}
267+
self.makeZip(files)
268+
zi = zipimport.zipimporter(TEMP_ZIP)
269+
270+
with warnings.catch_warnings(record=True) as caught:
271+
warnings.simplefilter("always", SyntaxWarning)
272+
zi.get_filename(TESTMOD)
273+
274+
self.assertEqual(caught, [])
275+
276+
def test_syntax_error_in_source_with_valid_pyc(self):
277+
bad_src = "x =\n"
278+
files = {TESTMOD + ".py": bad_src,
279+
TESTMOD + pyc_ext: make_pyc(test_co, NOW, len(bad_src))}
280+
self.makeZip(files)
281+
zi = zipimport.zipimporter(TEMP_ZIP)
282+
expected_path = os.path.join(TEMP_ZIP, TESTMOD + pyc_ext)
283+
284+
spec = zi.find_spec(TESTMOD)
285+
self.assertIsNotNone(spec)
286+
self.assertEqual(spec.origin, expected_path)
287+
288+
mod = importlib.util.module_from_spec(spec)
289+
spec.loader.exec_module(mod)
290+
self.assertEqual(mod.get_file(), expected_path)
291+
292+
def test_syntax_error_in_package_init_no_fallthrough(self):
293+
packdir = TESTPACK + os.sep
294+
files = {packdir + "__init__.py": "x =\n",
295+
TESTPACK + ".py": test_src}
296+
self.makeZip(files)
297+
zi = zipimport.zipimporter(TEMP_ZIP)
298+
expected_path = os.path.join(TEMP_ZIP, TESTPACK, "__init__.py")
299+
300+
spec = zi.find_spec(TESTPACK)
301+
self.assertIsNotNone(spec)
302+
self.assertEqual(spec.origin, expected_path)
303+
304+
mod = importlib.util.module_from_spec(spec)
305+
with self.assertRaises(SyntaxError):
306+
spec.loader.exec_module(mod)
307+
250308
def testPyc(self):
251309
files = {TESTMOD + pyc_ext: test_pyc}
252310
self.doTest(pyc_ext, files, TESTMOD)

‎Lib/zipimport.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,8 @@ def _get_module_code(self, fullname, *, compile_source=True):
808808
pass
809809
else:
810810
modpath = toc_entry[0]
811+
if not isbytecode and not compile_source:
812+
return None, ispackage, modpath
811813
data = _get_data(self.archive, toc_entry)
812814
code = None
813815
if isbytecode:
@@ -816,8 +818,6 @@ def _get_module_code(self, fullname, *, compile_source=True):
816818
except ImportError as exc:
817819
import_error = exc
818820
else:
819-
if not compile_source:
820-
return None, ispackage, modpath
821821
code = _compile_source(modpath, data, fullname)
822822
if code is None:
823823
# bad magic number or non-matching mtime
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
Fix duplicate :exc:`SyntaxWarning` messages when importing source modules
2-
from ZIP archives.
1+
``zipimporter.get_filename()`` and ``find_spec()`` no longer compile source
2+
modules; a :exc:`SyntaxError` in a zipped source module is now raised when the
3+
module is executed, matching the behavior of filesystem-based loaders.

0 commit comments

Comments
 (0)