diff options
author | Eric Snow <ericsnowcurrently@gmail.com> | 2016-09-05 21:50:11 (GMT) |
---|---|---|
committer | Eric Snow <ericsnowcurrently@gmail.com> | 2016-09-05 21:50:11 (GMT) |
commit | 92a6c170e6897ee98c36a3a9087b1a7d3e054d2b (patch) | |
tree | 59cbc717ac59e802529bc3ad4e61de161b66ac68 /Objects | |
parent | 45659861380a9cd9e41ea002d4de92519ffb3422 (diff) | |
download | cpython-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.c | 15 | ||||
-rw-r--r-- | Objects/typeobject.c | 66 |
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(); } /* |