Skip to content

Commit 6441d42

Browse files
authored
[3.13] gh-127651: Use __file__ in diagnostics if origin is missing (#127660) (#127775)
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]> (cherry picked from commit 3983527)
1 parent 310efda commit 6441d42

File tree

3 files changed

+66
-5
lines changed

3 files changed

+66
-5
lines changed

Lib/test/test_import/__init__.py

+46-2
Original file line numberDiff line numberDiff line change
@@ -826,6 +826,50 @@ def test_issue105979(self):
826826
self.assertIn("Frozen object named 'x' is invalid",
827827
str(cm.exception))
828828

829+
def test_frozen_module_from_import_error(self):
830+
with self.assertRaises(ImportError) as cm:
831+
from os import this_will_never_exist
832+
self.assertIn(
833+
f"cannot import name 'this_will_never_exist' from 'os' ({os.__file__})",
834+
str(cm.exception),
835+
)
836+
with self.assertRaises(ImportError) as cm:
837+
from sys import this_will_never_exist
838+
self.assertIn(
839+
"cannot import name 'this_will_never_exist' from 'sys' (unknown location)",
840+
str(cm.exception),
841+
)
842+
843+
scripts = [
844+
"""
845+
import os
846+
os.__spec__.has_location = False
847+
os.__file__ = []
848+
from os import this_will_never_exist
849+
""",
850+
"""
851+
import os
852+
os.__spec__.has_location = False
853+
del os.__file__
854+
from os import this_will_never_exist
855+
""",
856+
"""
857+
import os
858+
os.__spec__.origin = []
859+
os.__file__ = []
860+
from os import this_will_never_exist
861+
"""
862+
]
863+
for script in scripts:
864+
with self.subTest(script=script):
865+
expected_error = (
866+
b"cannot import name 'this_will_never_exist' "
867+
b"from 'os' (unknown location)"
868+
)
869+
popen = script_helper.spawn_python("-c", script)
870+
stdout, stderr = popen.communicate()
871+
self.assertIn(expected_error, stdout)
872+
829873
def test_script_shadowing_stdlib(self):
830874
script_errors = [
831875
(
@@ -1087,7 +1131,7 @@ class substr(str):
10871131
except AttributeError as e:
10881132
print(str(e))
10891133
1090-
fractions.__spec__.origin = 0
1134+
fractions.__spec__.origin = []
10911135
try:
10921136
fractions.Fraction
10931137
except AttributeError as e:
@@ -1111,7 +1155,7 @@ class substr(str):
11111155
except ImportError as e:
11121156
print(str(e))
11131157
1114-
fractions.__spec__.origin = 0
1158+
fractions.__spec__.origin = []
11151159
try:
11161160
from fractions import Fraction
11171161
except ImportError as e:
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

+19-3
Original file line numberDiff line numberDiff line change
@@ -2785,6 +2785,20 @@ import_from(PyThreadState *tstate, PyObject *v, PyObject *name)
27852785
}
27862786
}
27872787

2788+
if (origin == NULL) {
2789+
// Fall back to __file__ for diagnostics if we don't have
2790+
// an origin that is a location
2791+
origin = PyModule_GetFilenameObject(v);
2792+
if (origin == NULL) {
2793+
if (!PyErr_ExceptionMatches(PyExc_SystemError)) {
2794+
goto done;
2795+
}
2796+
// PyModule_GetFilenameObject raised "module filename missing"
2797+
_PyErr_Clear(tstate);
2798+
}
2799+
assert(origin == NULL || PyUnicode_Check(origin));
2800+
}
2801+
27882802
if (is_possibly_shadowing_stdlib) {
27892803
assert(origin);
27902804
errmsg = PyUnicode_FromFormat(
@@ -2845,9 +2859,11 @@ import_from(PyThreadState *tstate, PyObject *v, PyObject *name)
28452859
}
28462860

28472861
done_with_errmsg:
2848-
/* NULL checks for errmsg, mod_name, origin done by PyErr_SetImportError. */
2849-
_PyErr_SetImportErrorWithNameFrom(errmsg, mod_name, origin, name);
2850-
Py_DECREF(errmsg);
2862+
if (errmsg != NULL) {
2863+
/* NULL checks for mod_name and origin done by _PyErr_SetImportErrorWithNameFrom */
2864+
_PyErr_SetImportErrorWithNameFrom(errmsg, mod_name, origin, name);
2865+
Py_DECREF(errmsg);
2866+
}
28512867

28522868
done:
28532869
Py_XDECREF(origin);

0 commit comments

Comments
 (0)