diff options
Diffstat (limited to 'Modules/_pickle.c')
-rw-r--r-- | Modules/_pickle.c | 306 |
1 files changed, 184 insertions, 122 deletions
diff --git a/Modules/_pickle.c b/Modules/_pickle.c index fc5f87127e..5ba559948f 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -319,6 +319,7 @@ typedef struct PicklerObject { objects to support self-referential objects pickling. */ PyObject *pers_func; /* persistent_id() method, can be NULL */ + PyObject *dispatch_table; /* private dispatch_table, can be NULL */ PyObject *arg; PyObject *write; /* write() method of the output stream. */ @@ -605,7 +606,7 @@ PyMemoTable_Set(PyMemoTable *self, PyObject *key, Py_ssize_t value) /*************************************************************************/ /* Helpers for creating the argument tuple passed to functions. This has the - performance advantage of calling PyTuple_New() only once. + performance advantage of calling PyTuple_New() only once. XXX(avassalotti): Inline directly in _Pickler_FastCall() and _Unpickler_FastCall(). */ @@ -764,6 +765,7 @@ _Pickler_New(void) return NULL; self->pers_func = NULL; + self->dispatch_table = NULL; self->arg = NULL; self->write = NULL; self->proto = 0; @@ -813,7 +815,7 @@ _Pickler_SetProtocol(PicklerObject *self, PyObject *proto_obj, fix_imports = PyObject_IsTrue(fix_imports_obj); if (fix_imports == -1) return -1; - + self->proto = proto; self->bin = proto > 0; self->fix_imports = fix_imports && proto < 3; @@ -826,8 +828,9 @@ _Pickler_SetProtocol(PicklerObject *self, PyObject *proto_obj, static int _Pickler_SetOutputStream(PicklerObject *self, PyObject *file) { + _Py_IDENTIFIER(write); assert(file != NULL); - self->write = PyObject_GetAttrString(file, "write"); + self->write = _PyObject_GetAttrId(file, &PyId_write); if (self->write == NULL) { if (PyErr_ExceptionMatches(PyExc_AttributeError)) PyErr_SetString(PyExc_TypeError, @@ -909,7 +912,7 @@ _Unpickler_ReadFromFile(UnpicklerObject *self, Py_ssize_t n) Py_ssize_t read_size, prefetched_size = 0; assert(self->read != NULL); - + if (_Unpickler_SkipConsumed(self) < 0) return -1; @@ -1037,7 +1040,7 @@ _Unpickler_Readline(UnpicklerObject *self, char **result) self->next_read_idx = num_read; return _Unpickler_CopyLine(self, self->input_buffer, num_read, result); } - + /* If we get here, we've run off the end of the input string. Return the remaining string and let the caller figure it out. */ *result = self->input_buffer + self->next_read_idx; @@ -1173,15 +1176,19 @@ _Unpickler_New(void) static int _Unpickler_SetInputStream(UnpicklerObject *self, PyObject *file) { - self->peek = PyObject_GetAttrString(file, "peek"); + _Py_IDENTIFIER(peek); + _Py_IDENTIFIER(read); + _Py_IDENTIFIER(readline); + + self->peek = _PyObject_GetAttrId(file, &PyId_peek); if (self->peek == NULL) { if (PyErr_ExceptionMatches(PyExc_AttributeError)) PyErr_Clear(); else return -1; } - self->read = PyObject_GetAttrString(file, "read"); - self->readline = PyObject_GetAttrString(file, "readline"); + self->read = _PyObject_GetAttrId(file, &PyId_read); + self->readline = _PyObject_GetAttrId(file, &PyId_readline); if (self->readline == NULL || self->read == NULL) { if (PyErr_ExceptionMatches(PyExc_AttributeError)) PyErr_SetString(PyExc_TypeError, @@ -1543,7 +1550,10 @@ save_long(PicklerObject *self, PyObject *obj) PyErr_Clear(); } else - return save_int(self, val); +#if SIZEOF_LONG > 4 + if (val <= 0x7fffffffL && val >= -0x80000000L) +#endif + return save_int(self, val); if (self->proto >= 2) { /* Linear-time pickling. */ @@ -1598,7 +1608,7 @@ save_long(PicklerObject *self, PyObject *obj) * bits. */ if (sign < 0 && - nbytes > 1 && + nbytes > 1 && pdata[nbytes - 1] == 0xff && (pdata[nbytes - 2] & 0x80) != 0) { nbytes--; @@ -1664,7 +1674,7 @@ save_float(PicklerObject *self, PyObject *obj) return -1; if (_Pickler_Write(self, pdata, 9) < 0) return -1; - } + } else { int result = -1; char *buf = NULL; @@ -1798,90 +1808,65 @@ save_bytes(PicklerObject *self, PyObject *obj) /* A copy of PyUnicode_EncodeRawUnicodeEscape() that also translates backslash and newline characters to \uXXXX escapes. */ static PyObject * -raw_unicode_escape(const Py_UNICODE *s, Py_ssize_t size) +raw_unicode_escape(PyObject *obj) { PyObject *repr, *result; char *p; - char *q; + Py_ssize_t i, size, expandsize; + void *data; + unsigned int kind; - static const char *hexdigits = "0123456789abcdef"; + if (PyUnicode_READY(obj)) + return NULL; + + size = PyUnicode_GET_LENGTH(obj); + data = PyUnicode_DATA(obj); + kind = PyUnicode_KIND(obj); + if (kind == PyUnicode_4BYTE_KIND) + expandsize = 10; + else + expandsize = 6; -#ifdef Py_UNICODE_WIDE - const Py_ssize_t expandsize = 10; -#else - const Py_ssize_t expandsize = 6; -#endif - if (size > PY_SSIZE_T_MAX / expandsize) return PyErr_NoMemory(); - repr = PyByteArray_FromStringAndSize(NULL, expandsize * size); if (repr == NULL) return NULL; if (size == 0) goto done; - p = q = PyByteArray_AS_STRING(repr); - while (size-- > 0) { - Py_UNICODE ch = *s++; -#ifdef Py_UNICODE_WIDE + p = PyByteArray_AS_STRING(repr); + for (i=0; i < size; i++) { + Py_UCS4 ch = PyUnicode_READ(kind, data, i); /* Map 32-bit characters to '\Uxxxxxxxx' */ if (ch >= 0x10000) { *p++ = '\\'; *p++ = 'U'; - *p++ = hexdigits[(ch >> 28) & 0xf]; - *p++ = hexdigits[(ch >> 24) & 0xf]; - *p++ = hexdigits[(ch >> 20) & 0xf]; - *p++ = hexdigits[(ch >> 16) & 0xf]; - *p++ = hexdigits[(ch >> 12) & 0xf]; - *p++ = hexdigits[(ch >> 8) & 0xf]; - *p++ = hexdigits[(ch >> 4) & 0xf]; - *p++ = hexdigits[ch & 15]; + *p++ = Py_hexdigits[(ch >> 28) & 0xf]; + *p++ = Py_hexdigits[(ch >> 24) & 0xf]; + *p++ = Py_hexdigits[(ch >> 20) & 0xf]; + *p++ = Py_hexdigits[(ch >> 16) & 0xf]; + *p++ = Py_hexdigits[(ch >> 12) & 0xf]; + *p++ = Py_hexdigits[(ch >> 8) & 0xf]; + *p++ = Py_hexdigits[(ch >> 4) & 0xf]; + *p++ = Py_hexdigits[ch & 15]; } - else -#else - /* Map UTF-16 surrogate pairs to '\U00xxxxxx' */ - if (ch >= 0xD800 && ch < 0xDC00) { - Py_UNICODE ch2; - Py_UCS4 ucs; - - ch2 = *s++; - size--; - if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) { - ucs = (((ch & 0x03FF) << 10) | (ch2 & 0x03FF)) + 0x00010000; - *p++ = '\\'; - *p++ = 'U'; - *p++ = hexdigits[(ucs >> 28) & 0xf]; - *p++ = hexdigits[(ucs >> 24) & 0xf]; - *p++ = hexdigits[(ucs >> 20) & 0xf]; - *p++ = hexdigits[(ucs >> 16) & 0xf]; - *p++ = hexdigits[(ucs >> 12) & 0xf]; - *p++ = hexdigits[(ucs >> 8) & 0xf]; - *p++ = hexdigits[(ucs >> 4) & 0xf]; - *p++ = hexdigits[ucs & 0xf]; - continue; - } - /* Fall through: isolated surrogates are copied as-is */ - s--; - size++; - } -#endif /* Map 16-bit characters to '\uxxxx' */ - if (ch >= 256 || ch == '\\' || ch == '\n') { + else if (ch >= 256 || ch == '\\' || ch == '\n') { *p++ = '\\'; *p++ = 'u'; - *p++ = hexdigits[(ch >> 12) & 0xf]; - *p++ = hexdigits[(ch >> 8) & 0xf]; - *p++ = hexdigits[(ch >> 4) & 0xf]; - *p++ = hexdigits[ch & 15]; + *p++ = Py_hexdigits[(ch >> 12) & 0xf]; + *p++ = Py_hexdigits[(ch >> 8) & 0xf]; + *p++ = Py_hexdigits[(ch >> 4) & 0xf]; + *p++ = Py_hexdigits[ch & 15]; } /* Copy everything else as-is */ else *p++ = (char) ch; } - size = p - q; + size = p - PyByteArray_AS_STRING(repr); - done: +done: result = PyBytes_FromStringAndSize(PyByteArray_AS_STRING(repr), size); Py_DECREF(repr); return result; @@ -1896,9 +1881,7 @@ save_unicode(PicklerObject *self, PyObject *obj) if (self->bin) { char pdata[5]; - encoded = PyUnicode_EncodeUTF8(PyUnicode_AS_UNICODE(obj), - PyUnicode_GET_SIZE(obj), - "surrogatepass"); + encoded = PyUnicode_AsEncodedString(obj, "utf-8", "surrogatepass"); if (encoded == NULL) goto error; @@ -1924,8 +1907,7 @@ save_unicode(PicklerObject *self, PyObject *obj) else { const char unicode_op = UNICODE; - encoded = raw_unicode_escape(PyUnicode_AS_UNICODE(obj), - PyUnicode_GET_SIZE(obj)); + encoded = raw_unicode_escape(obj); if (encoded == NULL) goto error; @@ -2544,7 +2526,9 @@ save_dict(PicklerObject *self, PyObject *obj) status = batch_dict_exact(self, obj); Py_LeaveRecursiveCall(); } else { - items = PyObject_CallMethod(obj, "items", "()"); + _Py_IDENTIFIER(items); + + items = _PyObject_CallMethodId(obj, &PyId_items, "()"); if (items == NULL) goto error; iter = PyObject_GetIter(items); @@ -2828,6 +2812,30 @@ save_global(PicklerObject *self, PyObject *obj, PyObject *name) } static int +save_ellipsis(PicklerObject *self, PyObject *obj) +{ + PyObject *str = PyUnicode_FromString("Ellipsis"); + int res; + if (str == NULL) + return -1; + res = save_global(self, Py_Ellipsis, str); + Py_DECREF(str); + return res; +} + +static int +save_notimplemented(PicklerObject *self, PyObject *obj) +{ + PyObject *str = PyUnicode_FromString("NotImplemented"); + int res; + if (str == NULL) + return -1; + res = save_global(self, Py_NotImplemented, str); + Py_DECREF(str); + return res; +} + +static int save_pers(PicklerObject *self, PyObject *obj, PyObject *func) { PyObject *pid = NULL; @@ -2881,6 +2889,28 @@ save_pers(PicklerObject *self, PyObject *obj, PyObject *func) return status; } +static PyObject * +get_class(PyObject *obj) +{ + PyObject *cls; + static PyObject *str_class; + + if (str_class == NULL) { + str_class = PyUnicode_InternFromString("__class__"); + if (str_class == NULL) + return NULL; + } + cls = PyObject_GetAttr(obj, str_class); + if (cls == NULL) { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); + cls = (PyObject *) Py_TYPE(obj); + Py_INCREF(cls); + } + } + return cls; +} + /* We're saving obj, and args is the 2-thru-5 tuple returned by the * appropriate __reduce__ method for obj. */ @@ -2946,17 +2976,18 @@ save_reduce(PicklerObject *self, PyObject *args, PyObject *obj) /* Protocol 2 special case: if callable's name is __newobj__, use NEWOBJ. */ if (use_newobj) { - static PyObject *newobj_str = NULL; - PyObject *name_str; + static PyObject *newobj_str = NULL, *name_str = NULL; + PyObject *name; if (newobj_str == NULL) { newobj_str = PyUnicode_InternFromString("__newobj__"); - if (newobj_str == NULL) + name_str = PyUnicode_InternFromString("__name__"); + if (newobj_str == NULL || name_str == NULL) return -1; } - name_str = PyObject_GetAttrString(callable, "__name__"); - if (name_str == NULL) { + name = PyObject_GetAttr(callable, name_str); + if (name == NULL) { if (PyErr_ExceptionMatches(PyExc_AttributeError)) PyErr_Clear(); else @@ -2964,9 +2995,9 @@ save_reduce(PicklerObject *self, PyObject *args, PyObject *obj) use_newobj = 0; } else { - use_newobj = PyUnicode_Check(name_str) && - PyUnicode_Compare(name_str, newobj_str) == 0; - Py_DECREF(name_str); + use_newobj = PyUnicode_Check(name) && + PyUnicode_Compare(name, newobj_str) == 0; + Py_DECREF(name); } } if (use_newobj) { @@ -2982,20 +3013,14 @@ save_reduce(PicklerObject *self, PyObject *args, PyObject *obj) } cls = PyTuple_GET_ITEM(argtup, 0); - if (!PyObject_HasAttrString(cls, "__new__")) { + if (!PyType_Check(cls)) { PyErr_SetString(PicklingError, "args[0] from " - "__newobj__ args has no __new__"); + "__newobj__ args is not a type"); return -1; } if (obj != NULL) { - obj_class = PyObject_GetAttrString(obj, "__class__"); - if (obj_class == NULL) { - if (PyErr_ExceptionMatches(PyExc_AttributeError)) - PyErr_Clear(); - else - return -1; - } + obj_class = get_class(obj); p = obj_class != cls; /* true iff a problem */ Py_DECREF(obj_class); if (p) { @@ -3069,7 +3094,7 @@ save_reduce(PicklerObject *self, PyObject *args, PyObject *obj) return -1; if (state) { - if (save(self, state, 0) < 0 || + if (save(self, state, 0) < 0 || _Pickler_Write(self, &build_op, 1) < 0) return -1; } @@ -3113,6 +3138,14 @@ save(PicklerObject *self, PyObject *obj, int pers_save) status = save_none(self, obj); goto done; } + else if (obj == Py_Ellipsis) { + status = save_ellipsis(self, obj); + goto done; + } + else if (obj == Py_NotImplemented) { + status = save_notimplemented(self, obj); + goto done; + } else if (obj == Py_False || obj == Py_True) { status = save_bool(self, obj); goto done; @@ -3177,17 +3210,24 @@ save(PicklerObject *self, PyObject *obj, int pers_save) /* XXX: This part needs some unit tests. */ /* Get a reduction callable, and call it. This may come from - * copyreg.dispatch_table, the object's __reduce_ex__ method, - * or the object's __reduce__ method. + * self.dispatch_table, copyreg.dispatch_table, the object's + * __reduce_ex__ method, or the object's __reduce__ method. */ - reduce_func = PyDict_GetItem(dispatch_table, (PyObject *)type); + if (self->dispatch_table == NULL) { + reduce_func = PyDict_GetItem(dispatch_table, (PyObject *)type); + /* PyDict_GetItem() unlike PyObject_GetItem() and + PyObject_GetAttr() returns a borrowed ref */ + Py_XINCREF(reduce_func); + } else { + reduce_func = PyObject_GetItem(self->dispatch_table, (PyObject *)type); + if (reduce_func == NULL) { + if (PyErr_ExceptionMatches(PyExc_KeyError)) + PyErr_Clear(); + else + goto error; + } + } if (reduce_func != NULL) { - /* Here, the reference count of the reduce_func object returned by - PyDict_GetItem needs to be increased to be consistent with the one - returned by PyObject_GetAttr. This is allow us to blindly DECREF - reduce_func at the end of the save() routine. - */ - Py_INCREF(reduce_func); Py_INCREF(obj); reduce_value = _Pickler_FastCall(self, reduce_func, obj); } @@ -3323,7 +3363,7 @@ Pickler_dump(PicklerObject *self, PyObject *args) Developers often forget to call __init__() in their subclasses, which would trigger a segfault without this check. */ if (self->write == NULL) { - PyErr_Format(PicklingError, + PyErr_Format(PicklingError, "Pickler.__init__() was not called by %s.__init__()", Py_TYPE(self)->tp_name); return NULL; @@ -3360,6 +3400,7 @@ Pickler_dealloc(PicklerObject *self) Py_XDECREF(self->output_buffer); Py_XDECREF(self->write); Py_XDECREF(self->pers_func); + Py_XDECREF(self->dispatch_table); Py_XDECREF(self->arg); Py_XDECREF(self->fast_memo); @@ -3373,6 +3414,7 @@ Pickler_traverse(PicklerObject *self, visitproc visit, void *arg) { Py_VISIT(self->write); Py_VISIT(self->pers_func); + Py_VISIT(self->dispatch_table); Py_VISIT(self->arg); Py_VISIT(self->fast_memo); return 0; @@ -3384,6 +3426,7 @@ Pickler_clear(PicklerObject *self) Py_CLEAR(self->output_buffer); Py_CLEAR(self->write); Py_CLEAR(self->pers_func); + Py_CLEAR(self->dispatch_table); Py_CLEAR(self->arg); Py_CLEAR(self->fast_memo); @@ -3427,6 +3470,8 @@ Pickler_init(PicklerObject *self, PyObject *args, PyObject *kwds) PyObject *file; PyObject *proto_obj = NULL; PyObject *fix_imports = Py_True; + _Py_IDENTIFIER(persistent_id); + _Py_IDENTIFIER(dispatch_table); if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OO:Pickler", kwlist, &file, &proto_obj, &fix_imports)) @@ -3462,12 +3507,19 @@ Pickler_init(PicklerObject *self, PyObject *args, PyObject *kwds) self->fast_nesting = 0; self->fast_memo = NULL; self->pers_func = NULL; - if (PyObject_HasAttrString((PyObject *)self, "persistent_id")) { - self->pers_func = PyObject_GetAttrString((PyObject *)self, - "persistent_id"); + if (_PyObject_HasAttrId((PyObject *)self, &PyId_persistent_id)) { + self->pers_func = _PyObject_GetAttrId((PyObject *)self, + &PyId_persistent_id); if (self->pers_func == NULL) return -1; } + self->dispatch_table = NULL; + if (_PyObject_HasAttrId((PyObject *)self, &PyId_dispatch_table)) { + self->dispatch_table = _PyObject_GetAttrId((PyObject *)self, + &PyId_dispatch_table); + if (self->dispatch_table == NULL) + return -1; + } return 0; } @@ -3749,6 +3801,7 @@ Pickler_set_persid(PicklerObject *self, PyObject *value) static PyMemberDef Pickler_members[] = { {"bin", T_INT, offsetof(PicklerObject, bin)}, {"fast", T_INT, offsetof(PicklerObject, fast)}, + {"dispatch_table", T_OBJECT_EX, offsetof(PicklerObject, dispatch_table)}, {NULL} }; @@ -3803,7 +3856,7 @@ static PyTypeObject Pickler_Type = { 0, /*tp_is_gc*/ }; -/* Temporary helper for calling self.find_class(). +/* Temporary helper for calling self.find_class(). XXX: It would be nice to able to avoid Python function call overhead, by using directly the C version of find_class(), when find_class() is not @@ -3813,8 +3866,10 @@ static PyTypeObject Pickler_Type = { static PyObject * find_class(UnpicklerObject *self, PyObject *module_name, PyObject *global_name) { - return PyObject_CallMethod((PyObject *)self, "find_class", "OO", - module_name, global_name); + _Py_IDENTIFIER(find_class); + + return _PyObject_CallMethodId((PyObject *)self, &PyId_find_class, "OO", + module_name, global_name); } static Py_ssize_t @@ -3856,7 +3911,7 @@ load_int(UnpicklerObject *self) return bad_readline(); errno = 0; - /* XXX: Should the base argument of strtol() be explicitly set to 10? + /* XXX: Should the base argument of strtol() be explicitly set to 10? XXX(avassalotti): Should this uses PyOS_strtol()? */ x = strtol(s, &endptr, 0); @@ -4221,7 +4276,7 @@ load_binstring(UnpicklerObject *self) x = calc_binint(s, 4); if (x < 0) { - PyErr_SetString(UnpicklingError, + PyErr_SetString(UnpicklingError, "BINSTRING pickle has negative byte count"); return -1; } @@ -4418,16 +4473,19 @@ static PyObject * instantiate(PyObject *cls, PyObject *args) { PyObject *result = NULL; + _Py_IDENTIFIER(__getinitargs__); /* Caller must assure args are a tuple. Normally, args come from Pdata_poptuple which packs objects from the top of the stack into a newly created tuple. */ assert(PyTuple_Check(args)); if (Py_SIZE(args) > 0 || !PyType_Check(cls) || - PyObject_HasAttrString(cls, "__getinitargs__")) { + _PyObject_HasAttrId(cls, &PyId___getinitargs__)) { result = PyObject_CallObject(cls, args); } else { - result = PyObject_CallMethod(cls, "__new__", "O", cls); + _Py_IDENTIFIER(__new__); + + result = _PyObject_CallMethodId(cls, &PyId___new__, "O", cls); } return result; } @@ -4968,8 +5026,9 @@ do_append(UnpicklerObject *self, Py_ssize_t x) } else { PyObject *append_func; + _Py_IDENTIFIER(append); - append_func = PyObject_GetAttrString(list, "append"); + append_func = _PyObject_GetAttrId(list, &PyId_append); if (append_func == NULL) return -1; for (i = x; i < len; i++) { @@ -5015,7 +5074,7 @@ do_setitems(UnpicklerObject *self, Py_ssize_t x) return stack_underflow(); if (len == x) /* nothing to do */ return 0; - if ((len - x) % 2 != 0) { + if ((len - x) % 2 != 0) { /* Currupt or hostile pickle -- we never write one like this. */ PyErr_SetString(UnpicklingError, "odd number of items for SETITEMS"); return -1; @@ -5056,6 +5115,7 @@ load_build(UnpicklerObject *self) PyObject *state, *inst, *slotstate; PyObject *setstate; int status = 0; + _Py_IDENTIFIER(__setstate__); /* Stack is ... instance, state. We want to leave instance at * the stack top, possibly mutated via instance.__setstate__(state). @@ -5069,7 +5129,7 @@ load_build(UnpicklerObject *self) inst = self->stack->data[Py_SIZE(self->stack) - 1]; - setstate = PyObject_GetAttrString(inst, "__setstate__"); + setstate = _PyObject_GetAttrId(inst, &PyId___setstate__); if (setstate == NULL) { if (PyErr_ExceptionMatches(PyExc_AttributeError)) PyErr_Clear(); @@ -5112,12 +5172,13 @@ load_build(UnpicklerObject *self) PyObject *dict; PyObject *d_key, *d_value; Py_ssize_t i; + _Py_IDENTIFIER(__dict__); if (!PyDict_Check(state)) { PyErr_SetString(UnpicklingError, "state is not a dictionary"); goto error; } - dict = PyObject_GetAttrString(inst, "__dict__"); + dict = _PyObject_GetAttrId(inst, &PyId___dict__); if (dict == NULL) goto error; @@ -5372,7 +5433,7 @@ Unpickler_load(UnpicklerObject *self) not call Unpickler.__init__(). Here, we simply ensure that self->read is not NULL. */ if (self->read == NULL) { - PyErr_Format(UnpicklingError, + PyErr_Format(UnpicklingError, "Unpickler.__init__() was not called by %s.__init__()", Py_TYPE(self)->tp_name); return NULL; @@ -5474,7 +5535,7 @@ Unpickler_find_class(UnpicklerObject *self, PyObject *args) global = PyObject_GetAttr(module, global_name); Py_DECREF(module); } - else { + else { global = PyObject_GetAttr(module, global_name); } return global; @@ -5582,6 +5643,7 @@ Unpickler_init(UnpicklerObject *self, PyObject *args, PyObject *kwds) PyObject *fix_imports = Py_True; char *encoding = NULL; char *errors = NULL; + _Py_IDENTIFIER(persistent_load); /* XXX: That is an horrible error message. But, I don't know how to do better... */ @@ -5616,9 +5678,9 @@ Unpickler_init(UnpicklerObject *self, PyObject *args, PyObject *kwds) if (self->fix_imports == -1) return -1; - if (PyObject_HasAttrString((PyObject *)self, "persistent_load")) { - self->pers_func = PyObject_GetAttrString((PyObject *)self, - "persistent_load"); + if (_PyObject_HasAttrId((PyObject *)self, &PyId_persistent_load)) { + self->pers_func = _PyObject_GetAttrId((PyObject *)self, + &PyId_persistent_load); if (self->pers_func == NULL) return -1; } @@ -5651,7 +5713,7 @@ Unpickler_init(UnpicklerObject *self, PyObject *args, PyObject *kwds) * intentional, as these should be treated as black-box implementation details. * * We do, however, have to implement pickling/unpickling support because of - * real-world code like cvs2svn. + * real-world code like cvs2svn. */ typedef struct { |