Step 2: Make aq_acquire aware of __parent__ pointers, even if the object

isn't acquisition wrapped.
parent dd175e54
......@@ -38,7 +38,8 @@ static PyObject *py__add__, *py__sub__, *py__mul__, *py__div__,
*py__long__, *py__float__, *py__oct__, *py__hex__,
*py__getitem__, *py__setitem__, *py__delitem__,
*py__getslice__, *py__setslice__, *py__delslice__, *py__contains__,
*py__len__, *py__of__, *py__call__, *py__repr__, *py__str__, *py__cmp__;
*py__len__, *py__of__, *py__call__, *py__repr__, *py__str__, *py__cmp__,
*py__parent__;
static PyObject *Acquired=0;
......@@ -82,7 +83,8 @@ init_py_names(void)
INIT_PY_NAME(__repr__);
INIT_PY_NAME(__str__);
INIT_PY_NAME(__cmp__);
INIT_PY_NAME(__parent__);
#undef INIT_PY_NAME
}
......@@ -488,6 +490,7 @@ Wrapper_findattr(Wrapper *self, PyObject *oname,
Py_XDECREF(r); Py_XDECREF(v); Py_XDECREF(tb);
r=NULL;
}
/* normal attribute lookup */
else if ((r=PyObject_GetAttr(self->obj,oname)))
{
if (r==Acquired)
......@@ -522,7 +525,8 @@ Wrapper_findattr(Wrapper *self, PyObject *oname,
PyErr_Clear();
}
if (sco && (*name != '_' || explicit))
/* lookup has failed, acquire it from parent */
if (sco && (*name != '_' || explicit))
return Wrapper_acquire(self, oname, filter, extra, orig, explicit,
containment);
......@@ -535,11 +539,13 @@ Wrapper_acquire(Wrapper *self, PyObject *oname,
PyObject *filter, PyObject *extra, PyObject *orig,
int explicit, int containment)
{
PyObject *r;
PyObject *r, *v, *tb, *__parent__;
int sob=1, sco=1;
if (self->container)
{
/* if the container has an acquisition wrapper itself, we'll use
Wrapper_findattr to progress further */
if (isWrapper(self->container))
{
if (self->obj && isWrapper(self->obj))
......@@ -560,8 +566,36 @@ Wrapper_acquire(Wrapper *self, PyObject *oname,
if (r && has__of__(r)) ASSIGN(r,__of__(r,OBJECT(self)));
return r;
}
/* if the container has a __parent__ pointer, we create an
acquisition wrapper for it accordingly. Then we can proceed
with Wrapper_findattr, just as if the container had an
acquisition wrapper in the first place (see above) */
else if ((__parent__ = PyObject_GetAttr(self->container, py__parent__)))
{
ASSIGN(self->container, newWrapper(self->container, __parent__,
(PyTypeObject*)&Wrappertype));
r=Wrapper_findattr((Wrapper*)self->container,
oname, filter, extra, orig, sob, sco, explicit,
containment);
return r;
}
/* the container is the end of the acquisition chain; if we
can't look up the attribute here, we can't look it up at
all. */
else
{
/* we need to clean up the AttributeError from the previous
getattr (because it has clearly failed) */
/* perhaps it's overkill to only catch AttributeErrors */
PyErr_Fetch(&r,&v,&tb);
if (r && (r != PyExc_AttributeError))
{
PyErr_Restore(r,v,tb);
return NULL;
}
Py_XDECREF(r); Py_XDECREF(v); Py_XDECREF(tb);
r=NULL;
if ((r=PyObject_GetAttr(self->container,oname))) {
if (r == Acquired) {
Py_DECREF(r);
......@@ -1343,8 +1377,7 @@ static PyObject *
capi_aq_acquire(PyObject *self, PyObject *name, PyObject *filter,
PyObject *extra, int explicit, PyObject *defalt, int containment)
{
PyObject *result;
PyObject *result, *__parent__;
if (filter==Py_None) filter=0;
......@@ -1356,7 +1389,17 @@ capi_aq_acquire(PyObject *self, PyObject *name, PyObject *filter,
WRAPPER(self)->ob_type==(PyTypeObject*)&Wrappertype,
explicit, containment);
/* Not wrapped and no filter, so just getattr */
/* Not wrapped; check if we have a __parent__ pointer. If that's
the case, we create a wrapper and pretend it's business as
usual */
if ((__parent__ = PyObject_GetAttr(self, py__parent__))) {
self = newWrapper(self, __parent__, (PyTypeObject*)&Wrappertype);
return Wrapper_findattr(
WRAPPER(self), name, filter, extra, OBJECT(self), 1, 1,
explicit, containment);
}
/* no filter, and no __parent__, so just getattr */
if (! filter) return PyObject_GetAttr(self, name);
/* Crap, we've got to construct a wrapper so we can use Wrapper_findattr */
......
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# 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.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Acquisition test cases (and useful examples)
Acquisition [1] is a mechanism that allows objects to obtain
attributes from their environment. It is similar to inheritence,
except that, rather than traversing an inheritence hierarchy
to obtain attributes, a containment hierarchy is traversed.
The "ExtensionClass":ExtensionClass.html. release includes mix-in
extension base classes that can be used to add acquisition as a
feature to extension subclasses. These mix-in classes use the
context-wrapping feature of ExtensionClasses to implement
acquisition. Consider the following example::
>>> import ExtensionClass, Acquisition
>>> class C(ExtensionClass.Base):
... color='red'
>>> class A(Acquisition.Implicit):
... def report(self):
... print self.color
>>> a = A()
>>> c = C()
>>> c.a = a
>>> c.a.report()
red
>>> d = C()
>>> d.color = 'green'
>>> d.a = a
>>> d.a.report()
green
>>> a.report() # raises an attribute error
Traceback (most recent call last):
...
AttributeError: color
The class 'A' inherits acquisition behavior from
'Acquisition.Implicit'. The object, 'a', "has" the color of
objects 'c' and 'd' when it is accessed through them, but it
has no color by itself. The object 'a' obtains attributes
from it's environment, where it's environment is defined by
the access path used to reach 'a'.
Acquisition wrappers
When an object that supports acquisition is accessed through
an extension class instance, a special object, called an
acquisition wrapper, is returned. In the example above, the
expression 'c.a' returns an acquisition wrapper that
contains references to both 'c' and 'a'. It is this wrapper
that performs attribute lookup in 'c' when an attribute
cannot be found in 'a'.
Aquisition wrappers provide access to the wrapped objects
through the attributes 'aq_parent', 'aq_self', 'aq_base'.
In the example above, the expressions::
>>> c.a.aq_parent is c
1
and::
>>> c.a.aq_self is a
1
both evaluate to true, but the expression::
>>> c.a is a
0
evaluates to false, because the expression 'c.a' evaluates
to an acquisition wrapper around 'c' and 'a', not 'a' itself.
The attribute 'aq_base' is similar to 'aq_self'. Wrappers may be
nested and 'aq_self' may be a wrapped object. The 'aq_base'
attribute is the underlying object with all wrappers removed.
Acquisition Control
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'.
Controlled Acquisition
A class (or instance) can provide attribute by attribute control
over acquisition. This is done by:
- subclassing from 'Acquisition.Explicit', and
- setting all attributes that should be acquired to the special
value: 'Acquisition.Acquired'. Setting an attribute to this
value also allows inherited attributes to be overridden with
acquired ones.
For example, in::
>>> class E(Acquisition.Explicit):
... id = 1
... secret = 2
... color = Acquisition.Acquired
... __roles__ = Acquisition.Acquired
The *only* attributes that are automatically acquired from
containing objects are 'color', and '__roles__'.
>>> c = C()
>>> c.foo = 'foo'
>>> c.e = E()
>>> c.e.color
'red'
>>> c.e.foo
Traceback (most recent call last):
...
AttributeError: foo
Note also that the '__roles__' attribute is acquired even
though it's name begins with an underscore:
>>> c.__roles__ = 'Manager', 'Member'
>>> c.e.__roles__
('Manager', 'Member')
In fact, the special 'Acquisition.Acquired' value can be used
in 'Acquisition.Implicit' objects to implicitly acquire
selected objects that smell like private objects.
>>> class I(Acquisition.Implicit):
... __roles__ = Acquisition.Acquired
>>> c.x = C()
>>> c.x.__roles__
Traceback (most recent call last):
...
AttributeError: __roles__
>>> c.x = I()
>>> c.x.__roles__
('Manager', 'Member')
Filtered Acquisition
The acquisition method, 'aq_acquire', accepts two optional
arguments. The first of the additional arguments is a
"filtering" function that is used when considering whether to
acquire an object. The second of the additional arguments is an
object that is passed as extra data when calling the filtering
function and which defaults to 'None'.
The filter function is called with five arguments:
- The object that the 'aq_acquire' method was called on,
- The object where an object was found,
- The name of the object, as passed to 'aq_acquire',
- The object found, and
- The extra data passed to 'aq_acquire'.
If the filter returns a true object that the object found is
returned, otherwise, the acquisition search continues.
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!
The filtered acquisition in the last line skips over the first
attribute it finds with the name 'p', because the attribute
doesn't satisfy the condition given in the filter.
Acquisition and methods
Python methods of objects that support acquisition can use
acquired attributes as in the 'report' method of the first example
above. When a Python method is called on an object that is
wrapped by an acquisition wrapper, the wrapper is passed to the
method as the first argument. This rule also applies to
user-defined method types and to C methods defined in pure mix-in
classes.
Unfortunately, C methods defined in extension base classes that
define their own data structures, cannot use aquired attributes at
this time. This is because wrapper objects do not conform to the
data structures expected by these methods.
Acquiring Acquiring objects
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"
>>> a.b.c = C("c")
>>> a.b.c.color = "red"
>>> a.b.c.pref = "eggs"
>>> a.x = C("x")
>>> o = a.b.c.x
The expression 'o.color' might be expected to return '"red"'. In
earlier versions of ExtensionClass, however, this expression
failed. Acquired acquiring objects did not acquire from the
environment they were accessed in, because objects were only
wrapped when they were first found, and were not rewrapped as they
were passed down the acquisition tree.
In the current release of ExtensionClass, the expression "o.color"
does indeed return '"red"'.
>>> o.color
'red'
When searching for an attribute in 'o', objects are searched in
the order 'x', 'a', 'b', 'c'. So, for example, the expression,
'o.pref' returns '"spam"', not '"eggs"'::
>>> o.pref
'spam'
In earlier releases of ExtensionClass, the attempt to get the
'pref' attribute from 'o' would have failed.
If desired, the current rules for looking up attributes in complex
expressions can best be understood through repeated application of
the '__of__' method:
'a.x' -- 'x.__of__(a)'
'a.b' -- 'b.__of__(a)'
'a.b.x' -- 'x.__of__(a).__of__(b.__of__(a))'
'a.b.c' -- 'c.__of__(b.__of__(a))'
'a.b.c.x' --
'x.__of__(a).__of__(b.__of__(a)).__of__(c.__of__(b.__of__(a)))'
and by keeping in mind that attribute lookup in a wrapper
is done by trying to lookup the attribute in the wrapped object
first and then in the parent object. In the expressions above
involving the '__of__' method, lookup proceeds from left to right.
Note that heuristics are used to avoid most of the repeated
lookups. For example, in the expression: 'a.b.c.x.foo', the object
'a' is searched no more than once, even though it is wrapped three
times.
.. [1] Gil, J., Lorenz, D.,
"Environmental Acquisition--A New Inheritance-Like Abstraction Mechanism",
http://www.bell-labs.com/people/cope/oopsla/Oopsla96TechnicalProgramAbstracts.html#GilLorenz,
OOPSLA '96 Proceedings, ACM SIG-PLAN, October, 1996
$Id$
"""
import ExtensionClass
import Acquisition
class I(Acquisition.Implicit):
def __init__(self, id):
self.id = id
def __repr__(self):
return self.id
class E(Acquisition.Explicit):
def __init__(self, id):
self.id = id
def __repr__(self):
return self.id
def test_unwrapped():
"""
>>> c = I('unwrapped')
>>> show(c)
unwrapped
>>> c.aq_parent
Traceback (most recent call last):
...
AttributeError: aq_parent
>>> Acquisition.aq_acquire(c, 'id')
'unwrapped'
>>> Acquisition.aq_acquire(c, 'x')
Traceback (most recent call last):
...
AttributeError: x
>>> Acquisition.aq_acquire(c, 'id',
... lambda searched, parent, name, ob, extra: extra)
Traceback (most recent call last):
...
AttributeError: id
>>> Acquisition.aq_acquire(c, 'id',
... lambda searched, parent, name, ob, extra: extra,
... 1)
'unwrapped'
>>> Acquisition.aq_base(c) is c
1
>>> Acquisition.aq_chain(c)
[unwrapped]
>>> Acquisition.aq_chain(c, 1)
[unwrapped]
>>> Acquisition.aq_get(c, 'id')
'unwrapped'
>>> Acquisition.aq_get(c, 'x')
Traceback (most recent call last):
...
AttributeError: x
>>> Acquisition.aq_get(c, 'x', 'foo')
'foo'
>>> Acquisition.aq_get(c, 'x', 'foo', 1)
'foo'
>>> Acquisition.aq_inner(c) is c
1
>>> Acquisition.aq_parent(c)
>>> Acquisition.aq_self(c) is c
1
"""
def test_simple():
"""
>>> a = I('a')
>>> a.y = 42
>>> a.b = I('b')
>>> a.b.c = I('c')
>>> show(a.b.c)
c
|
b
|
a
>>> show(a.b.c.aq_parent)
b
|
a
>>> show(a.b.c.aq_self)
c
>>> show(a.b.c.aq_base)
c
>>> show(a.b.c.aq_inner)
c
|
b
|
a
>>> a.b.c.y
42
>>> a.b.c.aq_chain
[c, b, a]
>>> a.b.c.aq_inContextOf(a)
1
>>> a.b.c.aq_inContextOf(a.b)
1
>>> a.b.c.aq_inContextOf(a.b.c)
1
>>> a.b.c.aq_acquire('y')
42
>>> a.b.c.aq_acquire('id')
'c'
>>> a.b.c.aq_acquire('x')
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):
...
AttributeError: id
>>> Acquisition.aq_acquire(a.b.c, 'id',
... lambda searched, parent, name, ob, extra: extra,
... 1)
'c'
>>> Acquisition.aq_acquire(a.b.c, 'id')
'c'
>>> Acquisition.aq_acquire(a.b.c, 'x')
Traceback (most recent call last):
...
AttributeError: x
>>> 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):
...
AttributeError: id
>>> Acquisition.aq_acquire(a.b.c, 'id',
... lambda searched, parent, name, ob, extra: extra,
... 1)
'c'
>>> show(Acquisition.aq_base(a.b.c))
c
>>> Acquisition.aq_chain(a.b.c)
[c, b, a]
>>> Acquisition.aq_chain(a.b.c, 1)
[c, b, a]
>>> Acquisition.aq_get(a.b.c, 'id')
'c'
>>> Acquisition.aq_get(a.b.c, 'x')
Traceback (most recent call last):
...
AttributeError: x
>>> Acquisition.aq_get(a.b.c, 'x', 'foo')
'foo'
>>> Acquisition.aq_get(a.b.c, 'x', 'foo', 1)
'foo'
>>> show(Acquisition.aq_inner(a.b.c))
c
|
b
|
a
>>> show(Acquisition.aq_parent(a.b.c))
b
|
a
>>> show(Acquisition.aq_self(a.b.c))
c
"""
def test__of__exception():
"""
Wrapper_findattr did't check for an exception in a user defined
__of__ method before passing the result to the filter. In this
case the 'value' argument of the filter was NULL, which caused
a segfault when being accessed.
>>> class UserError(Exception):
... pass
...
>>> class X(Acquisition.Implicit):
... def __of__(self, parent):
... if Acquisition.aq_base(parent) is not parent:
... raise UserError, 'ack'
... return X.inheritedAttribute('__of__')(self, parent)
...
>>> a = I('a')
>>> a.b = I('b')
>>> a.b.x = X('x')
>>> Acquisition.aq_acquire(a.b, 'x',
... lambda self, object, name, value, extra: repr(value))
Traceback (most recent call last):
...
UserError: ack
"""
def test_muliple():
r"""
>>> a = I('a')
>>> a.color = 'red'
>>> a.a1 = I('a1')
>>> a.a1.color = 'green'
>>> a.a1.a11 = I('a11')
>>> a.a2 = I('a2')
>>> a.a2.a21 = I('a21')
>>> show(a.a1.a11.a2.a21)
a21
|
(a2)
| \
| (a2)
| | \
| | a2
| | |
| | a
| |
| a1
| |
| a
|
a11
|
a1
|
a
>>> a.a1.a11.a2.a21.color
'red'
>>> show(a.a1.a11.a2.a21.aq_parent)
(a2)
| \
| (a2)
| | \
| | a2
| | |
| | a
| |
| a1
| |
| a
|
a11
|
a1
|
a
>>> show(a.a1.a11.a2.a21.aq_parent.aq_parent)
a11
|
a1
|
a
>>> show(a.a1.a11.a2.a21.aq_self)
a21
>>> show(a.a1.a11.a2.a21.aq_parent.aq_self)
(a2)
| \
| a2
| |
| a
|
a1
|
a
>>> show(a.a1.a11.a2.a21.aq_base)
a21
>>> show(a.a1.a11.a2.a21.aq_inner)
a21
|
(a2)
| \
| (a2)
| | \
| | a2
| | |
| | a
| |
| a1
| |
| a
|
a11
|
a1
|
a
>>> show(a.a1.a11.a2.a21.aq_inner.aq_parent.aq_inner)
a2
|
a
>>> 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')
'a21'
>>> a.a1.a11.a2.a21.aq_acquire('color',
... lambda ob, parent, name, v, extra: extra)
Traceback (most recent call last):
...
AttributeError: color
>>> 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
>>> a.a1.a11.a2.a21.aq_acquire('y', containment=1)
Traceback (most recent call last):
...
AttributeError: y
Much of the same, but with methods:
>>> show(Acquisition.aq_parent(a.a1.a11.a2.a21))
(a2)
| \
| (a2)
| | \
| | a2
| | |
| | a
| |
| a1
| |
| a
|
a11
|
a1
|
a
>>> show(Acquisition.aq_parent(a.a1.a11.a2.a21.aq_parent))
a11
|
a1
|
a
>>> show(Acquisition.aq_self(a.a1.a11.a2.a21))
a21
>>> show(Acquisition.aq_self(a.a1.a11.a2.a21.aq_parent))
(a2)
| \
| a2
| |
| a
|
a1
|
a
>>> show(Acquisition.aq_base(a.a1.a11.a2.a21))
a21
>>> show(Acquisition.aq_inner(a.a1.a11.a2.a21))
a21
|
(a2)
| \
| (a2)
| | \
| | a2
| | |
| | a
| |
| a1
| |
| a
|
a11
|
a1
|
a
>>> show(Acquisition.aq_inner(a.a1.a11.a2.a21.aq_inner.aq_parent))
a2
|
a
>>> 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')
'a21'
>>> Acquisition.aq_acquire(a.a1.a11.a2.a21, 'color',
... lambda ob, parent, name, v, extra: extra)
Traceback (most recent call last):
...
AttributeError: color
>>> 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
>>> Acquisition.aq_acquire(a.a1.a11.a2.a21, 'y', containment=1)
Traceback (most recent call last):
...
AttributeError: y
"""
def test_pinball():
r"""
>>> a = I('a')
>>> a.a1 = I('a1')
>>> a.a1.a11 = I('a11')
>>> a.a1.a12 = I('a12')
>>> a.a2 = I('a2')
>>> a.a2.a21 = I('a21')
>>> a.a2.a22 = I('a22')
>>> show(a.a1.a11.a1.a12.a2.a21.a2.a22)
a22
|
(a2)
| \
| (a2)
| | \
| | a2
| | |
| | a
| |
| (a2)
| | \
| | (a2)
| | | \
| | | a2
| | | |
| | | a
| | |
| | (a1)
| | | \
| | | (a1)
| | | | \
| | | | a1
| | | | |
| | | | a
| | | |
| | | a1
| | | |
| | | a
| | |
| | a11
| | |
| | a1
| | |
| | a
| |
| a12
| |
| (a1)
| | \
| | (a1)
| | | \
| | | a1
| | | |
| | | a
| | |
| | a1
| | |
| | a
| |
| a11
| |
| a1
| |
| a
|
a21
|
(a2)
| \
| (a2)
| | \
| | a2
| | |
| | a
| |
| (a1)
| | \
| | (a1)
| | | \
| | | a1
| | | |
| | | a
| | |
| | a1
| | |
| | a
| |
| a11
| |
| a1
| |
| a
|
a12
|
(a1)
| \
| (a1)
| | \
| | a1
| | |
| | a
| |
| a1
| |
| a
|
a11
|
a1
|
a
"""
def test_explicit():
"""
>>> a = E('a')
>>> a.y = 42
>>> a.b = E('b')
>>> a.b.c = E('c')
>>> show(a.b.c)
c
|
b
|
a
>>> show(a.b.c.aq_parent)
b
|
a
>>> show(a.b.c.aq_self)
c
>>> show(a.b.c.aq_base)
c
>>> show(a.b.c.aq_inner)
c
|
b
|
a
>>> a.b.c.y
Traceback (most recent call last):
...
AttributeError: y
>>> a.b.c.aq_chain
[c, b, a]
>>> a.b.c.aq_inContextOf(a)
1
>>> a.b.c.aq_inContextOf(a.b)
1
>>> a.b.c.aq_inContextOf(a.b.c)
1
>>> a.b.c.aq_acquire('y')
42
>>> a.b.c.aq_acquire('id')
'c'
>>> a.b.c.aq_acquire('x')
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):
...
AttributeError: id
>>> Acquisition.aq_acquire(a.b.c, 'id',
... lambda searched, parent, name, ob, extra: extra,
... 1)
'c'
>>> Acquisition.aq_acquire(a.b.c, 'id')
'c'
>>> Acquisition.aq_acquire(a.b.c, 'x')
Traceback (most recent call last):
...
AttributeError: x
>>> 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):
...
AttributeError: id
>>> Acquisition.aq_acquire(a.b.c, 'id',
... lambda searched, parent, name, ob, extra: extra,
... 1)
'c'
>>> show(Acquisition.aq_base(a.b.c))
c
>>> Acquisition.aq_chain(a.b.c)
[c, b, a]
>>> Acquisition.aq_chain(a.b.c, 1)
[c, b, a]
>>> Acquisition.aq_get(a.b.c, 'id')
'c'
>>> Acquisition.aq_get(a.b.c, 'x')
Traceback (most recent call last):
...
AttributeError: x
>>> Acquisition.aq_get(a.b.c, 'y')
42
>>> Acquisition.aq_get(a.b.c, 'x', 'foo')
'foo'
>>> Acquisition.aq_get(a.b.c, 'x', 'foo', 1)
'foo'
>>> show(Acquisition.aq_inner(a.b.c))
c
|
b
|
a
>>> show(Acquisition.aq_parent(a.b.c))
b
|
a
>>> show(Acquisition.aq_self(a.b.c))
c
"""
def test_mixed_explicit_and_explicit():
"""
>>> a = I('a')
>>> a.y = 42
>>> a.b = E('b')
>>> a.b.z = 3
>>> a.b.c = I('c')
>>> show(a.b.c)
c
|
b
|
a
>>> show(a.b.c.aq_parent)
b
|
a
>>> show(a.b.c.aq_self)
c
>>> show(a.b.c.aq_base)
c
>>> show(a.b.c.aq_inner)
c
|
b
|
a
>>> a.b.c.y
42
>>> a.b.c.z
3
>>> a.b.c.aq_chain
[c, b, a]
>>> a.b.c.aq_inContextOf(a)
1
>>> a.b.c.aq_inContextOf(a.b)
1
>>> a.b.c.aq_inContextOf(a.b.c)
1
>>> a.b.c.aq_acquire('y')
42
>>> a.b.c.aq_acquire('z')
3
>>> a.b.c.aq_acquire('z', explicit=False)
3
>>> a.b.c.aq_acquire('id')
'c'
>>> a.b.c.aq_acquire('x')
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):
...
AttributeError: id
>>> Acquisition.aq_acquire(a.b.c, 'id',
... lambda searched, parent, name, ob, extra: extra,
... 1)
'c'
>>> Acquisition.aq_acquire(a.b.c, 'id')
'c'
>>> Acquisition.aq_acquire(a.b.c, 'x')
Traceback (most recent call last):
...
AttributeError: x
>>> 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):
...
AttributeError: id
>>> Acquisition.aq_acquire(a.b.c, 'id',
... lambda searched, parent, name, ob, extra: extra,
... 1)
'c'
>>> show(Acquisition.aq_base(a.b.c))
c
>>> Acquisition.aq_chain(a.b.c)
[c, b, a]
>>> Acquisition.aq_chain(a.b.c, 1)
[c, b, a]
>>> Acquisition.aq_get(a.b.c, 'id')
'c'
>>> Acquisition.aq_get(a.b.c, 'x')
Traceback (most recent call last):
...
AttributeError: x
>>> Acquisition.aq_get(a.b.c, 'y')
42
>>> Acquisition.aq_get(a.b.c, 'x', 'foo')
'foo'
>>> Acquisition.aq_get(a.b.c, 'x', 'foo', 1)
'foo'
>>> show(Acquisition.aq_inner(a.b.c))
c
|
b
|
a
>>> show(Acquisition.aq_parent(a.b.c))
b
|
a
>>> show(Acquisition.aq_self(a.b.c))
c
"""
def old_tests():
"""
>>> from ExtensionClass import Base
>>> import Acquisition
>>> class B(Base):
... color='red'
>>> class A(Acquisition.Implicit):
... def hi(self):
... print "%s()" % self.__class__.__name__, self.color
>>> b=B()
>>> b.a=A()
>>> b.a.hi()
A() red
>>> b.a.color='green'
>>> b.a.hi()
A() green
>>> try:
... A().hi()
... raise 'Program error', 'spam'
... except AttributeError: pass
A()
New test for wrapper comparisons.
>>> foo = b.a
>>> bar = b.a
>>> foo == bar
1
>>> c = A()
>>> b.c = c
>>> b.c.d = c
>>> b.c.d == c
1
>>> b.c.d == b.c
1
>>> b.c == c
1
>>> def checkContext(self, o):
... # Python equivalent to aq_inContextOf
... from Acquisition import aq_base, aq_parent, aq_inner
... subob = self
... o = aq_base(o)
... while 1:
... if aq_base(subob) is o: return 1
... self = aq_inner(subob)
... if self is None: break
... subob = aq_parent(self)
... if subob is None: break
>>> checkContext(b.c, b)
1
>>> not checkContext(b.c, b.a)
1
>>> b.a.aq_inContextOf(b)
1
>>> b.c.aq_inContextOf(b)
1
>>> b.c.d.aq_inContextOf(b)
1
>>> b.c.d.aq_inContextOf(c)
1
>>> b.c.d.aq_inContextOf(b.c)
1
>>> not b.c.aq_inContextOf(foo)
1
>>> not b.c.aq_inContextOf(b.a)
1
>>> not b.a.aq_inContextOf('somestring')
1
"""
def test_AqAlg():
"""
>>> A=I('A')
>>> A.B=I('B')
>>> A.B.color='red'
>>> A.C=I('C')
>>> A.C.D=I('D')
>>> A
A
>>> Acquisition.aq_chain(A)
[A]
>>> Acquisition.aq_chain(A, 1)
[A]
>>> map(Acquisition.aq_base, Acquisition.aq_chain(A, 1))
[A]
>>> A.C
C
>>> Acquisition.aq_chain(A.C)
[C, A]
>>> Acquisition.aq_chain(A.C, 1)
[C, A]
>>> map(Acquisition.aq_base, Acquisition.aq_chain(A.C, 1))
[C, A]
>>> A.C.D
D
>>> Acquisition.aq_chain(A.C.D)
[D, C, A]
>>> Acquisition.aq_chain(A.C.D, 1)
[D, C, A]
>>> map(Acquisition.aq_base, Acquisition.aq_chain(A.C.D, 1))
[D, C, A]
>>> A.B.C
C
>>> Acquisition.aq_chain(A.B.C)
[C, B, A]
>>> Acquisition.aq_chain(A.B.C, 1)
[C, A]
>>> map(Acquisition.aq_base, Acquisition.aq_chain(A.B.C, 1))
[C, A]
>>> A.B.C.D
D
>>> Acquisition.aq_chain(A.B.C.D)
[D, C, B, A]
>>> Acquisition.aq_chain(A.B.C.D, 1)
[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():
"""
>>> from ExtensionClass import Base
>>> import Acquisition
>>> class B(Base):
... color='red'
>>> class A(Acquisition.Explicit):
... def hi(self):
... print self.__class__.__name__, self.acquire('color')
>>> b=B()
>>> b.a=A()
>>> b.a.hi()
A red
>>> b.a.color='green'
>>> b.a.hi()
A green
>>> try:
... A().hi()
... raise 'Program error', 'spam'
... except AttributeError: pass
A
"""
def test_creating_wrappers_directly():
"""
>>> from ExtensionClass import Base
>>> from Acquisition import ImplicitAcquisitionWrapper
>>> class B(Base):
... pass
>>> a = B()
>>> a.color = 'red'
>>> a.b = B()
>>> w = ImplicitAcquisitionWrapper(a.b, a)
>>> w.color
'red'
>>> w = ImplicitAcquisitionWrapper(a.b)
Traceback (most recent call last):
...
TypeError: __init__() takes exactly 2 arguments (1 given)
We can reassign aq_parent
>>> x = B()
>>> x.color = 'green'
>>> w.aq_parent = x
>>> w.color
'green'
>>> w = ImplicitAcquisitionWrapper()
Traceback (most recent call last):
...
TypeError: __init__() takes exactly 2 arguments (0 given)
>>> w = ImplicitAcquisitionWrapper(obj=1)
Traceback (most recent call last):
...
TypeError: kwyword arguments not allowed
"""
def test_cant_pickle_acquisition_wrappers_classic():
"""
>>> import pickle
>>> class X:
... def __getstate__(self):
... return 1
We shouldn't be able to pickle wrappers:
>>> from Acquisition import ImplicitAcquisitionWrapper
>>> w = ImplicitAcquisitionWrapper(X(), X())
>>> pickle.dumps(w)
Traceback (most recent call last):
...
TypeError: Can't pickle objects in acquisition wrappers.
But that's not enough. We need to defeat persistence as well. :)
This is tricky. We want to generate the error in __getstate__, not
in the attr access, as attribute errors are too-often hidden:
>>> getstate = w.__getstate__
>>> getstate()
Traceback (most recent call last):
...
TypeError: Can't pickle objects in acquisition wrappers.
We shouldn't be able to pickle wrappers:
>>> from Acquisition import ExplicitAcquisitionWrapper
>>> w = ExplicitAcquisitionWrapper(X(), X())
>>> pickle.dumps(w)
Traceback (most recent call last):
...
TypeError: Can't pickle objects in acquisition wrappers.
But that's not enough. We need to defeat persistence as well. :)
This is tricky. We want to generate the error in __getstate__, not
in the attr access, as attribute errors are too-often hidden:
>>> getstate = w.__getstate__
>>> getstate()
Traceback (most recent call last):
...
TypeError: Can't pickle objects in acquisition wrappers.
"""
def test_cant_pickle_acquisition_wrappers_newstyle():
"""
>>> import pickle
>>> class X(object):
... def __getstate__(self):
... return 1
We shouldn't be able to pickle wrappers:
>>> from Acquisition import ImplicitAcquisitionWrapper
>>> w = ImplicitAcquisitionWrapper(X(), X())
>>> pickle.dumps(w)
Traceback (most recent call last):
...
TypeError: Can't pickle objects in acquisition wrappers.
But that's not enough. We need to defeat persistence as well. :)
This is tricky. We want to generate the error in __getstate__, not
in the attr access, as attribute errors are too-often hidden:
>>> getstate = w.__getstate__
>>> getstate()
Traceback (most recent call last):
...
TypeError: Can't pickle objects in acquisition wrappers.
We shouldn't be able to pickle wrappers:
>>> from Acquisition import ExplicitAcquisitionWrapper
>>> w = ExplicitAcquisitionWrapper(X(), X())
>>> pickle.dumps(w)
Traceback (most recent call last):
...
TypeError: Can't pickle objects in acquisition wrappers.
But that's not enough. We need to defeat persistence as well. :)
This is tricky. We want to generate the error in __getstate__, not
in the attr access, as attribute errors are too-often hidden:
>>> getstate = w.__getstate__
>>> getstate()
Traceback (most recent call last):
...
TypeError: Can't pickle objects in acquisition wrappers.
"""
def test_z3interfaces():
"""
>>> from zope.interface.verify import verifyClass
Explicit and Implicit implement IAcquirer:
>>> from Acquisition import Explicit
>>> from Acquisition import Implicit
>>> from Acquisition.interfaces import IAcquirer
>>> verifyClass(IAcquirer, Explicit)
True
>>> verifyClass(IAcquirer, Implicit)
True
ExplicitAcquisitionWrapper and ImplicitAcquisitionWrapper implement
IAcquisitionWrapper:
>>> from Acquisition import ExplicitAcquisitionWrapper
>>> from Acquisition import ImplicitAcquisitionWrapper
>>> from Acquisition.interfaces import IAcquisitionWrapper
>>> verifyClass(IAcquisitionWrapper, ExplicitAcquisitionWrapper)
True
>>> verifyClass(IAcquisitionWrapper, ImplicitAcquisitionWrapper)
True
"""
def show(x):
print showaq(x).strip()
def showaq(m_self, indent=''):
rval = ''
obj = m_self
base = getattr(obj, 'aq_base', obj)
try: id = base.id
except: id = str(base)
try: id = id()
except: pass
if hasattr(obj, 'aq_self'):
if hasattr(obj.aq_self, 'aq_self'):
rval = rval + indent + "(" + id + ")\n"
rval = rval + indent + "| \\\n"
rval = rval + showaq(obj.aq_self, '| ' + indent)
rval = rval + indent + "|\n"
rval = rval + showaq(obj.aq_parent, indent)
elif hasattr(obj, 'aq_parent'):
rval = rval + indent + id + "\n"
rval = rval + indent + "|\n"
rval = rval + showaq(obj.aq_parent, indent)
else:
rval = rval + indent + id + "\n"
return rval
def test_Basic_gc():
"""Test to make sure that EC instances participate in GC
>>> from ExtensionClass import Base
>>> import gc
>>> thresholds = gc.get_threshold()
>>> gc.set_threshold(0)
>>> 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
... a.b.c = C2()
... ignore = gc.collect()
... del a
... removed = gc.collect()
... print removed > 0
removed
True
removed
True
>>> gc.set_threshold(*thresholds)
"""
def test_Wrapper_gc():
"""Test to make sure that EC instances participate in GC
>>> import gc
>>> thresholds = gc.get_threshold()
>>> gc.set_threshold(0)
>>> for B in I, E:
... class C:
... def __del__(self):
... print 'removed'
...
... a=B('a')
... a.b = B('b')
... a.a_b = a.b # circ ref through wrapper
... a.b.c = C()
... ignored = gc.collect()
... del a
... removed = gc.collect()
... removed > 0
removed
True
removed
True
>>> gc.set_threshold(*thresholds)
"""
def test_proxying():
"""Make sure that recent python slots are proxied.
>>> import Acquisition
>>> class Impl(Acquisition.Implicit):
... pass
>>> class C(Acquisition.Implicit):
... def __getitem__(self, key):
... print 'getitem', key
... if key == 4:
... raise IndexError
... return key
... def __contains__(self, key):
... print 'contains', repr(key)
... return key == 5
The naked class behaves like this:
>>> c = C()
>>> 3 in c
contains 3
False
>>> 5 in c
contains 5
True
Let's put c in the context of i:
>>> i = Impl()
>>> i.c = c
Now check that __contains__ is properly used:
>>> 3 in i.c # c.__of__(i)
contains 3
False
>>> 5 in i.c
contains 5
True
"""
class Location(object):
__parent__ = None
class ECLocation(ExtensionClass.Base):
__parent__ = None
def test___parent__no_wrappers():
"""
Acquisition also works with objects that aren't wrappers, as long
as they have __parent__ pointers. Let's take a hierarchy like
z --isParent--> y --isParent--> x:
>>> x = Location()
>>> y = Location()
>>> z = Location()
>>> x.__parent__ = y
>>> y.__parent__ = z
and some attributes that we want to acquire:
>>> x.hello = 'world'
>>> y.foo = 42
>>> z.foo = 43 # this should not be found
>>> z.bar = 3.145
``aq_acquire`` works we know it from implicit/acquisition wrappers:
>>> Acquisition.aq_acquire(x, 'hello')
'world'
>>> Acquisition.aq_acquire(x, 'foo')
42
>>> Acquisition.aq_acquire(x, 'bar')
3.145
TODO aq_parent, aq_chain
"""
def test_implicit_wrapper_as___parent__():
"""
Let's do the same test again, only now not all objects are of the
same kind and link to each other via __parent__ pointers. The
root is a stupid ExtensionClass object:
>>> class Root(ExtensionClass.Base):
... bar = 3.145
>>> z = Root()
The intermediate parent is an object that supports implicit
acquisition. We bind it to the root via the __of__ protocol:
>>> class ImplWrapper(Acquisition.Implicit):
... foo = 42
>>> y = ImplWrapper().__of__(z)
The child object is again a simple object with a simple __parent__
pointer:
>>> x = Location()
>>> x.hello = 'world'
>>> x.__parent__ = y
``aq_acquire`` works as expected from implicit/acquisition
wrappers:
>>> Acquisition.aq_acquire(x, 'hello')
'world'
>>> Acquisition.aq_acquire(x, 'foo')
42
>>> Acquisition.aq_acquire(x, 'bar')
3.145
Note that also the (implicit) acquisition wrapper has a __parent__
pointer, which is automatically computed from the acquisition
container (it's identical to aq_parent):
>>> y.__parent__ is z
True
TODO aq_parent, aq_chain
"""
def test_explicit_wrapper_as___parent__():
"""
Let's do this test yet another time, with an explicit wrapper:
>>> class Root(ExtensionClass.Base):
... bar = 3.145
>>> z = Root()
The intermediate parent is an object that supports implicit
acquisition. We bind it to the root via the __of__ protocol:
>>> class ExplWrapper(Acquisition.Explicit):
... foo = 42
>>> y = ExplWrapper().__of__(z)
The child object is again a simple object with a simple __parent__
pointer:
>>> x = Location()
>>> x.hello = 'world'
>>> x.__parent__ = y
``aq_acquire`` works as expected from implicit/acquisition
wrappers:
>>> Acquisition.aq_acquire(x, 'hello')
'world'
>>> Acquisition.aq_acquire(x, 'foo')
42
>>> Acquisition.aq_acquire(x, 'bar')
3.145
Note that also the (implicit) acquisition wrapper has a __parent__
pointer, which is automatically computed from the acquisition
container (it's identical to aq_parent):
>>> y.__parent__ is z
True
TODO aq_parent, aq_chain
"""
import unittest
from zope.testing.doctest import DocTestSuite
def test_suite():
return unittest.TestSuite((
DocTestSuite(),
))
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment