https://hg.python.org/cpython/rev/b8d108a2a38e changeset: 99677:b8d108a2a38e parent: 99675:80d1faa9735d parent: 99676:0cd2de69fb66 user: Serhiy Storchaka <storchaka@gmail.com> date: Fri Dec 25 21:05:35 2015 +0200 summary: Issue #22995: Instances of extension types with a state that aren't subclasses of list or dict and haven't implemented any pickle-related methods (__reduce__, __reduce_ex__, __getnewargs__, __getnewargs_ex__, or __getstate__), can no longer be pickled. Including memoryview. files: Lib/test/test_csv.py | 13 ++++++ Lib/test/test_memoryview.py | 13 ++++++ Misc/NEWS | 5 ++ Objects/typeobject.c | 51 +++++++++++++++++------- 4 files changed, 67 insertions(+), 15 deletions(-) diff --git a/Lib/test/test_csv.py b/Lib/test/test_csv.py --- a/Lib/test/test_csv.py +++ b/Lib/test/test_csv.py @@ -1,6 +1,7 @@ # Copyright (C) 2001,2002 Python Software Foundation # csv package unit tests +import copy import io import sys import os @@ -9,6 +10,7 @@ from tempfile import TemporaryFile import csv import gc +import pickle from test import support class Test_Csv(unittest.TestCase): @@ -424,6 +426,17 @@ self.assertRaises(TypeError, csv.reader, [], quoting = -1) self.assertRaises(TypeError, csv.reader, [], quoting = 100) + def test_copy(self): + for name in csv.list_dialects(): + dialect = csv.get_dialect(name) + self.assertRaises(TypeError, copy.copy, dialect) + + def test_pickle(self): + for name in csv.list_dialects(): + dialect = csv.get_dialect(name) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + self.assertRaises(TypeError, pickle.dumps, dialect, proto) + class TestCsvBase(unittest.TestCase): def readerAssertEqual(self, input, expected_result): with TemporaryFile("w+", newline='') as fileobj: diff --git a/Lib/test/test_memoryview.py b/Lib/test/test_memoryview.py --- a/Lib/test/test_memoryview.py +++ b/Lib/test/test_memoryview.py @@ -11,6 +11,8 @@ import weakref import array import io +import copy +import pickle class AbstractMemoryTests: @@ -519,6 +521,17 @@ m2 = m1[::-1] self.assertEqual(m2.hex(), '30' * 200000) + def test_copy(self): + m = memoryview(b'abc') + with self.assertRaises(TypeError): + copy.copy(m) + + def test_pickle(self): + m = memoryview(b'abc') + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + with self.assertRaises(TypeError): + pickle.dumps(m, proto) + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,11 @@ Core and Builtins ----------------- +- Issue #22995: Instances of extension types with a state that aren't + subclasses of list or dict and haven't implemented any pickle-related + methods (__reduce__, __reduce_ex__, __getnewargs__, __getnewargs_ex__, + or __getstate__), can no longer be pickled. Including memoryview. + - Issue #20440: Massive replacing unsafe attribute setting code with special macro Py_SETREF. diff --git a/Objects/typeobject.c b/Objects/typeobject.c --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3839,7 +3839,7 @@ } Py_LOCAL(PyObject *) -_PyObject_GetState(PyObject *obj) +_PyObject_GetState(PyObject *obj, int required) { PyObject *state; PyObject *getstate; @@ -3854,6 +3854,13 @@ } PyErr_Clear(); + if (required && obj->ob_type->tp_itemsize) { + PyErr_Format(PyExc_TypeError, + "can't pickle %.200s objects", + Py_TYPE(obj)->tp_name); + return NULL; + } + { PyObject **dict; dict = _PyObject_GetDictPtr(obj); @@ -3878,6 +3885,24 @@ } assert(slotnames == Py_None || PyList_Check(slotnames)); + if (required) { + Py_ssize_t basicsize = PyBaseObject_Type.tp_basicsize; + if (obj->ob_type->tp_dictoffset) + basicsize += sizeof(PyObject *); + if (obj->ob_type->tp_weaklistoffset) + basicsize += sizeof(PyObject *); + if (slotnames != Py_None) + basicsize += sizeof(PyObject *) * Py_SIZE(slotnames); + if (obj->ob_type->tp_basicsize > basicsize) { + Py_DECREF(slotnames); + Py_DECREF(state); + PyErr_Format(PyExc_TypeError, + "can't pickle %.200s objects", + Py_TYPE(obj)->tp_name); + return NULL; + } + } + if (slotnames != Py_None && Py_SIZE(slotnames) > 0) { PyObject *slots; Py_ssize_t slotnames_size, i; @@ -4107,29 +4132,24 @@ PyObject *copyreg; PyObject *newobj, *newargs, *state, *listitems, *dictitems; PyObject *result; + int hasargs; if (Py_TYPE(obj)->tp_new == NULL) { PyErr_Format(PyExc_TypeError, - "can't pickle %s objects", + "can't pickle %.200s objects", Py_TYPE(obj)->tp_name); return NULL; } if (_PyObject_GetNewArguments(obj, &args, &kwargs) < 0) return NULL; - if (args == NULL) { - args = PyTuple_New(0); - if (args == NULL) { - Py_XDECREF(kwargs); - return NULL; - } - } copyreg = import_copyreg(); if (copyreg == NULL) { - Py_DECREF(args); + Py_XDECREF(args); Py_XDECREF(kwargs); return NULL; } + hasargs = (args != NULL); if (kwargs == NULL || PyDict_Size(kwargs) == 0) { _Py_IDENTIFIER(__newobj__); PyObject *cls; @@ -4139,13 +4159,13 @@ newobj = _PyObject_GetAttrId(copyreg, &PyId___newobj__); Py_DECREF(copyreg); if (newobj == NULL) { - Py_DECREF(args); + Py_XDECREF(args); return NULL; } - n = PyTuple_GET_SIZE(args); + n = args ? PyTuple_GET_SIZE(args) : 0; newargs = PyTuple_New(n+1); if (newargs == NULL) { - Py_DECREF(args); + Py_XDECREF(args); Py_DECREF(newobj); return NULL; } @@ -4157,7 +4177,7 @@ Py_INCREF(v); PyTuple_SET_ITEM(newargs, i+1, v); } - Py_DECREF(args); + Py_XDECREF(args); } else { _Py_IDENTIFIER(__newobj_ex__); @@ -4178,7 +4198,8 @@ } } - state = _PyObject_GetState(obj); + state = _PyObject_GetState(obj, + !hasargs && !PyList_Check(obj) && !PyDict_Check(obj)); if (state == NULL) { Py_DECREF(newobj); Py_DECREF(newargs); -- Repository URL: https://hg.python.org/cpython