Thanks to visit codestin.com
Credit goes to github.com

Skip to content

bpo-28869: Skip one additional frame when type.__new__ is called not directly from type.__call__. #14166

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
7 changes: 7 additions & 0 deletions Lib/test/test_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
Original file line number Diff line number Diff line change
@@ -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`).
70 changes: 52 additions & 18 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 '('.
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless I am missing something, depth can be only either 0 or 1, maybe it is better to use a name like skip or nested and write this as:

if (skip_frame && 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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
{
Expand Down