From: Serhiy Storchaka Date: Wed, 2 Aug 2017 08:33:33 +0000 (+0300) Subject: bpo-29902: Emit a Py3k deprecation warning when pickling or copying (#2823) X-Git-Tag: v2.7.14rc1~26 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3dd1ccbb0950b2b83713a495958c35d60b453fa9;p=python 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). --- diff --git a/Lib/test/test_buffer.py b/Lib/test/test_buffer.py index de80d4469d..c7114cde8b 100644 --- a/Lib/test/test_buffer.py +++ b/Lib/test/test_buffer.py @@ -8,6 +8,7 @@ import copy import pickle import sys import unittest +import warnings from test import test_support class BufferTests(unittest.TestCase): @@ -39,15 +40,19 @@ class BufferTests(unittest.TestCase): def test_copy(self): buf = buffer(b'abc') - with self.assertRaises(TypeError): + with self.assertRaises(TypeError), warnings.catch_warnings(): + warnings.filterwarnings('ignore', ".*buffer", DeprecationWarning) copy.copy(buf) - # See issue #22995 - ## def test_pickle(self): - ## buf = buffer(b'abc') - ## for proto in range(pickle.HIGHEST_PROTOCOL + 1): - ## with self.assertRaises(TypeError): - ## pickle.dumps(buf, proto) + @test_support.cpython_only + def test_pickle(self): + buf = buffer(b'abc') + for proto in range(2): + with self.assertRaises(TypeError): + pickle.dumps(buf, proto) + with test_support.check_py3k_warnings( + (".*buffer", DeprecationWarning)): + pickle.dumps(buf, 2) def test_main(): diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 1e63761f6d..bbc52aa679 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -1,5 +1,7 @@ import __builtin__ +import copy import gc +import pickle import sys import types import unittest @@ -10,6 +12,10 @@ from copy import deepcopy from test import test_support +def func(*args): + return args + + class OperatorsTest(unittest.TestCase): def __init__(self, *args, **kwargs): @@ -1415,6 +1421,21 @@ order (MRO) for bases """ else: self.fail("classmethod shouldn't accept keyword args") + @test_support.cpython_only + def test_classmethod_copy_pickle(self): + cm = classmethod(func) + with test_support.check_py3k_warnings( + (".*classmethod", DeprecationWarning)): + copy.copy(cm) + with test_support.check_py3k_warnings( + (".*classmethod", DeprecationWarning)): + copy.deepcopy(cm) + for proto in range(2): + self.assertRaises(TypeError, pickle.dumps, cm, proto) + with test_support.check_py3k_warnings( + (".*classmethod", DeprecationWarning)): + pickle.dumps(cm, 2) + @test_support.impl_detail("the module 'xxsubtype' is internal") def test_classmethods_in_c(self): # Testing C-based class methods... @@ -1463,6 +1484,21 @@ order (MRO) for bases """ self.assertEqual(d.foo(1), (d, 1)) self.assertEqual(D.foo(d, 1), (d, 1)) + @test_support.cpython_only + def test_staticmethod_copy_pickle(self): + sm = staticmethod(func) + with test_support.check_py3k_warnings( + (".*staticmethod", DeprecationWarning)): + copy.copy(sm) + with test_support.check_py3k_warnings( + (".*staticmethod", DeprecationWarning)): + copy.deepcopy(sm) + for proto in range(2): + self.assertRaises(TypeError, pickle.dumps, sm, proto) + with test_support.check_py3k_warnings( + (".*staticmethod", DeprecationWarning)): + pickle.dumps(sm, 2) + @test_support.impl_detail("the module 'xxsubtype' is internal") def test_staticmethods_in_c(self): # Testing C-based static methods... @@ -2158,6 +2194,21 @@ order (MRO) for bases """ else: self.fail("expected ZeroDivisionError from bad property") + @test_support.cpython_only + def test_property_copy_pickle(self): + p = property(func) + with test_support.check_py3k_warnings( + (".*property", DeprecationWarning)): + copy.copy(p) + with test_support.check_py3k_warnings( + (".*property", DeprecationWarning)): + copy.deepcopy(p) + for proto in range(2): + self.assertRaises(TypeError, pickle.dumps, p, proto) + with test_support.check_py3k_warnings( + (".*property", DeprecationWarning)): + pickle.dumps(p, 2) + @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") def test_properties_doc_attrib(self): diff --git a/Lib/test/test_memoryview.py b/Lib/test/test_memoryview.py index 4407af8264..42698583f9 100644 --- a/Lib/test/test_memoryview.py +++ b/Lib/test/test_memoryview.py @@ -12,6 +12,7 @@ from test import test_support import io import copy import pickle +import warnings class AbstractMemoryTests: @@ -359,15 +360,20 @@ class BytesMemorySliceSliceTest(unittest.TestCase, class OtherTest(unittest.TestCase): def test_copy(self): m = memoryview(b'abc') - with self.assertRaises(TypeError): + with self.assertRaises(TypeError), warnings.catch_warnings(): + warnings.filterwarnings('ignore', ".*memoryview", DeprecationWarning) copy.copy(m) - # See issue #22995 - ## def test_pickle(self): - ## m = memoryview(b'abc') - ## for proto in range(pickle.HIGHEST_PROTOCOL + 1): - ## with self.assertRaises(TypeError): - ## pickle.dumps(m, proto) + @test_support.cpython_only + def test_pickle(self): + m = memoryview(b'abc') + for proto in range(2): + with self.assertRaises(TypeError): + pickle.dumps(m, proto) + with test_support.check_py3k_warnings( + (".*memoryview", DeprecationWarning)): + pickle.dumps(m, 2) + def test_main(): diff --git a/Misc/NEWS.d/next/Library/2017-07-23-13-47-22.bpo-29902.CiuFdn.rst b/Misc/NEWS.d/next/Library/2017-07-23-13-47-22.bpo-29902.CiuFdn.rst new file mode 100644 index 0000000000..6aa6605be3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2017-07-23-13-47-22.bpo-29902.CiuFdn.rst @@ -0,0 +1,4 @@ +Py3k deprecation warning now is emitted when pickling or copying some builtin +and extension objects that don't support pickling explicitly and are pickled +incorrectly by default (like memoryview or staticmethod). This is a +TypeError in Python 3.6. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index eb5f5a4e9d..685c545773 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3366,6 +3366,29 @@ reduce_2(PyObject *obj) goto end; assert(names == Py_None || PyList_Check(names)); + if (required_state && Py_Py3kWarningFlag) { + 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 (names != Py_None) + basicsize += sizeof(PyObject *) * PyList_GET_SIZE(names); + if (obj->ob_type->tp_basicsize > basicsize) { + PyObject *msg = PyString_FromFormat( + "can't pickle %.200s objects", + Py_TYPE(obj)->tp_name); + if (msg == NULL) { + goto end; + } + if (PyErr_WarnPy3k(PyString_AS_STRING(msg), 1) < 0) { + Py_DECREF(msg); + goto end; + } + Py_DECREF(msg); + } + } + if (names != Py_None) { slots = PyDict_New(); if (slots == NULL)