[Python-checkins] bpo-29902: Emit a Py3k deprecation warning when pickling or copying (#2823)

Serhiy Storchaka webhook-mailer at python.org
Wed Aug 2 04:33:36 EDT 2017


https://github.com/python/cpython/commit/3dd1ccbb0950b2b83713a495958c35d60b453fa9
commit: 3dd1ccbb0950b2b83713a495958c35d60b453fa9
branch: 2.7
author: Serhiy Storchaka <storchaka at gmail.com>
committer: GitHub <noreply at github.com>
date: 2017-08-02T11:33:33+03:00
summary:

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).

files:
A Misc/NEWS.d/next/Library/2017-07-23-13-47-22.bpo-29902.CiuFdn.rst
M Lib/test/test_buffer.py
M Lib/test/test_descr.py
M Lib/test/test_memoryview.py
M Objects/typeobject.c

diff --git a/Lib/test/test_buffer.py b/Lib/test/test_buffer.py
index de80d4469d7..c7114cde8b9 100644
--- a/Lib/test/test_buffer.py
+++ b/Lib/test/test_buffer.py
@@ -8,6 +8,7 @@
 import pickle
 import sys
 import unittest
+import warnings
 from test import test_support
 
 class BufferTests(unittest.TestCase):
@@ -39,15 +40,19 @@ def test_large_buffer_size_and_offset(self):
 
     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 1e63761f6d0..bbc52aa6791 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 test import test_support
 
 
+def func(*args):
+    return args
+
+
 class OperatorsTest(unittest.TestCase):
 
     def __init__(self, *args, **kwargs):
@@ -1415,6 +1421,21 @@ def f(cls, arg): return (cls, arg)
         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 @@ class D(C):
         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 @@ class D(object):
         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 4407af82642..42698583f9f 100644
--- a/Lib/test/test_memoryview.py
+++ b/Lib/test/test_memoryview.py
@@ -12,6 +12,7 @@
 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 00000000000..6aa6605be3e
--- /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 eb5f5a4e9dd..685c5457737 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)



More information about the Python-checkins mailing list