[Checkins] SVN: Acquisition/branches/2.11/ Backport getnewargs fix from trunk

Hanno Schlichting hannosch at hannosch.eu
Sat Apr 3 19:58:12 EDT 2010


Log message for revision 110481:
  Backport getnewargs fix from trunk
  

Changed:
  U   Acquisition/branches/2.11/CHANGES.txt
  U   Acquisition/branches/2.11/src/Acquisition/_Acquisition.c
  U   Acquisition/branches/2.11/src/Acquisition/tests.py

-=-
Modified: Acquisition/branches/2.11/CHANGES.txt
===================================================================
--- Acquisition/branches/2.11/CHANGES.txt	2010-04-03 23:27:51 UTC (rev 110480)
+++ Acquisition/branches/2.11/CHANGES.txt	2010-04-03 23:58:11 UTC (rev 110481)
@@ -4,7 +4,18 @@
 2.11.3 (unreleased)
 -------------------
 
+- Give both wrapper classes a ``__getnewargs__`` method, which causes the ZODB
+  optimization to fail and create persistent references using the ``_p_oid``
+  alone. This happens to be the persistent oid of the wrapped object. This lets
+  these objects to be persisted correctly, even though they are passed to the
+  ZODB in a wrapped state.
 
+- Added failing tests for http://dev.plone.org/plone/ticket/10318. This shows
+  an edge-case where AQ wrappers can be pickled using the specific combination
+  of cPickle, pickle protocol one and a custom Pickler class with an
+  ``inst_persistent_id`` hook. Unfortunately this is the exact combination used
+  by ZODB3.
+
 2.11.2 (2010-02-25)
 -------------------
 

Modified: Acquisition/branches/2.11/src/Acquisition/_Acquisition.c
===================================================================
--- Acquisition/branches/2.11/src/Acquisition/_Acquisition.c	2010-04-03 23:27:51 UTC (rev 110480)
+++ Acquisition/branches/2.11/src/Acquisition/_Acquisition.c	2010-04-03 23:58:11 UTC (rev 110481)
@@ -1206,6 +1206,12 @@
   return NULL;
 }
 
+static PyObject *
+Wrapper___getnewargs__(PyObject *self)
+{
+  return PyTuple_New(0);
+}
+
 static struct PyMethodDef Wrapper_methods[] = {
   {"acquire", (PyCFunction)Wrapper_acquire_method, 
    METH_VARARGS|METH_KEYWORDS,
@@ -1215,6 +1221,8 @@
    "Get an attribute, acquiring it if necessary"},
   {"aq_inContextOf", (PyCFunction)Wrapper_inContextOf, METH_VARARGS,
    "Test whether the object is currently in the context of the argument"},
+  {"__getnewargs__", (PyCFunction)Wrapper___getnewargs__, METH_NOARGS,
+    "Get arguments to be passed to __new__"},
   {"__getstate__", (PyCFunction)Wrappers_are_not_picklable, METH_VARARGS,
    "Wrappers are not picklable"},
   {"__reduce__", (PyCFunction)Wrappers_are_not_picklable, METH_VARARGS,

Modified: Acquisition/branches/2.11/src/Acquisition/tests.py
===================================================================
--- Acquisition/branches/2.11/src/Acquisition/tests.py	2010-04-03 23:27:51 UTC (rev 110480)
+++ Acquisition/branches/2.11/src/Acquisition/tests.py	2010-04-03 23:58:11 UTC (rev 110481)
@@ -1502,8 +1502,127 @@
     TypeError: Can't pickle objects in acquisition wrappers.
     """
 
-def test_z3interfaces():
+
+def test_cant_persist_acquisition_wrappers_classic():
     """
+    >>> import cPickle
+
+    >>> class X:
+    ...     _p_oid = '1234'
+    ...     def __getstate__(self):
+    ...         return 1
+
+    We shouldn't be able to pickle wrappers:
+
+    >>> from Acquisition import ImplicitAcquisitionWrapper
+    >>> w = ImplicitAcquisitionWrapper(X(), X())
+    >>> cPickle.dumps(w)
+    Traceback (most recent call last):
+    ...
+    TypeError: Can't pickle objects in acquisition wrappers.
+
+    Check for pickle protocol one:
+
+    >>> cPickle.dumps(w, 1)
+    Traceback (most recent call last):
+    ...
+    TypeError: Can't pickle objects in acquisition wrappers.
+
+    Check custom pickler:
+
+    >>> from cStringIO import StringIO
+    >>> file = StringIO()
+    >>> pickler = cPickle.Pickler(file, 1)
+
+    >>> pickler.dump(w)
+    Traceback (most recent call last):
+    ...
+    TypeError: Can't pickle objects in acquisition wrappers.
+
+    Check custom pickler with a persistent_id method matching the semantics
+    in ZODB.serialize.ObjectWriter.persistent_id:
+
+    >>> file = StringIO()
+    >>> pickler = cPickle.Pickler(file, 1)
+
+    >>> def persistent_id(obj):
+    ...     klass = type(obj)
+    ...     oid = obj._p_oid
+    ...     if hasattr(klass, '__getnewargs__'):
+    ...         return oid
+    ...     return 'class_and_oid', klass
+
+    >>> pickler.inst_persistent_id = persistent_id
+    >>> _ = pickler.dump(w)
+    >>> state = file.getvalue()
+    >>> '1234' in state
+    True
+    >>> 'class_and_oid' in state
+    False
+    """
+
+
+def test_cant_persist_acquisition_wrappers_newstyle():
+    """
+    >>> import cPickle
+
+    >>> class X(object):
+    ...     _p_oid = '1234'
+    ...     def __getstate__(self):
+    ...         return 1
+
+    We shouldn't be able to pickle wrappers:
+
+    >>> from Acquisition import ImplicitAcquisitionWrapper
+    >>> w = ImplicitAcquisitionWrapper(X(), X())
+    >>> cPickle.dumps(w)
+    Traceback (most recent call last):
+    ...
+    TypeError: Can't pickle objects in acquisition wrappers.
+
+    Check for pickle protocol one:
+
+    >>> cPickle.dumps(w, 1)
+    Traceback (most recent call last):
+    ...
+    TypeError: Can't pickle objects in acquisition wrappers.
+
+    Check custom pickler:
+
+    >>> from cStringIO import StringIO
+    >>> file = StringIO()
+    >>> pickler = cPickle.Pickler(file, 1)
+
+    >>> pickler.dump(w)
+    Traceback (most recent call last):
+    ...
+    TypeError: Can't pickle objects in acquisition wrappers.
+
+    Check custom pickler with a persistent_id method matching the semantics
+    in ZODB.serialize.ObjectWriter.persistent_id:
+
+    >>> file = StringIO()
+    >>> pickler = cPickle.Pickler(file, 1)
+
+    >>> def persistent_id(obj):
+    ...     klass = type(obj)
+    ...     oid = obj._p_oid
+    ...     if hasattr(klass, '__getnewargs__'):
+    ...         return oid
+    ...     return 'class_and_oid', klass
+
+    >>> pickler.inst_persistent_id = persistent_id
+    >>> _ = pickler.dump(w)
+    >>> state = file.getvalue()
+    >>> '1234' in state
+    True
+    >>> 'class_and_oid' in state
+    False
+    """
+
+
+def test_interfaces():
+    """
     >>> from zope.interface.verify import verifyClass
 
     Explicit and Implicit implement IAcquirer:



More information about the checkins mailing list