diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index 21621c5ee4eac9..f6367fd1f4a672 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -83,6 +83,9 @@ Other Language Changes whitespace, not only spaces. (Contributed by Robert Xiao in :issue:`28927`.) +* :exc:`ImportError` now displays module name and module ``__file__`` path when + ``from ... import ...`` fails. :issue:`29546`. + New Modules =========== diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index df678f17f18e4e..ace1a46f9b91a7 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -85,6 +85,15 @@ def test_from_import_missing_attr_has_name_and_path(self): from os import i_dont_exist self.assertEqual(cm.exception.name, 'os') self.assertEqual(cm.exception.path, os.__file__) + self.assertRegex(str(cm.exception), "cannot import name 'i_dont_exist' from 'os' \(.*/Lib/os.py\)") + + def test_from_import_missing_attr_has_name_and_so_path(self): + import _opcode + with self.assertRaises(ImportError) as cm: + from _opcode import i_dont_exist + self.assertEqual(cm.exception.name, '_opcode') + self.assertEqual(cm.exception.path, _opcode.__file__) + self.assertRegex(str(cm.exception), "cannot import name 'i_dont_exist' from '_opcode' \(.*\.(so|dll)\)") def test_from_import_missing_attr_has_name(self): with self.assertRaises(ImportError) as cm: @@ -365,9 +374,12 @@ def __getattr__(self, _): module_name = 'test_from_import_AttributeError' self.addCleanup(unload, module_name) sys.modules[module_name] = AlwaysAttributeError() - with self.assertRaises(ImportError): + with self.assertRaises(ImportError) as cm: from test_from_import_AttributeError import does_not_exist + self.assertEqual(str(cm.exception), + "cannot import name 'does_not_exist' from '' (unknown location)") + @skip_if_dont_write_bytecode class FilePermissionTests(unittest.TestCase): diff --git a/Misc/NEWS b/Misc/NEWS index 0c2122f8ca04d1..0d4451f05ca51b 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -21,6 +21,8 @@ Core and Builtins - bpo-29546: Set the 'path' and 'name' attribute on ImportError for ``from ... import ...``. +- bpo-29546: Improve from-import error message with location + - Issue #29319: Prevent RunMainFromImporter overwriting sys.path[0]. - Issue #29337: Fixed possible BytesWarning when compare the code objects. diff --git a/Python/ceval.c b/Python/ceval.c index 69c93838419609..81c89dfadfaac0 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4995,7 +4995,7 @@ import_from(PyObject *v, PyObject *name) { PyObject *x; _Py_IDENTIFIER(__name__); - PyObject *fullmodname, *pkgname, *pkgpath; + PyObject *fullmodname, *pkgname, *pkgpath, *pkgname_or_unknown; x = PyObject_GetAttr(v, name); if (x != NULL || !PyErr_ExceptionMatches(PyExc_AttributeError)) @@ -5009,7 +5009,6 @@ import_from(PyObject *v, PyObject *name) goto error; } fullmodname = PyUnicode_FromFormat("%U.%U", pkgname, name); - Py_DECREF(pkgname); if (fullmodname == NULL) { return NULL; } @@ -5018,18 +5017,36 @@ import_from(PyObject *v, PyObject *name) if (x == NULL) { goto error; } + Py_DECREF(pkgname); Py_INCREF(x); return x; error: pkgpath = PyModule_GetFilenameObject(v); + if (pkgname == NULL) { + pkgname_or_unknown = PyUnicode_FromString(""); + if (pkgname_or_unknown == NULL) { + Py_XDECREF(pkgpath); + return NULL; + } + } else { + pkgname_or_unknown = pkgname; + } if (pkgpath == NULL || !PyUnicode_Check(pkgpath)) { PyErr_Clear(); - PyErr_SetImportError(PyUnicode_FromFormat("cannot import name %R", name), pkgname, NULL); + PyErr_SetImportError( + PyUnicode_FromFormat("cannot import name %R from %R (unknown location)", + name, pkgname_or_unknown), + pkgname, NULL); } else { - PyErr_SetImportError(PyUnicode_FromFormat("cannot import name %R", name), pkgname, pkgpath); + PyErr_SetImportError( + PyUnicode_FromFormat("cannot import name %R from %R (%S)", + name, pkgname_or_unknown, pkgpath), + pkgname, pkgpath); } + Py_XDECREF(pkgname_or_unknown); + Py_XDECREF(pkgpath); return NULL; } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy