diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index ceea79f6ad9..606b05784af 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -111,6 +111,27 @@ class ImportTests(unittest.TestCase): self.assertIn(cm.exception.name, {'posixpath', 'ntpath'}) self.assertIsNotNone(cm.exception) + def test_from_import_star_invalid_type(self): + import re + with _ready_to_import() as (name, path): + with open(path, 'w') as f: + f.write("__all__ = [b'invalid_type']") + globals = {} + with self.assertRaisesRegex( + TypeError, f"{re.escape(name)}\.__all__ must be str" + ): + exec(f"from {name} import *", globals) + self.assertNotIn(b"invalid_type", globals) + with _ready_to_import() as (name, path): + with open(path, 'w') as f: + f.write("globals()[b'invalid_type'] = object()") + globals = {} + with self.assertRaisesRegex( + TypeError, f"{re.escape(name)}\.__dict__ must be str" + ): + exec(f"from {name} import *", globals) + self.assertNotIn(b"invalid_type", globals) + 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. diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-02-24-21-51-42.bpo-32932.2cz31L.rst b/Misc/NEWS.d/next/Core and Builtins/2018-02-24-21-51-42.bpo-32932.2cz31L.rst new file mode 100644 index 00000000000..51e3d9b613d --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2018-02-24-21-51-42.bpo-32932.2cz31L.rst @@ -0,0 +1 @@ +Make error message more revealing when there are non-str objects in ``__all__``. diff --git a/Python/ceval.c b/Python/ceval.c index 14603d33008..d18a284e9f9 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4837,6 +4837,7 @@ import_all_from(PyObject *locals, PyObject *v) { _Py_IDENTIFIER(__all__); _Py_IDENTIFIER(__dict__); + _Py_IDENTIFIER(__name__); PyObject *all, *dict, *name, *value; int skip_leading_underscores = 0; int pos, err; @@ -4869,7 +4870,32 @@ import_all_from(PyObject *locals, PyObject *v) PyErr_Clear(); break; } - if (skip_leading_underscores && PyUnicode_Check(name)) { + if (!PyUnicode_Check(name)) { + PyObject *modname = _PyObject_GetAttrId(v, &PyId___name__); + if (modname == NULL) { + Py_DECREF(name); + err = -1; + break; + } + if (!PyUnicode_Check(modname)) { + PyErr_Format(PyExc_TypeError, + "module __name__ must be a string, not %.100s", + Py_TYPE(modname)->tp_name); + } + else { + PyErr_Format(PyExc_TypeError, + "%s in %U.%s must be str, not %.100s", + skip_leading_underscores ? "Key" : "Item", + modname, + skip_leading_underscores ? "__dict__" : "__all__", + Py_TYPE(name)->tp_name); + } + Py_DECREF(modname); + Py_DECREF(name); + err = -1; + break; + } + if (skip_leading_underscores) { if (PyUnicode_READY(name) == -1) { Py_DECREF(name); err = -1;