summaryrefslogtreecommitdiffstats
path: root/Objects
diff options
context:
space:
mode:
authorEric Snow <ericsnowcurrently@gmail.com>2016-09-05 21:50:11 (GMT)
committerEric Snow <ericsnowcurrently@gmail.com>2016-09-05 21:50:11 (GMT)
commit92a6c170e6897ee98c36a3a9087b1a7d3e054d2b (patch)
tree59cbc717ac59e802529bc3ad4e61de161b66ac68 /Objects
parent45659861380a9cd9e41ea002d4de92519ffb3422 (diff)
downloadcpython-92a6c170e6897ee98c36a3a9087b1a7d3e054d2b.zip
cpython-92a6c170e6897ee98c36a3a9087b1a7d3e054d2b.tar.gz
cpython-92a6c170e6897ee98c36a3a9087b1a7d3e054d2b.tar.bz2
Issue #24254: Preserve class attribute definition order.
Diffstat (limited to 'Objects')
-rw-r--r--Objects/odictobject.c15
-rw-r--r--Objects/typeobject.c66
2 files changed, 80 insertions, 1 deletions
diff --git a/Objects/odictobject.c b/Objects/odictobject.c
index 14be1cd..f056074 100644
--- a/Objects/odictobject.c
+++ b/Objects/odictobject.c
@@ -1762,6 +1762,21 @@ PyODict_DelItem(PyObject *od, PyObject *key)
return _PyDict_DelItem_KnownHash(od, key, hash);
}
+PyObject *
+_PyODict_KeysAsTuple(PyObject *od) {
+ Py_ssize_t i = 0;
+ _ODictNode *node;
+ PyObject *keys = PyTuple_New(PyODict_Size(od));
+ if (keys == NULL)
+ return NULL;
+ _odict_FOREACH((PyODictObject *)od, node) {
+ Py_INCREF(_odictnode_KEY(node));
+ PyTuple_SET_ITEM(keys, i, _odictnode_KEY(node));
+ i++;
+ }
+ return keys;
+}
+
/* -------------------------------------------
* The OrderedDict views (keys/values/items)
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 5f0db2b..6cffb4e 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -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__);
@@ -489,6 +490,23 @@ type_set_module(PyTypeObject *type, PyObject *value, void *context)
}
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)
{
PyObject *mod = NULL;
@@ -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();
}
/*