Skip to content

Commit 3983527

Browse files
gh-127651: Use __file__ in diagnostics if origin is missing (#127660)
See the left hand side in https://github.com/python/cpython/pull/123929/files#diff-c22186367cbe20233e843261998dc027ae5f1f8c0d2e778abfa454ae74cc59deL2840-L2849 --------- Co-authored-by: Serhiy Storchaka <[email protected]>
1 parent 58c7538 commit 3983527

File tree

3 files changed

+66
-5
lines changed

3 files changed

+66
-5
lines changed

Lib/test/test_import/__init__.py

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -807,6 +807,50 @@ def test_issue105979(self):
807807
self.assertIn("Frozen object named 'x' is invalid",
808808
str(cm.exception))
809809

810+
def test_frozen_module_from_import_error(self):
811+
with self.assertRaises(ImportError) as cm:
812+
from os import this_will_never_exist
813+
self.assertIn(
814+
f"cannot import name 'this_will_never_exist' from 'os' ({os.__file__})",
815+
str(cm.exception),
816+
)
817+
with self.assertRaises(ImportError) as cm:
818+
from sys import this_will_never_exist
819+
self.assertIn(
820+
"cannot import name 'this_will_never_exist' from 'sys' (unknown location)",
821+
str(cm.exception),
822+
)
823+
824+
scripts = [
825+
"""
826+
import os
827+
os.__spec__.has_location = False
828+
os.__file__ = []
829+
from os import this_will_never_exist
830+
""",
831+
"""
832+
import os
833+
os.__spec__.has_location = False
834+
del os.__file__
835+
from os import this_will_never_exist
836+
""",
837+
"""
838+
import os
839+
os.__spec__.origin = []
840+
os.__file__ = []
841+
from os import this_will_never_exist
842+
"""
843+
]
844+
for script in scripts:
845+
with self.subTest(script=script):
846+
expected_error = (
847+
b"cannot import name 'this_will_never_exist' "
848+
b"from 'os' (unknown location)"
849+
)
850+
popen = script_helper.spawn_python("-c", script)
851+
stdout, stderr = popen.communicate()
852+
self.assertIn(expected_error, stdout)
853+
810854
def test_script_shadowing_stdlib(self):
811855
script_errors = [
812856
(
@@ -1068,7 +1112,7 @@ class substr(str):
10681112
except AttributeError as e:
10691113
print(str(e))
10701114
1071-
fractions.__spec__.origin = 0
1115+
fractions.__spec__.origin = []
10721116
try:
10731117
fractions.Fraction
10741118
except AttributeError as e:
@@ -1092,7 +1136,7 @@ class substr(str):
10921136
except ImportError as e:
10931137
print(str(e))
10941138
1095-
fractions.__spec__.origin = 0
1139+
fractions.__spec__.origin = []
10961140
try:
10971141
from fractions import Fraction
10981142
except ImportError as e:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
When raising :exc:`ImportError` for missing symbols in ``from`` imports, use ``__file__`` in the error message if ``__spec__.origin`` is not a location

Python/ceval.c

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2859,6 +2859,20 @@ _PyEval_ImportFrom(PyThreadState *tstate, PyObject *v, PyObject *name)
28592859
}
28602860
}
28612861

2862+
if (origin == NULL) {
2863+
// Fall back to __file__ for diagnostics if we don't have
2864+
// an origin that is a location
2865+
origin = PyModule_GetFilenameObject(v);
2866+
if (origin == NULL) {
2867+
if (!PyErr_ExceptionMatches(PyExc_SystemError)) {
2868+
goto done;
2869+
}
2870+
// PyModule_GetFilenameObject raised "module filename missing"
2871+
_PyErr_Clear(tstate);
2872+
}
2873+
assert(origin == NULL || PyUnicode_Check(origin));
2874+
}
2875+
28622876
if (is_possibly_shadowing_stdlib) {
28632877
assert(origin);
28642878
errmsg = PyUnicode_FromFormat(
@@ -2919,9 +2933,11 @@ _PyEval_ImportFrom(PyThreadState *tstate, PyObject *v, PyObject *name)
29192933
}
29202934

29212935
done_with_errmsg:
2922-
/* NULL checks for errmsg, mod_name, origin done by PyErr_SetImportError. */
2923-
_PyErr_SetImportErrorWithNameFrom(errmsg, mod_name, origin, name);
2924-
Py_DECREF(errmsg);
2936+
if (errmsg != NULL) {
2937+
/* NULL checks for mod_name and origin done by _PyErr_SetImportErrorWithNameFrom */
2938+
_PyErr_SetImportErrorWithNameFrom(errmsg, mod_name, origin, name);
2939+
Py_DECREF(errmsg);
2940+
}
29252941

29262942
done:
29272943
Py_XDECREF(origin);

0 commit comments

Comments
 (0)