Index: Python/codecs.c =================================================================== --- Python/codecs.c (revision 71083) +++ Python/codecs.c (working copy) @@ -838,9 +838,27 @@ interp->codec_error_registry = PyDict_New(); if (interp->codec_error_registry) { + /* This is the name of a module where the error functions are + accessible; the name will be available via the __module__ attribute + of the functions. We do this to be nicer with modules relying on the + attribute (e.g. pickle) to find where functions are defined. + + We do not "_codecs", because _codecs does not export the *_errors + functions on the module level; however, "codecs" does, so we use + that instead. + + N.B: It is not a fatal error if the call to PyUnicode_FromString + fails. In that case, codecs_module_name will simply be NULL and + PyCFunction_NewEx will handle it fine. Also, this explains why we + use Py_XDECREF, instead of Py_DECREF, at the end. + */ + PyObject *codecs_module_name = PyUnicode_FromString("codecs"); + for (i = 0; i < sizeof(methods)/sizeof(methods[0]); ++i) { - PyObject *func = PyCFunction_New(&methods[i].def, NULL); + PyObject *func; int res; + + func = PyCFunction_NewEx(&methods[i].def, NULL, codecs_module_name); if (!func) Py_FatalError("can't initialize codec error registry"); res = PyCodec_RegisterError(methods[i].name, func); @@ -848,8 +866,10 @@ if (res) Py_FatalError("can't initialize codec error registry"); } + Py_XDECREF(codecs_module_name); } + if (interp->codec_search_path == NULL || interp->codec_search_cache == NULL || interp->codec_error_registry == NULL) Index: Modules/_pickle.c =================================================================== --- Modules/_pickle.c (revision 71084) +++ Modules/_pickle.c (working copy) @@ -1684,10 +1684,74 @@ return -1; } +/* This is a variant of batch_dict() above that specializes for dicts, with no + * support for dict subclasses. Like batch_dict(), we batch up chunks of + * MARK key value ... key value SETITEMS + * opcode sequences. Calling code should have arranged to first create an + * empty dict, or dict-like object, for the SETITEMS to operate on. + * Returns 0 on success, -1 on error. + * + * Note that this currently doesn't work for protocol 0. + */ static int +batch_dict_exact(PicklerObject *self, PyObject *obj) +{ + PyObject *key = NULL; + PyObject *value = NULL; + int i; + Py_ssize_t dict_size, ppos = 0; + + const char mark_op = MARK; + const char setitem_op = SETITEM; + const char setitems_op = SETITEMS; + + assert(obj != NULL); + assert(self->proto > 0); + + dict_size = PyDict_Size(obj); + if (dict_size == 1) { + /* Only one item to save */ + if (!PyDict_Next(obj, &ppos, &key, &value)) + return -1; /* This should never happen. */ + + if (save(self, key, 0) < 0) + return -1; + if (save(self, value, 0) < 0) + return -1; + if (pickler_write(self, &setitem_op, 1) < 0) + return -1; + + return 0; /* and we're done! */ + } + + /* More than one item to save; write them in batches of BATCHSIZE. */ + do { + i = 0; + if (pickler_write(self, &mark_op, 1) < 0) + return -1; + while (PyDict_Next(obj, &ppos, &key, &value)) { + if (save(self, key, 0) < 0) + return -1; + if (save(self, value, 0) < 0) + return -1; + if (++i == BATCHSIZE) + break; + } + if (pickler_write(self, &setitems_op, 1) < 0) + return -1; + if (PyDict_Size(obj) != dict_size) { + PyErr_Format(PyExc_RuntimeError, + "dictionary changed size during iteration"); + return -1; + } + } while (i == BATCHSIZE); + + return 0; +} + +static int save_dict(PicklerObject *self, PyObject *obj) { - PyObject *items, *iter; char header[3]; int len; int status = 0; @@ -1717,16 +1781,31 @@ goto error; if (len != 0) { - /* Save the dict items. */ - items = PyObject_CallMethod(obj, "items", "()"); - if (items == NULL) - goto error; - iter = PyObject_GetIter(items); - Py_DECREF(items); - if (iter == NULL) - goto error; - status = batch_dict(self, iter); - Py_DECREF(iter); + if (PyDict_CheckExact(obj) && self->proto > 0) { + /* We can take certain shortcuts if we know this is a dict and + not a dict subclass. */ + if (Py_EnterRecursiveCall(" while pickling an object") == 0) { + status = batch_dict_exact(self, obj); + Py_LeaveRecursiveCall(); + } + } + else { + PyObject *items, *iter; + + /* Save the dict items. */ + items = PyObject_CallMethod(obj, "items", "()"); + if (items == NULL) + goto error; + iter = PyObject_GetIter(items); + Py_DECREF(items); + if (iter == NULL) + goto error; + if (Py_EnterRecursiveCall(" while pickling an object") == 0) { + status = batch_dict(self, iter); + Py_LeaveRecursiveCall(); + } + Py_DECREF(iter); + } } if (0) { @@ -2194,6 +2273,8 @@ PyObject *memo_key = NULL; int status = 0; + assert(obj != NULL); + if (Py_EnterRecursiveCall(" while pickling an object") < 0) return -1;