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.
This commit is contained in:
Serhiy Storchaka 2015-12-25 21:05:35 +02:00
commit f9253c96fd
4 changed files with 67 additions and 15 deletions

View File

@ -1,6 +1,7 @@
# Copyright (C) 2001,2002 Python Software Foundation # Copyright (C) 2001,2002 Python Software Foundation
# csv package unit tests # csv package unit tests
import copy
import io import io
import sys import sys
import os import os
@ -9,6 +10,7 @@ from io import StringIO
from tempfile import TemporaryFile from tempfile import TemporaryFile
import csv import csv
import gc import gc
import pickle
from test import support from test import support
class Test_Csv(unittest.TestCase): class Test_Csv(unittest.TestCase):
@ -424,6 +426,17 @@ class TestDialectRegistry(unittest.TestCase):
self.assertRaises(TypeError, csv.reader, [], quoting = -1) self.assertRaises(TypeError, csv.reader, [], quoting = -1)
self.assertRaises(TypeError, csv.reader, [], quoting = 100) 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): class TestCsvBase(unittest.TestCase):
def readerAssertEqual(self, input, expected_result): def readerAssertEqual(self, input, expected_result):
with TemporaryFile("w+", newline='') as fileobj: with TemporaryFile("w+", newline='') as fileobj:

View File

@ -11,6 +11,8 @@ import gc
import weakref import weakref
import array import array
import io import io
import copy
import pickle
class AbstractMemoryTests: class AbstractMemoryTests:
@ -519,6 +521,17 @@ class OtherTest(unittest.TestCase):
m2 = m1[::-1] m2 = m1[::-1]
self.assertEqual(m2.hex(), '30' * 200000) 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__": if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -10,6 +10,11 @@ Release date: tba
Core and Builtins 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 - Issue #20440: Massive replacing unsafe attribute setting code with special
macro Py_SETREF. macro Py_SETREF.

View File

@ -3839,7 +3839,7 @@ _PyType_GetSlotNames(PyTypeObject *cls)
} }
Py_LOCAL(PyObject *) Py_LOCAL(PyObject *)
_PyObject_GetState(PyObject *obj) _PyObject_GetState(PyObject *obj, int required)
{ {
PyObject *state; PyObject *state;
PyObject *getstate; PyObject *getstate;
@ -3854,6 +3854,13 @@ _PyObject_GetState(PyObject *obj)
} }
PyErr_Clear(); 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; PyObject **dict;
dict = _PyObject_GetDictPtr(obj); dict = _PyObject_GetDictPtr(obj);
@ -3878,6 +3885,24 @@ _PyObject_GetState(PyObject *obj)
} }
assert(slotnames == Py_None || PyList_Check(slotnames)); 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) { if (slotnames != Py_None && Py_SIZE(slotnames) > 0) {
PyObject *slots; PyObject *slots;
Py_ssize_t slotnames_size, i; Py_ssize_t slotnames_size, i;
@ -4107,29 +4132,24 @@ reduce_newobj(PyObject *obj)
PyObject *copyreg; PyObject *copyreg;
PyObject *newobj, *newargs, *state, *listitems, *dictitems; PyObject *newobj, *newargs, *state, *listitems, *dictitems;
PyObject *result; PyObject *result;
int hasargs;
if (Py_TYPE(obj)->tp_new == NULL) { if (Py_TYPE(obj)->tp_new == NULL) {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"can't pickle %s objects", "can't pickle %.200s objects",
Py_TYPE(obj)->tp_name); Py_TYPE(obj)->tp_name);
return NULL; return NULL;
} }
if (_PyObject_GetNewArguments(obj, &args, &kwargs) < 0) if (_PyObject_GetNewArguments(obj, &args, &kwargs) < 0)
return NULL; return NULL;
if (args == NULL) {
args = PyTuple_New(0);
if (args == NULL) {
Py_XDECREF(kwargs);
return NULL;
}
}
copyreg = import_copyreg(); copyreg = import_copyreg();
if (copyreg == NULL) { if (copyreg == NULL) {
Py_DECREF(args); Py_XDECREF(args);
Py_XDECREF(kwargs); Py_XDECREF(kwargs);
return NULL; return NULL;
} }
hasargs = (args != NULL);
if (kwargs == NULL || PyDict_Size(kwargs) == 0) { if (kwargs == NULL || PyDict_Size(kwargs) == 0) {
_Py_IDENTIFIER(__newobj__); _Py_IDENTIFIER(__newobj__);
PyObject *cls; PyObject *cls;
@ -4139,13 +4159,13 @@ reduce_newobj(PyObject *obj)
newobj = _PyObject_GetAttrId(copyreg, &PyId___newobj__); newobj = _PyObject_GetAttrId(copyreg, &PyId___newobj__);
Py_DECREF(copyreg); Py_DECREF(copyreg);
if (newobj == NULL) { if (newobj == NULL) {
Py_DECREF(args); Py_XDECREF(args);
return NULL; return NULL;
} }
n = PyTuple_GET_SIZE(args); n = args ? PyTuple_GET_SIZE(args) : 0;
newargs = PyTuple_New(n+1); newargs = PyTuple_New(n+1);
if (newargs == NULL) { if (newargs == NULL) {
Py_DECREF(args); Py_XDECREF(args);
Py_DECREF(newobj); Py_DECREF(newobj);
return NULL; return NULL;
} }
@ -4157,7 +4177,7 @@ reduce_newobj(PyObject *obj)
Py_INCREF(v); Py_INCREF(v);
PyTuple_SET_ITEM(newargs, i+1, v); PyTuple_SET_ITEM(newargs, i+1, v);
} }
Py_DECREF(args); Py_XDECREF(args);
} }
else { else {
_Py_IDENTIFIER(__newobj_ex__); _Py_IDENTIFIER(__newobj_ex__);
@ -4178,7 +4198,8 @@ reduce_newobj(PyObject *obj)
} }
} }
state = _PyObject_GetState(obj); state = _PyObject_GetState(obj,
!hasargs && !PyList_Check(obj) && !PyDict_Check(obj));
if (state == NULL) { if (state == NULL) {
Py_DECREF(newobj); Py_DECREF(newobj);
Py_DECREF(newargs); Py_DECREF(newargs);