Skip to content
Merged
37 changes: 37 additions & 0 deletions Lib/test/test_import/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import os
import py_compile
import random
import re
import shutil
import stat
import subprocess
Expand Down Expand Up @@ -807,6 +808,42 @@ def test_issue105979(self):
self.assertIn("Frozen object named 'x' is invalid",
str(cm.exception))

def test_frozen_module_from_import_error(self):
with self.assertRaises(ImportError) as cm:
from os import this_will_never_exist
self.assertRegex(
str(cm.exception),
f"cannot import name 'this_will_never_exist' from 'os' \\({re.escape(os.__file__)}\\)"
)


scripts = [
"""
import os
os.__spec__.has_location = False
os.__file__ = 123
from os import this_will_never_exist
""",
"""
import os
os.__spec__.has_location = False
del os.__file__
from os import this_will_never_exist
"""
]
for script in scripts:
with self.subTest(script=script), os_helper.temp_dir() as tmp:
with open(os.path.join(tmp, "main.py"), "w", encoding='utf-8') as f:
f.write(script)

expected_error = (
b"cannot import name 'this_will_never_exist' "
b"from 'os' \\(unknown location\\)"
)
popen = script_helper.spawn_python(os.path.join(tmp, "main.py"), cwd=tmp)
stdout, stderr = popen.communicate()
self.assertRegex(stdout, expected_error)

def test_script_shadowing_stdlib(self):
script_errors = [
(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
When raising :exc:`ImportError` for missing symbols in ``from`` imports, use ``__file__`` in the error message if ``__spec__.origin`` is not a location
13 changes: 13 additions & 0 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -2903,6 +2903,19 @@ _PyEval_ImportFrom(PyThreadState *tstate, PyObject *v, PyObject *name)
}
else {
assert(rc == 0);
if (origin == NULL) {
// Fall back to __file__ for diagnostics if we don't have
// an origin that is a location
origin = PyModule_GetFilenameObject(v);
if (origin == NULL) {
if (!PyErr_ExceptionMatches(PyExc_SystemError)) {
goto done;
}
// PyModule_GetFilenameObject raised "module filename missing"
_PyErr_Clear(tstate);
}
assert(origin == NULL || PyUnicode_Check(origin));
}
if (origin) {
errmsg = PyUnicode_FromFormat(
"cannot import name %R from %R (%S)",
Expand Down
Loading