mirror of https://github.com/python/cpython
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:
parent
35b773accb
commit
c3f52b4d70
|
@ -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__ = {}
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Modules will always have a dictionary, even when created by
|
||||||
|
``types.ModuleType.__new__()``
|
|
@ -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 */
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue