bpo-44486: Make sure that modules always have a dictionary. (GH-26847)

* Make sure that modules always have a dictionary.
This commit is contained in:
Mark Shannon 2021-06-23 10:00:43 +01:00 committed by GitHub
parent 35b773accb
commit c3f52b4d70
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 66 additions and 52 deletions

View File

@ -22,8 +22,8 @@ class ModuleTests(unittest.TestCase):
# An uninitialized module has no __dict__ or __name__, # An uninitialized module has no __dict__ or __name__,
# and __doc__ is None # and __doc__ is None
foo = ModuleType.__new__(ModuleType) foo = ModuleType.__new__(ModuleType)
self.assertTrue(foo.__dict__ is None) self.assertTrue(isinstance(foo.__dict__, dict))
self.assertRaises(TypeError, dir, foo) self.assertEqual(dir(foo), [])
try: try:
s = foo.__name__ s = foo.__name__
self.fail("__name__ = %s" % repr(s)) self.fail("__name__ = %s" % repr(s))
@ -318,15 +318,6 @@ a = A(destroyed)"""
del foo.__dict__['__annotations__'] del foo.__dict__['__annotations__']
def test_annotations_getset_raises(self): def test_annotations_getset_raises(self):
# module has no dict, all operations fail
foo = ModuleType.__new__(ModuleType)
with self.assertRaises(TypeError):
print(foo.__annotations__)
with self.assertRaises(TypeError):
foo.__annotations__ = {}
with self.assertRaises(TypeError):
del foo.__annotations__
# double delete # double delete
foo = ModuleType("foo") foo = ModuleType("foo")
foo.__annotations__ = {} foo.__annotations__ = {}

View File

@ -0,0 +1,2 @@
Modules will always have a dictionary, even when created by
``types.ModuleType.__new__()``

View File

@ -64,8 +64,7 @@ module_init_dict(PyModuleObject *mod, PyObject *md_dict,
_Py_IDENTIFIER(__package__); _Py_IDENTIFIER(__package__);
_Py_IDENTIFIER(__loader__); _Py_IDENTIFIER(__loader__);
if (md_dict == NULL) assert(md_dict != NULL);
return -1;
if (doc == NULL) if (doc == NULL)
doc = Py_None; doc = Py_None;
@ -87,12 +86,11 @@ module_init_dict(PyModuleObject *mod, PyObject *md_dict,
return 0; return 0;
} }
static PyModuleObject *
PyObject * new_module_notrack(PyTypeObject *mt)
PyModule_NewObject(PyObject *name)
{ {
PyModuleObject *m; PyModuleObject *m;
m = PyObject_GC_New(PyModuleObject, &PyModule_Type); m = PyObject_GC_New(PyModuleObject, mt);
if (m == NULL) if (m == NULL)
return NULL; return NULL;
m->md_def = NULL; m->md_def = NULL;
@ -100,6 +98,29 @@ PyModule_NewObject(PyObject *name)
m->md_weaklist = NULL; m->md_weaklist = NULL;
m->md_name = NULL; m->md_name = NULL;
m->md_dict = PyDict_New(); m->md_dict = PyDict_New();
if (m->md_dict != NULL) {
return m;
}
Py_DECREF(m);
return NULL;
}
static PyObject *
new_module(PyTypeObject *mt, PyObject *args, PyObject *kws)
{
PyObject *m = (PyObject *)new_module_notrack(mt);
if (m != NULL) {
PyObject_GC_Track(m);
}
return m;
}
PyObject *
PyModule_NewObject(PyObject *name)
{
PyModuleObject *m = new_module_notrack(&PyModule_Type);
if (m == NULL)
return NULL;
if (module_init_dict(m, m->md_dict, name, NULL) != 0) if (module_init_dict(m, m->md_dict, name, NULL) != 0)
goto fail; goto fail;
PyObject_GC_Track(m); PyObject_GC_Track(m);
@ -728,43 +749,42 @@ module_getattro(PyModuleObject *m, PyObject *name)
return attr; return attr;
} }
PyErr_Clear(); PyErr_Clear();
if (m->md_dict) { assert(m->md_dict != NULL);
_Py_IDENTIFIER(__getattr__); _Py_IDENTIFIER(__getattr__);
getattr = _PyDict_GetItemIdWithError(m->md_dict, &PyId___getattr__); getattr = _PyDict_GetItemIdWithError(m->md_dict, &PyId___getattr__);
if (getattr) { if (getattr) {
return PyObject_CallOneArg(getattr, name); return PyObject_CallOneArg(getattr, name);
} }
if (PyErr_Occurred()) { if (PyErr_Occurred()) {
return NULL; return NULL;
} }
mod_name = _PyDict_GetItemIdWithError(m->md_dict, &PyId___name__); mod_name = _PyDict_GetItemIdWithError(m->md_dict, &PyId___name__);
if (mod_name && PyUnicode_Check(mod_name)) { if (mod_name && PyUnicode_Check(mod_name)) {
Py_INCREF(mod_name); Py_INCREF(mod_name);
PyObject *spec = _PyDict_GetItemIdWithError(m->md_dict, &PyId___spec__); PyObject *spec = _PyDict_GetItemIdWithError(m->md_dict, &PyId___spec__);
if (spec == NULL && PyErr_Occurred()) { if (spec == NULL && PyErr_Occurred()) {
Py_DECREF(mod_name);
return NULL;
}
Py_XINCREF(spec);
if (_PyModuleSpec_IsInitializing(spec)) {
PyErr_Format(PyExc_AttributeError,
"partially initialized "
"module '%U' has no attribute '%U' "
"(most likely due to a circular import)",
mod_name, name);
}
else {
PyErr_Format(PyExc_AttributeError,
"module '%U' has no attribute '%U'",
mod_name, name);
}
Py_XDECREF(spec);
Py_DECREF(mod_name); Py_DECREF(mod_name);
return NULL; return NULL;
} }
else if (PyErr_Occurred()) { Py_XINCREF(spec);
return NULL; if (_PyModuleSpec_IsInitializing(spec)) {
PyErr_Format(PyExc_AttributeError,
"partially initialized "
"module '%U' has no attribute '%U' "
"(most likely due to a circular import)",
mod_name, name);
} }
else {
PyErr_Format(PyExc_AttributeError,
"module '%U' has no attribute '%U'",
mod_name, name);
}
Py_XDECREF(spec);
Py_DECREF(mod_name);
return NULL;
}
else if (PyErr_Occurred()) {
return NULL;
} }
PyErr_Format(PyExc_AttributeError, PyErr_Format(PyExc_AttributeError,
"module has no attribute '%U'", name); "module has no attribute '%U'", name);
@ -948,7 +968,7 @@ PyTypeObject PyModule_Type = {
0, /* tp_descr_set */ 0, /* tp_descr_set */
offsetof(PyModuleObject, md_dict), /* tp_dictoffset */ offsetof(PyModuleObject, md_dict), /* tp_dictoffset */
module___init__, /* tp_init */ module___init__, /* tp_init */
PyType_GenericAlloc, /* tp_alloc */ 0, /* tp_alloc */
PyType_GenericNew, /* tp_new */ new_module, /* tp_new */
PyObject_GC_Del, /* tp_free */ PyObject_GC_Del, /* tp_free */
}; };

View File

@ -3337,6 +3337,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
_PyLoadAttrCache *cache1 = &caches[-1].load_attr; _PyLoadAttrCache *cache1 = &caches[-1].load_attr;
DEOPT_IF(!PyModule_CheckExact(owner), LOAD_ATTR); DEOPT_IF(!PyModule_CheckExact(owner), LOAD_ATTR);
PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner)->md_dict; PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner)->md_dict;
assert(dict != NULL);
DEOPT_IF(dict->ma_keys->dk_version != cache1->dk_version_or_hint, LOAD_ATTR); DEOPT_IF(dict->ma_keys->dk_version != cache1->dk_version_or_hint, LOAD_ATTR);
assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE); assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE);
assert(cache0->index < dict->ma_keys->dk_nentries); assert(cache0->index < dict->ma_keys->dk_nentries);