Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Doc/library/zipimport.rst
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ zipimporter Objects

Return the value ``__file__`` would be set to if the specified module
was imported. Raise :exc:`ZipImportError` if the module couldn't be
imported.
found.

.. versionadded:: 3.1

Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_cmd_line_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -846,7 +846,7 @@ def test_zipfile_run_filter_syntax_warnings_by_module(self):
'-Werror:::test_pkg.__main__',
os.path.join(zip_name, 'test_pkg')
)
self.assertEqual(err.count(b': SyntaxWarning: '), 12)
self.assertEqual(err.count(b': SyntaxWarning: '), 6)


def tearDownModule():
Expand Down
76 changes: 76 additions & 0 deletions Lib/test/test_zipimport.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import time
import unittest
import unittest.mock
import warnings

from test import support
from test.support import import_helper
Expand Down Expand Up @@ -229,6 +230,81 @@ def testPy(self):
files = {TESTMOD + ".py": test_src}
self.doTest(".py", files, TESTMOD)

def test_syntax_warning(self):
files = {TESTMOD + ".py": "x = 1 is 1\n"}
self.makeZip(files)
zi = zipimport.zipimporter(TEMP_ZIP)

with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always", SyntaxWarning)
spec = zi.find_spec(TESTMOD)
self.assertIsNotNone(spec)
self.assertEqual(caught, [])

mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)

self.assertEqual(len(caught), 1)
self.assertIsInstance(caught[0].message, SyntaxWarning)

def test_syntax_error_defers_to_exec(self):
files = {TESTMOD + ".py": "x =\n"}
self.makeZip(files)
zi = zipimport.zipimporter(TEMP_ZIP)
expected_path = os.path.join(TEMP_ZIP, TESTMOD + ".py")

spec = zi.find_spec(TESTMOD)
self.assertIsNotNone(spec)
self.assertEqual(spec.origin, expected_path)
self.assertEqual(zi.get_filename(TESTMOD), expected_path)

mod = importlib.util.module_from_spec(spec)
with self.assertRaises(SyntaxError):
spec.loader.exec_module(mod)

def test_get_filename_no_warnings(self):
files = {TESTMOD + ".py": "x = 1 is 1\n"}
self.makeZip(files)
zi = zipimport.zipimporter(TEMP_ZIP)

with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always", SyntaxWarning)
zi.get_filename(TESTMOD)

self.assertEqual(caught, [])

def test_syntax_error_in_source_with_valid_pyc(self):
bad_src = "x =\n"
files = {TESTMOD + ".py": bad_src,
TESTMOD + pyc_ext: make_pyc(test_co, NOW, len(bad_src))}
self.makeZip(files)
zi = zipimport.zipimporter(TEMP_ZIP)
expected_path = os.path.join(TEMP_ZIP, TESTMOD + pyc_ext)

spec = zi.find_spec(TESTMOD)
self.assertIsNotNone(spec)
self.assertEqual(spec.origin, expected_path)

mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
self.assertEqual(mod.get_file(), expected_path)

def test_syntax_error_in_package_init_no_fallthrough(self):
packdir = TESTPACK + os.sep
files = {packdir + "__init__.py": "x =\n",
TESTPACK + ".py": test_src}
self.makeZip(files)
zi = zipimport.zipimporter(TEMP_ZIP)
expected_path = os.path.join(TEMP_ZIP, TESTPACK, "__init__.py")

spec = zi.find_spec(TESTPACK)
self.assertIsNotNone(spec)
self.assertEqual(spec.origin, expected_path)

mod = importlib.util.module_from_spec(spec)
with self.assertRaises(SyntaxError):
spec.loader.exec_module(mod)

def testPyc(self):
files = {TESTMOD + pyc_ext: test_pyc}
self.doTest(pyc_ext, files, TESTMOD)
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_zipimport_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ def test_import_filter_syntax_warnings_by_module(self):
warnings.filterwarnings('error', module='test_mod')
import test_pkg.test_mod
self.assertEqual(sorted(wm.lineno for wm in wlog),
sorted([4, 7, 10, 13, 14, 21]*2))
[4, 7, 10, 13, 14, 21])
filename = test_pkg.test_mod.__file__
for wm in wlog:
self.assertEqual(wm.filename, filename)
Expand Down
14 changes: 8 additions & 6 deletions Lib/zipimport.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,12 @@ def get_filename(self, fullname):
"""get_filename(fullname) -> filename string.

Return the filename for the specified module or raise ZipImportError
if it couldn't be imported.
if it couldn't be found.
"""
# Deciding the filename requires working out where the code
# would come from if the module was actually loaded
code, ispackage, modpath = _get_module_code(self, fullname)
_, _, modpath = _get_module_code(
self, fullname, compile_source=False)
return modpath


Expand Down Expand Up @@ -793,9 +794,9 @@ def _get_pyc_source(self, path):
return _get_data(self.archive, toc_entry)


# Get the code object associated with the module specified by
# 'fullname'.
def _get_module_code(self, fullname):
# Get the code object associated with the module specified by 'fullname'.
# If compile_source is false, return None for source code without reading or compiling it.
def _get_module_code(self, fullname, *, compile_source=True):
path = _get_module_path(self, fullname)
import_error = None
for suffix, isbytecode, ispackage in _zip_searchorder:
Expand All @@ -807,6 +808,8 @@ def _get_module_code(self, fullname):
pass
else:
modpath = toc_entry[0]
if not isbytecode and not compile_source:
return None, ispackage, modpath
data = _get_data(self.archive, toc_entry)
code = None
if isbytecode:
Expand All @@ -820,7 +823,6 @@ def _get_module_code(self, fullname):
# bad magic number or non-matching mtime
# in byte code, try next
continue
modpath = toc_entry[0]
return code, ispackage, modpath
else:
if import_error:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Fix duplicate :exc:`SyntaxWarning` messages when importing source modules
from ZIP archives. :meth:`zipimport.zipimporter.get_filename`, and therefore
:meth:`~zipimport.zipimporter.find_spec`, no longer compile source modules. A
:exc:`SyntaxError` in a zipped source module is now raised when the module is
executed rather than when it is found, matching the behavior of
filesystem-based loaders.
Loading