diff --git a/Lib/test/test_abc.py b/Lib/test/test_abc.py index 000e5838e3c712..5a24c0a9b113a9 100644 --- a/Lib/test/test_abc.py +++ b/Lib/test/test_abc.py @@ -488,6 +488,13 @@ class C(with_metaclass(abc_ABCMeta, A, B)): pass self.assertEqual(C.__class__, abc_ABCMeta) + def test_type_module(self): + class A(metaclass=abc_ABCMeta): + pass + self.assertEqual(A.__module__, __name__) + B = abc_ABCMeta('B', (), {}) + self.assertEqual(B.__module__, __name__) + class TestABCWithInitSubclass(unittest.TestCase): def test_works_with_init_subclass(self): diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-10-13-13-58-45.bpo-28869.vuckM1.rst b/Misc/NEWS.d/next/Core and Builtins/2019-10-13-13-58-45.bpo-28869.vuckM1.rst new file mode 100644 index 00000000000000..2082e0971c5fa9 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-10-13-13-58-45.bpo-28869.vuckM1.rst @@ -0,0 +1,4 @@ +:meth:`type.__new__` now skips one additional frame when determine the +default value for ``__module__`` if it was not called directly from +:meth:`type.__call__`. This fixes ``__module__`` of classes created by +calling a metaclass (like :class:`abc.ABCMeta`). diff --git a/Objects/typeobject.c b/Objects/typeobject.c index e27c4b2c40a2c0..c6cf2b1e7df70d 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -85,6 +85,12 @@ clear_slotdefs(void); static PyObject * lookup_maybe_method(PyObject *self, _Py_Identifier *attrid, int *unbound); +static PyObject * +type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds); + +static PyObject * +type_new_ex(PyTypeObject *metatype, PyObject *args, PyObject *kwds, int depth); + /* * finds the beginning of the docstring's introspection signature. * if present, returns a pointer pointing to the first '('. @@ -995,7 +1001,12 @@ type_call(PyTypeObject *type, PyObject *args, PyObject *kwds) return NULL; } - obj = type->tp_new(type, args, kwds); + if (type->tp_new == type_new) { + obj = type_new_ex(type, args, kwds, 0); + } + else { + obj = type->tp_new(type, args, kwds); + } obj = _Py_CheckFunctionResult(tstate, (PyObject*)type, obj, NULL); if (obj == NULL) return NULL; @@ -2344,8 +2355,39 @@ _PyType_CalculateMetaclass(PyTypeObject *metatype, PyObject *bases) return winner; } + +static int +type_set_default_module(PyObject *dict, int depth) +{ + if (_PyDict_GetItemIdWithError(dict, &PyId___module__) != NULL) { + return 0; + } + if (PyErr_Occurred()) { + return -1; + } + + PyFrameObject *f = PyEval_GetFrame(); + if (f == NULL) { + return 0; + } + while (depth-- > 0 && f->f_back) { + f = f->f_back; + } + + PyObject *name = _PyDict_GetItemIdWithError(f->f_globals, &PyId___name__); + if (name != NULL) { + if (_PyDict_SetItemId(dict, &PyId___module__, name) < 0) { + return -1; + } + } + else if (PyErr_Occurred()) { + return -1; + } + return 0; +} + static PyObject * -type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) +type_new_ex(PyTypeObject *metatype, PyObject *args, PyObject *kwds, int depth) { PyObject *name, *bases = NULL, *orig_dict, *dict = NULL; PyObject *qualname, *slots = NULL, *tmp, *newslots, *cell; @@ -2608,22 +2650,8 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) type->tp_dict = dict; /* Set __module__ in the dict */ - if (_PyDict_GetItemIdWithError(dict, &PyId___module__) == NULL) { - if (PyErr_Occurred()) { - goto error; - } - tmp = PyEval_GetGlobals(); - if (tmp != NULL) { - tmp = _PyDict_GetItemIdWithError(tmp, &PyId___name__); - if (tmp != NULL) { - if (_PyDict_SetItemId(dict, &PyId___module__, - tmp) < 0) - goto error; - } - else if (PyErr_Occurred()) { - goto error; - } - } + if (type_set_default_module(dict, depth) < 0) { + goto error; } /* Set ht_qualname to dict['__qualname__'] if available, else to @@ -2845,6 +2873,12 @@ static const short slotoffsets[] = { #include "typeslots.inc" }; +static PyObject * +type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) +{ + return type_new_ex(metatype, args, kwds, 1); +} + PyObject * PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) {