]> granicus.if.org Git - python/commitdiff
bpo-29546: Set 'path' on ImportError for ``from ... import ...`` (GH-91)
authorMatthias Bussonnier <bussonniermatthias@gmail.com>
Wed, 15 Feb 2017 00:05:25 +0000 (16:05 -0800)
committerBrett Cannon <brettcannon@users.noreply.github.com>
Wed, 15 Feb 2017 00:05:25 +0000 (16:05 -0800)
Lib/test/test_import/__init__.py
Misc/NEWS
Python/ceval.c

index d61782a6cb9ac8e68078eb57af3dc7feedc70f76..df678f17f18e4e1b28997bda6265208a79fb452a 100644 (file)
@@ -80,6 +80,25 @@ class ImportTests(unittest.TestCase):
         with self.assertRaises(ImportError):
             from importlib import something_that_should_not_exist_anywhere
 
+    def test_from_import_missing_attr_has_name_and_path(self):
+        with self.assertRaises(ImportError) as cm:
+            from os import i_dont_exist
+        self.assertEqual(cm.exception.name, 'os')
+        self.assertEqual(cm.exception.path, os.__file__)
+
+    def test_from_import_missing_attr_has_name(self):
+        with self.assertRaises(ImportError) as cm:
+            # _warning has no path as it's a built-in module.
+            from _warning import i_dont_exist
+        self.assertEqual(cm.exception.name, '_warning')
+        self.assertIsNone(cm.exception.path)
+
+    def test_from_import_missing_attr_path_is_canonical(self):
+        with self.assertRaises(ImportError) as cm:
+            from os.path import i_dont_exist
+        self.assertIn(cm.exception.name, {'posixpath', 'ntpath'})
+        self.assertIsNotNone(cm.exception)
+
     def test_case_sensitivity(self):
         # Brief digression to test that import is case-sensitive:  if we got
         # this far, we know for sure that "random" exists.
index e024e01a300b7d6bb7dfc93d8d0c75293ced9f61..51055ef6f6f5bab59b0bf1b1fe8094dc9e2b4967 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,8 @@ Core and Builtins
 
 - bpo-29438: Fixed use-after-free problem in key sharing dict.
 
+- bpo-29546: Set the 'path' and 'name' attribute on ImportError for ``from ... import ...``.
+
 - Issue #29319: Prevent RunMainFromImporter overwriting sys.path[0].
 
 - Issue #29337: Fixed possible BytesWarning when compare the code objects.
index 66fd361502626f86bf7da68a35f09553e911d895..69c9383841960979d5abcd38ebd67c22923bbcd3 100644 (file)
@@ -4995,7 +4995,7 @@ import_from(PyObject *v, PyObject *name)
 {
     PyObject *x;
     _Py_IDENTIFIER(__name__);
-    PyObject *fullmodname, *pkgname;
+    PyObject *fullmodname, *pkgname, *pkgpath;
 
     x = PyObject_GetAttr(v, name);
     if (x != NULL || !PyErr_ExceptionMatches(PyExc_AttributeError))
@@ -5021,7 +5021,15 @@ import_from(PyObject *v, PyObject *name)
     Py_INCREF(x);
     return x;
  error:
-    PyErr_Format(PyExc_ImportError, "cannot import name %R", name);
+    pkgpath = PyModule_GetFilenameObject(v);
+
+    if (pkgpath == NULL || !PyUnicode_Check(pkgpath)) {
+        PyErr_Clear();
+        PyErr_SetImportError(PyUnicode_FromFormat("cannot import name %R", name), pkgname, NULL);
+    } else {
+        PyErr_SetImportError(PyUnicode_FromFormat("cannot import name %R", name), pkgname, pkgpath);
+    }
+
     return NULL;
 }