Skip to content

Commit d8b291a

Browse files
authored
bpo-32932: More revealing error message when non-str objects in __all__ (GH-5848)
1 parent 5cbb841 commit d8b291a

File tree

3 files changed

+49
-1
lines changed

3 files changed

+49
-1
lines changed

Lib/test/test_import/__init__.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,27 @@ def test_from_import_missing_attr_path_is_canonical(self):
111111
self.assertIn(cm.exception.name, {'posixpath', 'ntpath'})
112112
self.assertIsNotNone(cm.exception)
113113

114+
def test_from_import_star_invalid_type(self):
115+
import re
116+
with _ready_to_import() as (name, path):
117+
with open(path, 'w') as f:
118+
f.write("__all__ = [b'invalid_type']")
119+
globals = {}
120+
with self.assertRaisesRegex(
121+
TypeError, f"{re.escape(name)}\.__all__ must be str"
122+
):
123+
exec(f"from {name} import *", globals)
124+
self.assertNotIn(b"invalid_type", globals)
125+
with _ready_to_import() as (name, path):
126+
with open(path, 'w') as f:
127+
f.write("globals()[b'invalid_type'] = object()")
128+
globals = {}
129+
with self.assertRaisesRegex(
130+
TypeError, f"{re.escape(name)}\.__dict__ must be str"
131+
):
132+
exec(f"from {name} import *", globals)
133+
self.assertNotIn(b"invalid_type", globals)
134+
114135
def test_case_sensitivity(self):
115136
# Brief digression to test that import is case-sensitive: if we got
116137
# this far, we know for sure that "random" exists.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Make error message more revealing when there are non-str objects in ``__all__``.

Python/ceval.c

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4837,6 +4837,7 @@ import_all_from(PyObject *locals, PyObject *v)
48374837
{
48384838
_Py_IDENTIFIER(__all__);
48394839
_Py_IDENTIFIER(__dict__);
4840+
_Py_IDENTIFIER(__name__);
48404841
PyObject *all, *dict, *name, *value;
48414842
int skip_leading_underscores = 0;
48424843
int pos, err;
@@ -4869,7 +4870,32 @@ import_all_from(PyObject *locals, PyObject *v)
48694870
PyErr_Clear();
48704871
break;
48714872
}
4872-
if (skip_leading_underscores && PyUnicode_Check(name)) {
4873+
if (!PyUnicode_Check(name)) {
4874+
PyObject *modname = _PyObject_GetAttrId(v, &PyId___name__);
4875+
if (modname == NULL) {
4876+
Py_DECREF(name);
4877+
err = -1;
4878+
break;
4879+
}
4880+
if (!PyUnicode_Check(modname)) {
4881+
PyErr_Format(PyExc_TypeError,
4882+
"module __name__ must be a string, not %.100s",
4883+
Py_TYPE(modname)->tp_name);
4884+
}
4885+
else {
4886+
PyErr_Format(PyExc_TypeError,
4887+
"%s in %U.%s must be str, not %.100s",
4888+
skip_leading_underscores ? "Key" : "Item",
4889+
modname,
4890+
skip_leading_underscores ? "__dict__" : "__all__",
4891+
Py_TYPE(name)->tp_name);
4892+
}
4893+
Py_DECREF(modname);
4894+
Py_DECREF(name);
4895+
err = -1;
4896+
break;
4897+
}
4898+
if (skip_leading_underscores) {
48734899
if (PyUnicode_READY(name) == -1) {
48744900
Py_DECREF(name);
48754901
err = -1;

0 commit comments

Comments
 (0)