diff -r 377bd6e0f61c Include/dictobject.h --- a/Include/dictobject.h Mon Sep 09 22:40:13 2013 -0700 +++ b/Include/dictobject.h Wed Sep 11 11:28:11 2013 +0300 @@ -116,6 +116,15 @@ PyAPI_FUNC(void) _PyDict_DebugMallocStats(FILE *out); #endif +#ifndef Py_LIMITED_API +PyAPI_DATA(PyTypeObject) _PyTransformDict_Type; +#define _PyTransformDict_Check(op) \ + PyObject_IsInstance((PyObject *)(op), \ + (PyObject *)&_PyTransformDict_Type) +#define _PyTransformDict_CheckExact(op) (Py_TYPE(op) == &_PyTransformDict_Type) +PyAPI_FUNC(PyObject *) _PyTransformDict_New(PyObject *keyfunc); +#endif + #ifdef __cplusplus } #endif diff -r 377bd6e0f61c Lib/collections/__init__.py --- a/Lib/collections/__init__.py Mon Sep 09 22:40:13 2013 -0700 +++ b/Lib/collections/__init__.py Wed Sep 11 11:28:11 2013 +0300 @@ -1,5 +1,5 @@ __all__ = ['deque', 'defaultdict', 'namedtuple', 'UserDict', 'UserList', - 'UserString', 'Counter', 'OrderedDict', 'ChainMap'] + 'UserString', 'Counter', 'OrderedDict', 'ChainMap', 'TransformDict'] # For backwards compatibility, continue to make the collections ABCs # available through the collections module. @@ -861,6 +861,10 @@ self.maps[0].clear() +from _collections import TransformDict +transformdict = TransformDict + + ################################################################################ ### UserDict ################################################################################ diff -r 377bd6e0f61c Modules/_collectionsmodule.c --- a/Modules/_collectionsmodule.c Mon Sep 09 22:40:13 2013 -0700 +++ b/Modules/_collectionsmodule.c Wed Sep 11 11:28:11 2013 +0300 @@ -1889,5 +1889,10 @@ Py_INCREF(&dequereviter_type); PyModule_AddObject(m, "_deque_reverse_iterator", (PyObject *)&dequereviter_type); + if (PyType_Ready(&_PyTransformDict_Type) < 0) + return NULL; + Py_INCREF(&_PyTransformDict_Type); + PyModule_AddObject(m, "TransformDict", (PyObject *)&_PyTransformDict_Type); + return m; } diff -r 377bd6e0f61c Objects/dictobject.c --- a/Objects/dictobject.c Mon Sep 09 22:40:13 2013 -0700 +++ b/Objects/dictobject.c Wed Sep 11 11:28:11 2013 +0300 @@ -218,6 +218,8 @@ Py_hash_t hash, PyObject ***value_addr); static PyDictKeyEntry *lookdict_split(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject ***value_addr); +static PyDictKeyEntry *lookdict_transform(PyDictObject *mp, PyObject *key, + Py_hash_t hash, PyObject ***value_addr); static int dictresize(PyDictObject *mp, Py_ssize_t minused); @@ -697,7 +699,8 @@ PyObject *key, *value; assert(PyDict_Check(dict)); /* Shortcut */ - if (((PyDictObject *)dict)->ma_keys->dk_lookup != lookdict) + if (((PyDictObject *)dict)->ma_keys->dk_lookup != lookdict && + ((PyDictObject *)dict)->ma_keys->dk_lookup != lookdict_transform) return 1; while (PyDict_Next(dict, &pos, &key, &value)) if (!PyUnicode_Check(key)) @@ -765,7 +768,8 @@ PyDictKeyEntry *ep; assert(key != NULL); - if (!PyUnicode_CheckExact(key)) + if (!PyUnicode_CheckExact(key) && + mp->ma_keys->dk_lookup != lookdict_transform) mp->ma_keys->dk_lookup = lookdict; i = hash & mask; ep = &ep0[i]; @@ -848,7 +852,9 @@ *value_addr = value; } assert(ep->me_key != NULL && ep->me_key != dummy); - assert(PyUnicode_CheckExact(key) || mp->ma_keys->dk_lookup == lookdict); + assert(PyUnicode_CheckExact(key) || + mp->ma_keys->dk_lookup == lookdict || + mp->ma_keys->dk_lookup == lookdict_transform); return 0; } @@ -877,7 +883,9 @@ assert(value != NULL); assert(key != NULL); assert(key != dummy); - assert(PyUnicode_CheckExact(key) || k->dk_lookup == lookdict); + assert(PyUnicode_CheckExact(key) || + k->dk_lookup == lookdict || + k->dk_lookup == lookdict_transform); i = hash & mask; ep = &ep0[i]; for (perturb = hash; ep->me_key != NULL; perturb >>= PERTURB_SHIFT) { @@ -925,12 +933,16 @@ mp->ma_keys = oldkeys; return -1; } - if (oldkeys->dk_lookup == lookdict) - mp->ma_keys->dk_lookup = lookdict; + if (oldkeys->dk_lookup == lookdict || + oldkeys->dk_lookup == lookdict_transform) + mp->ma_keys->dk_lookup = oldkeys->dk_lookup; oldsize = DK_SIZE(oldkeys); mp->ma_values = NULL; /* If empty then nothing to copy so just return */ if (oldsize == 1) { + if (PyObject_IsInstance((PyObject *)mp, + (PyObject *)&_PyTransformDict_Type)) + mp->ma_keys->dk_lookup = lookdict_transform; assert(oldkeys == Py_EMPTY_KEYS); DK_DECREF(oldkeys); return 0; @@ -1059,7 +1071,9 @@ if (!PyDict_Check(op)) return NULL; - if (!PyUnicode_CheckExact(key) || + if (mp->ma_keys->dk_lookup == lookdict_transform) + hash = (Py_hash_t)key; + else if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); @@ -1112,7 +1126,9 @@ PyErr_BadInternalCall(); return NULL; } - if (!PyUnicode_CheckExact(key) || + if (mp->ma_keys->dk_lookup == lookdict_transform) + hash = (Py_hash_t)key; + else if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); @@ -1190,7 +1206,9 @@ assert(key); assert(value); mp = (PyDictObject *)op; - if (!PyUnicode_CheckExact(key) || + if (mp->ma_keys->dk_lookup == lookdict_transform) + hash = (Py_hash_t)key; + else if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); @@ -1216,13 +1234,15 @@ return -1; } assert(key); - if (!PyUnicode_CheckExact(key) || + mp = (PyDictObject *)op; + if (mp->ma_keys->dk_lookup == lookdict_transform) + hash = (Py_hash_t)key; + else if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return -1; } - mp = (PyDictObject *)op; ep = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr); if (ep == NULL) return -1; @@ -1491,7 +1511,9 @@ PyDictKeyEntry *ep; PyObject **value_addr; - if (!PyUnicode_CheckExact(key) || + if (mp->ma_keys->dk_lookup == lookdict_transform) + hash = (Py_hash_t)key; + else if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) @@ -1915,7 +1937,8 @@ return -1; } mp = (PyDictObject*)a; - if (PyDict_Check(b)) { + if (PyDict_Check(b) && !_PyTransformDict_Check(a) && + !_PyTransformDict_Check(b)) { other = (PyDictObject*)b; if (other == mp || other->ma_used == 0) /* a.update(a) or a.update({}); nothing to do */ @@ -2101,6 +2124,10 @@ /* can't be equal if # of entries differ */ return 0; /* Same # of entries -- check all of 'em. Exit early on any diff. */ + if (a->ma_keys->dk_lookup == lookdict_transform && + b->ma_keys->dk_lookup != lookdict_transform) { + PyDictObject *tmp = a; a = b; b = tmp; + } for (i = 0; i < DK_SIZE(a->ma_keys); i++) { PyDictKeyEntry *ep = &a->ma_keys->dk_entries[i]; PyObject *aval; @@ -2113,13 +2140,18 @@ PyObject *bval; PyObject **vaddr; PyObject *key = ep->me_key; + Py_hash_t hash; /* temporarily bump aval's refcount to ensure it stays alive until we're done with it */ Py_INCREF(aval); /* ditto for key */ Py_INCREF(key); /* reuse the known hash value */ - if ((b->ma_keys->dk_lookup)(b, key, ep->me_hash, &vaddr) == NULL) + if (b->ma_keys->dk_lookup != lookdict_transform) + hash = ep->me_hash; + else + hash = (Py_hash_t)key; + if ((b->ma_keys->dk_lookup)(b, key, hash, &vaddr) == NULL) bval = NULL; else bval = *vaddr; @@ -2167,7 +2199,9 @@ PyDictKeyEntry *ep; PyObject **value_addr; - if (!PyUnicode_CheckExact(key) || + if (mp->ma_keys->dk_lookup == lookdict_transform) + hash = (Py_hash_t)key; + else if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) @@ -2192,7 +2226,9 @@ if (!PyArg_UnpackTuple(args, "get", 1, 2, &key, &failobj)) return NULL; - if (!PyUnicode_CheckExact(key) || + if (mp->ma_keys->dk_lookup == lookdict_transform) + hash = (Py_hash_t)key; + else if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) @@ -2221,7 +2257,9 @@ PyErr_BadInternalCall(); return NULL; } - if (!PyUnicode_CheckExact(key) || + if (mp->ma_keys->dk_lookup == lookdict_transform) + hash = (Py_hash_t)key; + else if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) @@ -2291,7 +2329,9 @@ _PyErr_SetKeyError(key); return NULL; } - if (!PyUnicode_CheckExact(key) || + if (mp->ma_keys->dk_lookup == lookdict_transform) + hash = (Py_hash_t)key; + else if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) @@ -2538,7 +2578,9 @@ PyDictKeyEntry *ep; PyObject **value_addr; - if (!PyUnicode_CheckExact(key) || + if (mp->ma_keys->dk_lookup == lookdict_transform) + hash = (Py_hash_t)key; + else if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) @@ -3808,3 +3850,435 @@ 2, &PyDictDummy_Type }; +/* TransformDict implementation */ + +typedef struct { + PyDictObject dict; + PyObject *transform; +} tdictobject; + +static PyDictKeyEntry * +lookdict_transform2(PyDictObject *mp, PyObject *key, PyObject *tkey, + Py_hash_t hash, PyObject ***value_addr) +{ + size_t i; + size_t perturb; + PyDictKeyEntry *freeslot; + size_t mask; + PyDictKeyEntry *ep0; + PyDictKeyEntry *ep; + int cmp; + PyObject *startkey, *tstartkey; + PyObject *transform = ((tdictobject *)mp)->transform; + +top: + mask = DK_MASK(mp->ma_keys); + ep0 = &mp->ma_keys->dk_entries[0]; + i = (size_t)hash & mask; + ep = &ep0[i]; + if (ep->me_key == NULL || ep->me_key == key) { + *value_addr = &ep->me_value; + return ep; + } + if (ep->me_key == dummy) + freeslot = ep; + else { + /*if (ep->me_hash == hash)*/ { + startkey = ep->me_key; + Py_INCREF(startkey); + tstartkey = PyObject_CallFunctionObjArgs(transform, startkey, NULL); + if (tstartkey == NULL) + return NULL; + cmp = PyObject_RichCompareBool(tstartkey, tkey, Py_EQ); + Py_DECREF(tstartkey); + Py_DECREF(startkey); + if (cmp < 0) + return NULL; + if (ep0 == mp->ma_keys->dk_entries && ep->me_key == startkey) { + if (cmp > 0) { + *value_addr = &ep->me_value; + return ep; + } + } + else { + /* The dict was mutated, restart */ + goto top; + } + } + freeslot = NULL; + } + + /* In the loop, me_key == dummy is by far (factor of 100s) the + least likely outcome, so test for that last. */ + for (perturb = hash; ; perturb >>= PERTURB_SHIFT) { + i = (i << 2) + i + perturb + 1; + ep = &ep0[i & mask]; + if (ep->me_key == NULL) { + if (freeslot == NULL) { + *value_addr = &ep->me_value; + return ep; + } else { + *value_addr = &freeslot->me_value; + return freeslot; + } + } + if (ep->me_key == key) { + *value_addr = &ep->me_value; + return ep; + } + if (/*ep->me_hash == hash && */ep->me_key != dummy) { + startkey = ep->me_key; + Py_INCREF(startkey); + tstartkey = PyObject_CallFunctionObjArgs(transform, startkey, NULL); + if (tstartkey == NULL) { + *value_addr = NULL; + return NULL; + } + cmp = PyObject_RichCompareBool(startkey, tkey, Py_EQ); + Py_DECREF(tstartkey); + Py_DECREF(startkey); + if (cmp < 0) { + *value_addr = NULL; + return NULL; + } + if (ep0 == mp->ma_keys->dk_entries && ep->me_key == startkey) { + if (cmp > 0) { + *value_addr = &ep->me_value; + return ep; + } + } + else { + /* The dict was mutated, restart */ + goto top; + } + } + else if (ep->me_key == dummy && freeslot == NULL) + freeslot = ep; + } + assert(0); /* NOT REACHED */ + return 0; +} + +static PyDictKeyEntry * +lookdict_transform(PyDictObject *mp, PyObject *key, + Py_hash_t hash, PyObject ***value_addr) +{ + tdictobject *d = (tdictobject *)mp; + PyObject *transform = d->transform; + PyObject *tkey; + PyDictKeyEntry *ep; + + tkey = PyObject_CallFunctionObjArgs(transform, key, NULL); + if (tkey == NULL) { + return NULL; + } + + hash = PyObject_Hash(tkey); + if (hash == -1) { + Py_DECREF(tkey); + return NULL; + } + + ep = lookdict_transform2(&d->dict, key, tkey, hash, value_addr); + Py_DECREF(tkey); + return ep; +} + +static PyObject * +tdict_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *transform; + tdictobject *d; + if (PyTuple_GET_SIZE(args) < 1) { + PyErr_SetString(PyExc_TypeError, + "TransformDict expected at least 1 arguments, got 0"); + return NULL; + } + if (PyTuple_GET_SIZE(args) > 2) { + PyErr_Format(PyExc_TypeError, + "TransformDict expected at most 2 arguments, got %zd", + PyTuple_GET_SIZE(args)); + return NULL; + } + transform = PyTuple_GET_ITEM(args, 0); + d = (tdictobject *)dict_new(type, args, kwds); + if (d != NULL && d->dict.ma_keys != Py_EMPTY_KEYS) + d->dict.ma_keys->dk_lookup = lookdict_transform; + if (d != NULL) { + Py_INCREF(transform); + Py_XDECREF(d->transform); + d->transform = transform; + } + return (PyObject *)d; +} + +PyObject * +_PyTransformDict_New(PyObject *transform) +{ + PyDictObject *mp; + PyDictKeysObject *keys = new_keys_object(PyDict_MINSIZE_COMBINED); + if (keys == NULL) + return NULL; + keys->dk_lookup = lookdict_transform; + mp = PyObject_GC_New(PyDictObject, &_PyTransformDict_Type); + if (mp == NULL) { + DK_DECREF(keys); + return NULL; + } + mp->ma_keys = keys; + mp->ma_values = NULL; + mp->ma_used = 0; + Py_INCREF(transform); + ((tdictobject *)mp)->transform = transform; + return (PyObject *)mp; +} + +PyDoc_STRVAR(tdict_copy_doc, "D.copy() -> a shallow copy of D."); + +static PyObject * +tdict_copy(tdictobject *d) +{ + /* This calls the object's class. That only works for subclasses + whose class constructor has the same signature. Subclasses that + define a different constructor signature must override copy(). + */ + + return PyObject_CallFunctionObjArgs((PyObject*)Py_TYPE(d), d->transform, d, NULL); +} + +PyDoc_STRVAR(tdict_reduce_doc, "Return state information for pickling."); + +static PyObject * +tdict_reduce(tdictobject *d) +{ + /* __reduce__ must return a 5-tuple as follows: + + - factory function + - tuple of args for the factory function + - additional state (here None) + - sequence iterator (here None) + - dictionary iterator (yielding successive (key, value) pairs + + This API is used by pickle.py and copy.py. + */ + PyObject *args; + PyObject *items; + PyObject *iter; + PyObject *result; + _Py_IDENTIFIER(items); + + args = PyTuple_Pack(1, d->transform); + if (args == NULL) + return NULL; + items = _PyObject_CallMethodId((PyObject *)d, &PyId_items, "()"); + if (items == NULL) { + Py_DECREF(args); + return NULL; + } + iter = PyObject_GetIter(items); + if (iter == NULL) { + Py_DECREF(items); + Py_DECREF(args); + return NULL; + } + result = PyTuple_Pack(5, Py_TYPE(d), args, + Py_None, Py_None, iter); + Py_DECREF(iter); + Py_DECREF(items); + Py_DECREF(args); + return result; +} + +static PyMethodDef tdict_methods[] = { + {"copy", (PyCFunction)tdict_copy, METH_NOARGS, + tdict_copy_doc}, + {"__copy__", (PyCFunction)tdict_copy, METH_NOARGS, + tdict_copy_doc}, + {"__reduce__", (PyCFunction)tdict_reduce, METH_NOARGS, + tdict_reduce_doc}, + {NULL} +}; + +static int +tdict_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + PyObject *transform, *dictargs; + if (PyTuple_GET_SIZE(args) < 1) { + PyErr_SetString(PyExc_TypeError, + "TransformDict expected at least 1 arguments, got 0"); + return -1; + } + if (PyTuple_GET_SIZE(args) > 2) { + PyErr_Format(PyExc_TypeError, + "TransformDict expected at most 2 arguments, got %zd", + PyTuple_GET_SIZE(args)); + return -1; + } + transform = PyTuple_GET_ITEM(args, 0); + dictargs = PySequence_GetSlice(args, 1, PyTuple_GET_SIZE(args)); + if (dictargs == NULL) + return -1; +// ((tdictobject *)self)->transform = transform; + return dict_update_common(self, dictargs, kwds, "collections.TransformDict"); +} + +static void +tdict_dealloc(tdictobject *d) +{ + Py_CLEAR(d->transform); + dict_dealloc((PyDictObject *)d); +} + +static PyObject * +tdict_repr(PyDictObject *mp) +{ + Py_ssize_t i; + PyObject *s, *temp, *colon = NULL; + PyObject *pieces = NULL, *result = NULL; + PyObject *key, *value; + + i = Py_ReprEnter((PyObject *)mp); + if (i != 0) { + return i > 0 ? PyUnicode_FromString("TransformDict(...)") : NULL; + } + + if (mp->ma_used == 0) { + result = PyUnicode_FromFormat("TransformDict(%R)", + ((tdictobject *)mp)->transform); + goto Done; + } + + pieces = PyList_New(0); + if (pieces == NULL) + goto Done; + + colon = PyUnicode_FromString(", "); + if (colon == NULL) + goto Done; + + /* Do repr() on each key+value pair, and insert ", " between them. + Note that repr may mutate the dict. */ + i = 0; + while (PyDict_Next((PyObject *)mp, &i, &key, &value)) { + int status; + /* Prevent repr from deleting key or value during key format. */ + Py_INCREF(key); + Py_INCREF(value); + s = PyObject_Repr(key); + PyUnicode_Append(&s, colon); + PyUnicode_AppendAndDel(&s, PyObject_Repr(value)); + Py_DECREF(key); + Py_DECREF(value); + if (s == NULL) + goto Done; + status = PyList_Append(pieces, s); + Py_DECREF(s); /* append created a new ref */ + if (status < 0) + goto Done; + } + + /* Add "collections.TransformDict(transform, {...})" decorations to the + first and last items. */ + assert(PyList_GET_SIZE(pieces) > 0); + s = PyUnicode_FromFormat("TransformDict(%R, {", + ((tdictobject *)mp)->transform); + if (s == NULL) + goto Done; + temp = PyList_GET_ITEM(pieces, 0); + PyUnicode_AppendAndDel(&s, temp); + PyList_SET_ITEM(pieces, 0, s); + if (s == NULL) + goto Done; + + s = PyUnicode_FromString("})"); + if (s == NULL) + goto Done; + temp = PyList_GET_ITEM(pieces, PyList_GET_SIZE(pieces) - 1); + PyUnicode_AppendAndDel(&temp, s); + PyList_SET_ITEM(pieces, PyList_GET_SIZE(pieces) - 1, temp); + if (temp == NULL) + goto Done; + + /* Paste them all together with "), (" between. */ + s = PyUnicode_FromString(", "); + if (s == NULL) + goto Done; + result = PyUnicode_Join(s, pieces); + Py_DECREF(s); + +Done: + Py_XDECREF(pieces); + Py_XDECREF(colon); + Py_ReprLeave((PyObject *)mp); + return result; +} + +static int +tdict_traverse(PyObject *op, visitproc visit, void *arg) +{ + Py_VISIT(((tdictobject *)op)->transform); + return dict_traverse(op, visit, arg); +} + +static int +tdict_tp_clear(PyObject *op) +{ + Py_CLEAR(((tdictobject *)op)->transform); + PyDict_Clear(op); + return 0; +} + + +PyDoc_STRVAR(tdict_doc, +"TransformDict(transform) -> new empty TransformDict\n" +"TransformDict(transform, mapping) -> new TransformDict initialized from a mapping object's\n" +" (key, value) pairs\n" +"TransformDict(transform, iterable) -> new TransformDict initialized as if via:\n" +" d = TransformDict(transform)\n" +" for k, v in iterable:\n" +" d[k] = v\n" +"TransformDict(transform, **kwargs) -> new TransformDict initialized with the name=value pairs\n" +" in the keyword argument list. For example: TransformDict(len, one=1, two=2)"); + +PyTypeObject _PyTransformDict_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "collections.TransformDict", + sizeof(tdictobject), + 0, + (destructor)tdict_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + (reprfunc)tdict_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + (reprfunc)tdict_repr, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE | Py_TPFLAGS_DICT_SUBCLASS, /* tp_flags */ + tdict_doc, /* tp_doc */ + tdict_traverse, /* tp_traverse */ + tdict_tp_clear, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + tdict_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + &PyDict_Type, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + tdict_init, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + tdict_new, /* tp_new */ + PyObject_GC_Del, /* tp_free */ +}; diff -r 377bd6e0f61c Objects/object.c --- a/Objects/object.c Mon Sep 09 22:40:13 2013 -0700 +++ b/Objects/object.c Wed Sep 11 11:28:11 2013 +0300 @@ -1717,6 +1717,9 @@ if (PyType_Ready(&PyDict_Type) < 0) Py_FatalError("Can't initialize dict type"); + if (PyType_Ready(&_PyTransformDict_Type) < 0) + Py_FatalError("Can't initialize TransformDict type"); + if (PyType_Ready(&PySet_Type) < 0) Py_FatalError("Can't initialize set type");