[Checkins] SVN: Acquisition/branches/erp5-aq_dynamic/ Sync to Acquisition trunk: __getnewargs__ fix and ZF license compliance
Leonardo Rochael Almeida
leorochael at gmail.com
Thu Apr 8 00:16:33 EDT 2010
Log message for revision 110629:
Sync to Acquisition trunk: __getnewargs__ fix and ZF license compliance
Changed:
U Acquisition/branches/erp5-aq_dynamic/CHANGES.txt
A Acquisition/branches/erp5-aq_dynamic/COPYRIGHT.txt
A Acquisition/branches/erp5-aq_dynamic/LICENSE.txt
U Acquisition/branches/erp5-aq_dynamic/setup.py
U Acquisition/branches/erp5-aq_dynamic/src/Acquisition/Acquisition.h
U Acquisition/branches/erp5-aq_dynamic/src/Acquisition/_Acquisition.c
U Acquisition/branches/erp5-aq_dynamic/src/Acquisition/interfaces.py
U Acquisition/branches/erp5-aq_dynamic/src/Acquisition/tests.py
-=-
Modified: Acquisition/branches/erp5-aq_dynamic/CHANGES.txt
===================================================================
--- Acquisition/branches/erp5-aq_dynamic/CHANGES.txt 2010-04-08 04:08:50 UTC (rev 110628)
+++ Acquisition/branches/erp5-aq_dynamic/CHANGES.txt 2010-04-08 04:16:33 UTC (rev 110629)
@@ -1,10 +1,25 @@
Changelog
=========
-2.13.2 (unreleased)
+2.13.3 (unreleased)
-------------------
+2.13.2 (2010-04-04)
+-------------------
+
+- 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.13.1 (2010-02-23)
-------------------
Copied: Acquisition/branches/erp5-aq_dynamic/COPYRIGHT.txt (from rev 110628, Acquisition/branches/erp5-aq_dynamic-mergepoint4-fromtrunk/COPYRIGHT.txt)
===================================================================
--- Acquisition/branches/erp5-aq_dynamic/COPYRIGHT.txt (rev 0)
+++ Acquisition/branches/erp5-aq_dynamic/COPYRIGHT.txt 2010-04-08 04:16:33 UTC (rev 110629)
@@ -0,0 +1 @@
+Zope Foundation and Contributors
\ No newline at end of file
Copied: Acquisition/branches/erp5-aq_dynamic/LICENSE.txt (from rev 110628, Acquisition/branches/erp5-aq_dynamic-mergepoint4-fromtrunk/LICENSE.txt)
===================================================================
--- Acquisition/branches/erp5-aq_dynamic/LICENSE.txt (rev 0)
+++ Acquisition/branches/erp5-aq_dynamic/LICENSE.txt 2010-04-08 04:16:33 UTC (rev 110629)
@@ -0,0 +1,44 @@
+Zope Public License (ZPL) Version 2.1
+
+A copyright notice accompanies this license document that identifies the
+copyright holders.
+
+This license has been certified as open source. It has also been designated as
+GPL compatible by the Free Software Foundation (FSF).
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions in source code must retain the accompanying copyright
+notice, this list of conditions, and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the accompanying copyright
+notice, this list of conditions, and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+3. Names of the copyright holders must not be used to endorse or promote
+products derived from this software without prior written permission from the
+copyright holders.
+
+4. The right to distribute this software or to use it for any purpose does not
+give you the right to use Servicemarks (sm) or Trademarks (tm) of the
+copyright
+holders. Use of them is covered by separate agreement with the copyright
+holders.
+
+5. If any files are modified, you must cause the modified files to carry
+prominent notices stating that you changed the files and the date of any
+change.
+
+Disclaimer
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED
+OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Modified: Acquisition/branches/erp5-aq_dynamic/setup.py
===================================================================
--- Acquisition/branches/erp5-aq_dynamic/setup.py 2010-04-08 04:08:50 UTC (rev 110628)
+++ Acquisition/branches/erp5-aq_dynamic/setup.py 2010-04-08 04:16:33 UTC (rev 110629)
@@ -1,6 +1,6 @@
##############################################################################
#
-# Copyright (c) 2007 Zope Corporation and Contributors.
+# Copyright (c) 2007 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
@@ -17,12 +17,12 @@
from setuptools import setup, find_packages, Extension
setup(name='Acquisition',
- version = '2.13.2dev',
+ version = '2.13.3dev',
url='http://pypi.python.org/pypi/Acquisition',
license='ZPL 2.1',
description="Acquisition is a mechanism that allows objects to obtain "
"attributes from the containment hierarchy they're in.",
- author='Zope Corporation and Contributors',
+ author='Zope Foundation and Contributors',
author_email='zope-dev at zope.org',
long_description=open(
os.path.join('src', 'Acquisition', 'README.txt')).read() + '\n' +
Modified: Acquisition/branches/erp5-aq_dynamic/src/Acquisition/Acquisition.h
===================================================================
--- Acquisition/branches/erp5-aq_dynamic/src/Acquisition/Acquisition.h 2010-04-08 04:08:50 UTC (rev 110628)
+++ Acquisition/branches/erp5-aq_dynamic/src/Acquisition/Acquisition.h 2010-04-08 04:16:33 UTC (rev 110629)
@@ -1,6 +1,6 @@
/*****************************************************************************
- Copyright (c) 1996-2002 Zope Corporation and Contributors.
+ Copyright (c) 1996-2002 Zope Foundation and Contributors.
All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Modified: Acquisition/branches/erp5-aq_dynamic/src/Acquisition/_Acquisition.c
===================================================================
--- Acquisition/branches/erp5-aq_dynamic/src/Acquisition/_Acquisition.c 2010-04-08 04:08:50 UTC (rev 110628)
+++ Acquisition/branches/erp5-aq_dynamic/src/Acquisition/_Acquisition.c 2010-04-08 04:16:33 UTC (rev 110629)
@@ -1,6 +1,6 @@
/*****************************************************************************
- Copyright (c) 1996-2003 Zope Corporation and Contributors.
+ Copyright (c) 1996-2003 Zope Foundation and Contributors.
All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
@@ -1348,6 +1348,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,
@@ -1357,6 +1363,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/erp5-aq_dynamic/src/Acquisition/interfaces.py
===================================================================
--- Acquisition/branches/erp5-aq_dynamic/src/Acquisition/interfaces.py 2010-04-08 04:08:50 UTC (rev 110628)
+++ Acquisition/branches/erp5-aq_dynamic/src/Acquisition/interfaces.py 2010-04-08 04:16:33 UTC (rev 110629)
@@ -1,6 +1,6 @@
##############################################################################
#
-# Copyright (c) 2005 Zope Corporation and Contributors. All Rights Reserved.
+# Copyright (c) 2005 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
Modified: Acquisition/branches/erp5-aq_dynamic/src/Acquisition/tests.py
===================================================================
--- Acquisition/branches/erp5-aq_dynamic/src/Acquisition/tests.py 2010-04-08 04:08:50 UTC (rev 110628)
+++ Acquisition/branches/erp5-aq_dynamic/src/Acquisition/tests.py 2010-04-08 04:16:33 UTC (rev 110629)
@@ -1,6 +1,6 @@
##############################################################################
#
-# Copyright (c) 2003 Zope Corporation and Contributors.
+# Copyright (c) 2003 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
@@ -70,9 +70,9 @@
cannot be found in 'a'.
Aquisition wrappers provide access to the wrapped objects
- through the attributes 'aq_parent', 'aq_self', 'aq_base'.
+ through the attributes 'aq_parent', 'aq_self', 'aq_base'.
In the example above, the expressions::
-
+
>>> c.a.aq_parent is c
1
@@ -97,28 +97,28 @@
Two styles of acquisition are supported in the current
ExtensionClass release, implicit and explicit aquisition.
-
+
Implicit acquisition
-
+
Implicit acquisition is so named because it searches for
attributes from the environment automatically whenever an
attribute cannot be obtained directly from an object or
through inheritence.
-
+
An attribute may be implicitly acquired if it's name does
not begin with an underscore, '_'.
-
+
To support implicit acquisition, an object should inherit
from the mix-in class 'Acquisition.Implicit'.
-
+
Explicit Acquisition
-
+
When explicit acquisition is used, attributes are not
automatically obtained from the environment. Instead, the
method 'aq_aquire' must be used, as in::
-
+
print c.a.aq_acquire('color')
-
+
To support explicit acquisition, an object should inherit
from the mix-in class 'Acquisition.Explicit'.
@@ -206,31 +206,31 @@
For example, in::
>>> from Acquisition import Explicit
-
+
>>> class HandyForTesting:
... def __init__(self, name): self.name=name
... def __str__(self):
... return "%s(%s)" % (self.name, self.__class__.__name__)
... __repr__=__str__
-
+
>>> class E(Explicit, HandyForTesting):
... pass
-
+
>>> class Nice(HandyForTesting):
... isNice=1
... def __str__(self):
... return HandyForTesting.__str__(self)+' and I am nice!'
... __repr__=__str__
-
+
>>> a = E('a')
>>> a.b = E('b')
>>> a.b.c = E('c')
>>> a.p = Nice('spam')
>>> a.b.p = E('p')
-
+
>>> def find_nice(self, ancestor, name, object, extra):
... return hasattr(object,'isNice') and object.isNice
-
+
>>> print a.b.c.aq_acquire('p', find_nice)
spam(Nice) and I am nice!
@@ -258,13 +258,13 @@
Consider the following example::
>>> from Acquisition import Implicit
-
+
>>> class C(Implicit):
... def __init__(self, name): self.name=name
... def __str__(self):
... return "%s(%s)" % (self.name, self.__class__.__name__)
... __repr__=__str__
-
+
>>> a = C("a")
>>> a.b = C("b")
>>> a.b.pref = "spam"
@@ -323,9 +323,9 @@
'a' is searched no more than once, even though it is wrapped three
times.
-.. [1] Gil, J., Lorenz, D.,
+.. [1] Gil, J., Lorenz, D.,
"Environmental Acquisition--A New Inheritance-Like Abstraction Mechanism",
- http://www.bell-labs.com/people/cope/oopsla/Oopsla96TechnicalProgramAbstracts.html#GilLorenz,
+ http://www.bell-labs.com/people/cope/oopsla/Oopsla96TechnicalProgramAbstracts.html#GilLorenz,
OOPSLA '96 Proceedings, ACM SIG-PLAN, October, 1996
$Id$
@@ -368,7 +368,7 @@
Traceback (most recent call last):
...
AttributeError: x
-
+
>>> Acquisition.aq_acquire(c, 'id',
... lambda searched, parent, name, ob, extra: extra)
Traceback (most recent call last):
@@ -409,8 +409,8 @@
>>> Acquisition.aq_self(c) is c
1
-
-
+
+
"""
def test_simple():
@@ -475,7 +475,7 @@
Traceback (most recent call last):
...
AttributeError: x
-
+
>>> a.b.c.aq_acquire('id',
... lambda searched, parent, name, ob, extra: extra)
Traceback (most recent call last):
@@ -497,7 +497,7 @@
>>> Acquisition.aq_acquire(a.b.c, 'y')
42
-
+
>>> Acquisition.aq_acquire(a.b.c, 'id',
... lambda searched, parent, name, ob, extra: extra)
Traceback (most recent call last):
@@ -685,19 +685,19 @@
>>> show(a.a1.a11.a2.a21.aq_inner.aq_parent.aq_inner.aq_parent)
a
-
+
>>> a.a1.a11.a2.a21.aq_chain
[a21, a2, a11, a1, a]
-
+
>>> a.a1.a11.a2.a21.aq_inContextOf(a)
1
-
+
>>> a.a1.a11.a2.a21.aq_inContextOf(a.a2)
1
>>> a.a1.a11.a2.a21.aq_inContextOf(a.a1)
0
-
+
>>> a.a1.a11.a2.a21.aq_acquire('color')
'red'
>>> a.a1.a11.a2.a21.aq_acquire('id')
@@ -712,7 +712,7 @@
>>> a.a1.a11.a2.a21.aq_acquire('color',
... lambda ob, parent, name, v, extra: extra, 1)
'red'
-
+
>>> a.a1.y = 42
>>> a.a1.a11.a2.a21.aq_acquire('y')
42
@@ -796,13 +796,13 @@
>>> show(Acquisition.aq_parent(
... a.a1.a11.a2.a21.aq_inner.aq_parent.aq_inner))
a
-
+
>>> Acquisition.aq_chain(a.a1.a11.a2.a21)
[a21, a2, a11, a1, a]
-
+
>>> Acquisition.aq_chain(a.a1.a11.a2.a21, 1)
[a21, a2, a]
-
+
>>> Acquisition.aq_acquire(a.a1.a11.a2.a21, 'color')
'red'
>>> Acquisition.aq_acquire(a.a1.a11.a2.a21, 'id')
@@ -817,7 +817,7 @@
>>> Acquisition.aq_acquire(a.a1.a11.a2.a21, 'color',
... lambda ob, parent, name, v, extra: extra, 1)
'red'
-
+
>>> a.a1.y = 42
>>> Acquisition.aq_acquire(a.a1.a11.a2.a21, 'y')
42
@@ -829,7 +829,7 @@
"""
-
+
def test_pinball():
r"""
>>> a = I('a')
@@ -943,7 +943,7 @@
a1
|
a
-
+
"""
def test_explicit():
@@ -1003,7 +1003,7 @@
Traceback (most recent call last):
...
AttributeError: x
-
+
>>> a.b.c.aq_acquire('id',
... lambda searched, parent, name, ob, extra: extra)
Traceback (most recent call last):
@@ -1025,7 +1025,7 @@
>>> Acquisition.aq_acquire(a.b.c, 'y')
42
-
+
>>> Acquisition.aq_acquire(a.b.c, 'id',
... lambda searched, parent, name, ob, extra: extra)
Traceback (most recent call last):
@@ -1143,7 +1143,7 @@
Traceback (most recent call last):
...
AttributeError: x
-
+
>>> a.b.c.aq_acquire('id',
... lambda searched, parent, name, ob, extra: extra)
Traceback (most recent call last):
@@ -1165,7 +1165,7 @@
>>> Acquisition.aq_acquire(a.b.c, 'y')
42
-
+
>>> Acquisition.aq_acquire(a.b.c, 'id',
... lambda searched, parent, name, ob, extra: extra)
Traceback (most recent call last):
@@ -1221,7 +1221,7 @@
def test_aq_inContextOf():
- """
+ """
>>> from ExtensionClass import Base
>>> import Acquisition
@@ -1247,9 +1247,9 @@
... raise 'Program error', 'spam'
... except AttributeError: pass
A()
-
+
New test for wrapper comparisons.
-
+
>>> foo = b.a
>>> bar = b.a
>>> foo == bar
@@ -1379,14 +1379,14 @@
[D, C, A]
>>> map(Acquisition.aq_base, Acquisition.aq_chain(A.B.C.D, 1))
[D, C, A]
-
+
>>> A.B.C.D.color
'red'
>>> Acquisition.aq_get(A.B.C.D, "color", None)
'red'
>>> Acquisition.aq_get(A.B.C.D, "color", None, 1)
-
+
"""
def test_explicit_acquisition():
@@ -1414,7 +1414,7 @@
... raise 'Program error', 'spam'
... except AttributeError: pass
A
-
+
"""
def test_creating_wrappers_directly():
@@ -1565,8 +1565,126 @@
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:
@@ -1593,7 +1711,7 @@
def show(x):
print showaq(x).strip()
-
+
def showaq(m_self, indent=''):
rval = ''
obj = m_self
@@ -1631,11 +1749,11 @@
>>> for B in I, E:
... class C1(B):
... pass
- ...
+ ...
... class C2(Base):
... def __del__(self):
... print 'removed'
- ...
+ ...
... a=C1('a')
... a.b = C1('a.b')
... a.b.a = a
@@ -1664,7 +1782,7 @@
... class C:
... def __del__(self):
... print 'removed'
- ...
+ ...
... a=B('a')
... a.b = B('b')
... a.a_b = a.b # circ ref through wrapper
@@ -1891,7 +2009,7 @@
class Location(object):
__parent__ = None
-
+
class ECLocation(ExtensionClass.Base):
__parent__ = None
More information about the checkins
mailing list