Skip to content

Commit 1bc1564

Browse files
Carreauwarsaw
authored andcommitted
bpo-29546: Improve from-import error message with location (#103)
bpo-29546: Improve from-import error message with location
1 parent d37c068 commit 1bc1564

File tree

4 files changed

+39
-5
lines changed

4 files changed

+39
-5
lines changed

Doc/whatsnew/3.7.rst

+3
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ Other Language Changes
8383
whitespace, not only spaces.
8484
(Contributed by Robert Xiao in :issue:`28927`.)
8585

86+
* :exc:`ImportError` now displays module name and module ``__file__`` path when
87+
``from ... import ...`` fails. :issue:`29546`.
88+
8689

8790
New Modules
8891
===========

Lib/test/test_import/__init__.py

+13-1
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,15 @@ def test_from_import_missing_attr_has_name_and_path(self):
8585
from os import i_dont_exist
8686
self.assertEqual(cm.exception.name, 'os')
8787
self.assertEqual(cm.exception.path, os.__file__)
88+
self.assertRegex(str(cm.exception), "cannot import name 'i_dont_exist' from 'os' \(.*/Lib/os.py\)")
89+
90+
def test_from_import_missing_attr_has_name_and_so_path(self):
91+
import _opcode
92+
with self.assertRaises(ImportError) as cm:
93+
from _opcode import i_dont_exist
94+
self.assertEqual(cm.exception.name, '_opcode')
95+
self.assertEqual(cm.exception.path, _opcode.__file__)
96+
self.assertRegex(str(cm.exception), "cannot import name 'i_dont_exist' from '_opcode' \(.*\.(so|dll)\)")
8897

8998
def test_from_import_missing_attr_has_name(self):
9099
with self.assertRaises(ImportError) as cm:
@@ -365,9 +374,12 @@ def __getattr__(self, _):
365374
module_name = 'test_from_import_AttributeError'
366375
self.addCleanup(unload, module_name)
367376
sys.modules[module_name] = AlwaysAttributeError()
368-
with self.assertRaises(ImportError):
377+
with self.assertRaises(ImportError) as cm:
369378
from test_from_import_AttributeError import does_not_exist
370379

380+
self.assertEqual(str(cm.exception),
381+
"cannot import name 'does_not_exist' from '<unknown module name>' (unknown location)")
382+
371383

372384
@skip_if_dont_write_bytecode
373385
class FilePermissionTests(unittest.TestCase):

Misc/NEWS

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ Core and Builtins
2424

2525
- bpo-29546: Set the 'path' and 'name' attribute on ImportError for ``from ... import ...``.
2626

27+
- bpo-29546: Improve from-import error message with location
28+
2729
- Issue #29319: Prevent RunMainFromImporter overwriting sys.path[0].
2830

2931
- Issue #29337: Fixed possible BytesWarning when compare the code objects.

Python/ceval.c

+21-4
Original file line numberDiff line numberDiff line change
@@ -4995,7 +4995,7 @@ import_from(PyObject *v, PyObject *name)
49954995
{
49964996
PyObject *x;
49974997
_Py_IDENTIFIER(__name__);
4998-
PyObject *fullmodname, *pkgname, *pkgpath;
4998+
PyObject *fullmodname, *pkgname, *pkgpath, *pkgname_or_unknown;
49994999

50005000
x = PyObject_GetAttr(v, name);
50015001
if (x != NULL || !PyErr_ExceptionMatches(PyExc_AttributeError))
@@ -5009,7 +5009,6 @@ import_from(PyObject *v, PyObject *name)
50095009
goto error;
50105010
}
50115011
fullmodname = PyUnicode_FromFormat("%U.%U", pkgname, name);
5012-
Py_DECREF(pkgname);
50135012
if (fullmodname == NULL) {
50145013
return NULL;
50155014
}
@@ -5018,18 +5017,36 @@ import_from(PyObject *v, PyObject *name)
50185017
if (x == NULL) {
50195018
goto error;
50205019
}
5020+
Py_DECREF(pkgname);
50215021
Py_INCREF(x);
50225022
return x;
50235023
error:
50245024
pkgpath = PyModule_GetFilenameObject(v);
5025+
if (pkgname == NULL) {
5026+
pkgname_or_unknown = PyUnicode_FromString("<unknown module name>");
5027+
if (pkgname_or_unknown == NULL) {
5028+
Py_XDECREF(pkgpath);
5029+
return NULL;
5030+
}
5031+
} else {
5032+
pkgname_or_unknown = pkgname;
5033+
}
50255034

50265035
if (pkgpath == NULL || !PyUnicode_Check(pkgpath)) {
50275036
PyErr_Clear();
5028-
PyErr_SetImportError(PyUnicode_FromFormat("cannot import name %R", name), pkgname, NULL);
5037+
PyErr_SetImportError(
5038+
PyUnicode_FromFormat("cannot import name %R from %R (unknown location)",
5039+
name, pkgname_or_unknown),
5040+
pkgname, NULL);
50295041
} else {
5030-
PyErr_SetImportError(PyUnicode_FromFormat("cannot import name %R", name), pkgname, pkgpath);
5042+
PyErr_SetImportError(
5043+
PyUnicode_FromFormat("cannot import name %R from %R (%S)",
5044+
name, pkgname_or_unknown, pkgpath),
5045+
pkgname, pkgpath);
50315046
}
50325047

5048+
Py_XDECREF(pkgname_or_unknown);
5049+
Py_XDECREF(pkgpath);
50335050
return NULL;
50345051
}
50355052

0 commit comments

Comments
 (0)