Skip to content

Commit 3dd1ccb

Browse files
bpo-29902: Emit a Py3k deprecation warning when pickling or copying (#2823)
some builtin and extension objects that don't support pickling explicitly and are pickled incorrectly by default (like memoryview or staticmethod).
1 parent 956902e commit 3dd1ccb

File tree

5 files changed

+103
-14
lines changed

5 files changed

+103
-14
lines changed

Lib/test/test_buffer.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import pickle
99
import sys
1010
import unittest
11+
import warnings
1112
from test import test_support
1213

1314
class BufferTests(unittest.TestCase):
@@ -39,15 +40,19 @@ def test_large_buffer_size_and_offset(self):
3940

4041
def test_copy(self):
4142
buf = buffer(b'abc')
42-
with self.assertRaises(TypeError):
43+
with self.assertRaises(TypeError), warnings.catch_warnings():
44+
warnings.filterwarnings('ignore', ".*buffer", DeprecationWarning)
4345
copy.copy(buf)
4446

45-
# See issue #22995
46-
## def test_pickle(self):
47-
## buf = buffer(b'abc')
48-
## for proto in range(pickle.HIGHEST_PROTOCOL + 1):
49-
## with self.assertRaises(TypeError):
50-
## pickle.dumps(buf, proto)
47+
@test_support.cpython_only
48+
def test_pickle(self):
49+
buf = buffer(b'abc')
50+
for proto in range(2):
51+
with self.assertRaises(TypeError):
52+
pickle.dumps(buf, proto)
53+
with test_support.check_py3k_warnings(
54+
(".*buffer", DeprecationWarning)):
55+
pickle.dumps(buf, 2)
5156

5257

5358
def test_main():

Lib/test/test_descr.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import __builtin__
2+
import copy
23
import gc
4+
import pickle
35
import sys
46
import types
57
import unittest
@@ -10,6 +12,10 @@
1012
from test import test_support
1113

1214

15+
def func(*args):
16+
return args
17+
18+
1319
class OperatorsTest(unittest.TestCase):
1420

1521
def __init__(self, *args, **kwargs):
@@ -1415,6 +1421,21 @@ def f(cls, arg): return (cls, arg)
14151421
else:
14161422
self.fail("classmethod shouldn't accept keyword args")
14171423

1424+
@test_support.cpython_only
1425+
def test_classmethod_copy_pickle(self):
1426+
cm = classmethod(func)
1427+
with test_support.check_py3k_warnings(
1428+
(".*classmethod", DeprecationWarning)):
1429+
copy.copy(cm)
1430+
with test_support.check_py3k_warnings(
1431+
(".*classmethod", DeprecationWarning)):
1432+
copy.deepcopy(cm)
1433+
for proto in range(2):
1434+
self.assertRaises(TypeError, pickle.dumps, cm, proto)
1435+
with test_support.check_py3k_warnings(
1436+
(".*classmethod", DeprecationWarning)):
1437+
pickle.dumps(cm, 2)
1438+
14181439
@test_support.impl_detail("the module 'xxsubtype' is internal")
14191440
def test_classmethods_in_c(self):
14201441
# Testing C-based class methods...
@@ -1463,6 +1484,21 @@ class D(C):
14631484
self.assertEqual(d.foo(1), (d, 1))
14641485
self.assertEqual(D.foo(d, 1), (d, 1))
14651486

1487+
@test_support.cpython_only
1488+
def test_staticmethod_copy_pickle(self):
1489+
sm = staticmethod(func)
1490+
with test_support.check_py3k_warnings(
1491+
(".*staticmethod", DeprecationWarning)):
1492+
copy.copy(sm)
1493+
with test_support.check_py3k_warnings(
1494+
(".*staticmethod", DeprecationWarning)):
1495+
copy.deepcopy(sm)
1496+
for proto in range(2):
1497+
self.assertRaises(TypeError, pickle.dumps, sm, proto)
1498+
with test_support.check_py3k_warnings(
1499+
(".*staticmethod", DeprecationWarning)):
1500+
pickle.dumps(sm, 2)
1501+
14661502
@test_support.impl_detail("the module 'xxsubtype' is internal")
14671503
def test_staticmethods_in_c(self):
14681504
# Testing C-based static methods...
@@ -2158,6 +2194,21 @@ class D(object):
21582194
else:
21592195
self.fail("expected ZeroDivisionError from bad property")
21602196

2197+
@test_support.cpython_only
2198+
def test_property_copy_pickle(self):
2199+
p = property(func)
2200+
with test_support.check_py3k_warnings(
2201+
(".*property", DeprecationWarning)):
2202+
copy.copy(p)
2203+
with test_support.check_py3k_warnings(
2204+
(".*property", DeprecationWarning)):
2205+
copy.deepcopy(p)
2206+
for proto in range(2):
2207+
self.assertRaises(TypeError, pickle.dumps, p, proto)
2208+
with test_support.check_py3k_warnings(
2209+
(".*property", DeprecationWarning)):
2210+
pickle.dumps(p, 2)
2211+
21612212
@unittest.skipIf(sys.flags.optimize >= 2,
21622213
"Docstrings are omitted with -O2 and above")
21632214
def test_properties_doc_attrib(self):

Lib/test/test_memoryview.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import io
1313
import copy
1414
import pickle
15+
import warnings
1516

1617

1718
class AbstractMemoryTests:
@@ -359,15 +360,20 @@ class BytesMemorySliceSliceTest(unittest.TestCase,
359360
class OtherTest(unittest.TestCase):
360361
def test_copy(self):
361362
m = memoryview(b'abc')
362-
with self.assertRaises(TypeError):
363+
with self.assertRaises(TypeError), warnings.catch_warnings():
364+
warnings.filterwarnings('ignore', ".*memoryview", DeprecationWarning)
363365
copy.copy(m)
364366

365-
# See issue #22995
366-
## def test_pickle(self):
367-
## m = memoryview(b'abc')
368-
## for proto in range(pickle.HIGHEST_PROTOCOL + 1):
369-
## with self.assertRaises(TypeError):
370-
## pickle.dumps(m, proto)
367+
@test_support.cpython_only
368+
def test_pickle(self):
369+
m = memoryview(b'abc')
370+
for proto in range(2):
371+
with self.assertRaises(TypeError):
372+
pickle.dumps(m, proto)
373+
with test_support.check_py3k_warnings(
374+
(".*memoryview", DeprecationWarning)):
375+
pickle.dumps(m, 2)
376+
371377

372378

373379
def test_main():
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Py3k deprecation warning now is emitted when pickling or copying some builtin
2+
and extension objects that don't support pickling explicitly and are pickled
3+
incorrectly by default (like memoryview or staticmethod). This is a
4+
TypeError in Python 3.6.

Objects/typeobject.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3366,6 +3366,29 @@ reduce_2(PyObject *obj)
33663366
goto end;
33673367
assert(names == Py_None || PyList_Check(names));
33683368

3369+
if (required_state && Py_Py3kWarningFlag) {
3370+
Py_ssize_t basicsize = PyBaseObject_Type.tp_basicsize;
3371+
if (obj->ob_type->tp_dictoffset)
3372+
basicsize += sizeof(PyObject *);
3373+
if (obj->ob_type->tp_weaklistoffset)
3374+
basicsize += sizeof(PyObject *);
3375+
if (names != Py_None)
3376+
basicsize += sizeof(PyObject *) * PyList_GET_SIZE(names);
3377+
if (obj->ob_type->tp_basicsize > basicsize) {
3378+
PyObject *msg = PyString_FromFormat(
3379+
"can't pickle %.200s objects",
3380+
Py_TYPE(obj)->tp_name);
3381+
if (msg == NULL) {
3382+
goto end;
3383+
}
3384+
if (PyErr_WarnPy3k(PyString_AS_STRING(msg), 1) < 0) {
3385+
Py_DECREF(msg);
3386+
goto end;
3387+
}
3388+
Py_DECREF(msg);
3389+
}
3390+
}
3391+
33693392
if (names != Py_None) {
33703393
slots = PyDict_New();
33713394
if (slots == NULL)

0 commit comments

Comments
 (0)