mirror of
https://github.com/python/cpython.git
synced 2025-08-03 16:39:00 +00:00
Issue #24254: Preserve class attribute definition order.
This commit is contained in:
parent
4565986138
commit
92a6c170e6
18 changed files with 568 additions and 189 deletions
|
@ -48,6 +48,7 @@ static size_t method_cache_collisions = 0;
|
|||
_Py_IDENTIFIER(__abstractmethods__);
|
||||
_Py_IDENTIFIER(__class__);
|
||||
_Py_IDENTIFIER(__delitem__);
|
||||
_Py_IDENTIFIER(__definition_order__);
|
||||
_Py_IDENTIFIER(__dict__);
|
||||
_Py_IDENTIFIER(__doc__);
|
||||
_Py_IDENTIFIER(__getattribute__);
|
||||
|
@ -488,6 +489,23 @@ type_set_module(PyTypeObject *type, PyObject *value, void *context)
|
|||
return _PyDict_SetItemId(type->tp_dict, &PyId___module__, value);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
type_deforder(PyTypeObject *type, void *context)
|
||||
{
|
||||
if (type->tp_deforder == NULL)
|
||||
Py_RETURN_NONE;
|
||||
Py_INCREF(type->tp_deforder);
|
||||
return type->tp_deforder;
|
||||
}
|
||||
|
||||
static int
|
||||
type_set_deforder(PyTypeObject *type, PyObject *value, void *context)
|
||||
{
|
||||
Py_XINCREF(value);
|
||||
Py_XSETREF(type->tp_deforder, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
type_abstractmethods(PyTypeObject *type, void *context)
|
||||
{
|
||||
|
@ -834,6 +852,8 @@ static PyGetSetDef type_getsets[] = {
|
|||
{"__qualname__", (getter)type_qualname, (setter)type_set_qualname, NULL},
|
||||
{"__bases__", (getter)type_get_bases, (setter)type_set_bases, NULL},
|
||||
{"__module__", (getter)type_module, (setter)type_set_module, NULL},
|
||||
{"__definition_order__", (getter)type_deforder,
|
||||
(setter)type_set_deforder, NULL},
|
||||
{"__abstractmethods__", (getter)type_abstractmethods,
|
||||
(setter)type_set_abstractmethods, NULL},
|
||||
{"__dict__", (getter)type_dict, NULL, NULL},
|
||||
|
@ -2351,6 +2371,7 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
|
|||
goto error;
|
||||
}
|
||||
|
||||
/* Copy the definition namespace into a new dict. */
|
||||
dict = PyDict_Copy(orig_dict);
|
||||
if (dict == NULL)
|
||||
goto error;
|
||||
|
@ -2559,6 +2580,48 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
|
|||
if (qualname != NULL && PyDict_DelItem(dict, PyId___qualname__.object) < 0)
|
||||
goto error;
|
||||
|
||||
/* Set tp_deforder to the extracted definition order, if any. */
|
||||
type->tp_deforder = _PyDict_GetItemId(dict, &PyId___definition_order__);
|
||||
if (type->tp_deforder != NULL) {
|
||||
Py_INCREF(type->tp_deforder);
|
||||
|
||||
// Due to subclass lookup, __definition_order__ can't be in __dict__.
|
||||
if (_PyDict_DelItemId(dict, &PyId___definition_order__) != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (type->tp_deforder != Py_None) {
|
||||
Py_ssize_t numnames;
|
||||
|
||||
if (!PyTuple_Check(type->tp_deforder)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"__definition_order__ must be a tuple or None");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Make sure they are identifers.
|
||||
numnames = PyTuple_Size(type->tp_deforder);
|
||||
for (i = 0; i < numnames; i++) {
|
||||
PyObject *name = PyTuple_GET_ITEM(type->tp_deforder, i);
|
||||
if (name == NULL) {
|
||||
goto error;
|
||||
}
|
||||
if (!PyUnicode_Check(name) || !PyUnicode_IsIdentifier(name)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"__definition_order__ must "
|
||||
"contain only identifiers, got '%s'",
|
||||
name);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (PyODict_Check(orig_dict)) {
|
||||
type->tp_deforder = _PyODict_KeysAsTuple(orig_dict);
|
||||
if (type->tp_deforder == NULL)
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Set tp_doc to a copy of dict['__doc__'], if the latter is there
|
||||
and is a string. The __doc__ accessor will first look for tp_doc;
|
||||
if that fails, it will still look into __dict__.
|
||||
|
@ -3073,6 +3136,7 @@ type_dealloc(PyTypeObject *type)
|
|||
Py_XDECREF(type->tp_mro);
|
||||
Py_XDECREF(type->tp_cache);
|
||||
Py_XDECREF(type->tp_subclasses);
|
||||
Py_XDECREF(type->tp_deforder);
|
||||
/* A type's tp_doc is heap allocated, unlike the tp_doc slots
|
||||
* of most other objects. It's okay to cast it to char *.
|
||||
*/
|
||||
|
@ -3115,7 +3179,7 @@ type_subclasses(PyTypeObject *type, PyObject *args_ignored)
|
|||
static PyObject *
|
||||
type_prepare(PyObject *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
return PyDict_New();
|
||||
return PyODict_New();
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue