Commit f7f98754 authored by Boxiang Sun's avatar Boxiang Sun

Original implementation

parents
*.pyc
*.pyo
*.so
.installed.cfg
*.egg-info/
bin/
develop-eggs/
eggs/
parts/
build/
language: python
sudo: false
python:
- 2.6
- 2.7
install:
- python bootstrap.py
- bin/buildout
script:
- bin/test -v1
notifications:
email: false
Changelog
=========
2.13.9 (2015-02-17)
-------------------
- Tolerate Unicode attribute names (ASCII only). LP #143358.
- Make module-level ``aq_acquire`` API respect the ``default`` parameter.
LP #1387363.
- Don't raise an attribute error for ``__iter__`` if the fallback to
``__getitem__`` succeeds. LP #1155760.
2.13.8 (2011-06-11)
-------------------
- Fixed a segfault on 64bit platforms when providing the `explicit` argument to
the aq_acquire method of an Acquisition wrapper. Thx to LP #675064 for the
hint to the solution. The code passed an int instead of a pointer into a
function.
2.13.7 (2011-03-02)
-------------------
- Fixed bug: When an object did not implement ``__unicode__``, calling
``unicode(wrapped)`` was calling ``__str__`` with an unwrapped ``self``.
2.13.6 (2011-02-19)
-------------------
- Add ``aq_explicit`` to ``IAcquisitionWrapper``.
- Fixed bug: ``unicode(wrapped)`` was not calling a ``__unicode__``
method on wrapped objects.
2.13.5 (2010-09-29)
-------------------
- Fixed unit tests that failed on 64bit Python on Windows machines.
2.13.4 (2010-08-31)
-------------------
- LP 623665: Fixed typo in Acquisition.h.
2.13.3 (2010-04-19)
-------------------
- Use the doctest module from the standard library and no longer depend on
zope.testing.
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)
-------------------
- Update to include ExtensionClass 2.13.0.
- Fix the ``tp_name`` of the ImplicitAcquisitionWrapper and
ExplicitAcquisitionWrapper to match their Python visible names and thus have
a correct ``__name__``.
- Expand the ``tp_name`` of our extension types to hold the fully qualified
name. This ensures classes have their ``__module__`` set correctly.
2.13.0 (2010-02-14)
-------------------
- Added support for method cache in Acquisition. Patch contributed by
Yoshinori K. Okuji. See https://bugs.launchpad.net/zope2/+bug/486182.
2.12.4 (2009-10-29)
-------------------
- Fix iteration proxying to pass `self` acquisition-wrapped into both
`__iter__` as well as `__getitem__` (this fixes
https://bugs.launchpad.net/zope2/+bug/360761).
- Add tests for the __getslice__ proxying, including open-ended slicing.
2.12.3 (2009-08-08)
-------------------
- More 64-bit fixes in Py_BuildValue calls.
- More 64-bit issues fixed: Use correct integer size for slice operations.
2.12.2 (2009-08-02)
-------------------
- Fixed 64-bit compatibility issues for Python 2.5.x / 2.6.x. See
http://www.python.org/dev/peps/pep-0353/ for details.
2.12.1 (2009-04-15)
-------------------
- Update for iteration proxying: The proxy for `__iter__` must not rely on the
object to have an `__iter__` itself, but also support fall-back iteration via
`__getitem__` (this fixes https://bugs.launchpad.net/zope2/+bug/360761).
2.12 (2009-01-25)
-----------------
- Release as separate package.
Zope Foundation and Contributors
\ No newline at end of file
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.
Metadata-Version: 1.0
Name: Acquisition
Version: 2.13.9
Summary: Acquisition is a mechanism that allows objects to obtain attributes from the containment hierarchy they're in.
Home-page: http://pypi.python.org/pypi/Acquisition
Author: Zope Foundation and Contributors
Author-email: zope-dev@zope.org
License: ZPL 2.1
Description: .. contents::
Introductory Example
====================
Zope implements acquisition with "Extension Class" mix-in classes. To
use acquisition your classes must inherit from an acquisition base
class. For 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 its
environment, where its 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``.
Acquisition wrappers provide access to the wrapped objects through the
attributes ``aq_parent``, ``aq_self``, ``aq_base``. Continue the
example from above::
>>> c.a.aq_parent is c
True
>>> c.a.aq_self is a
True
Explicit and Implicit Acquisition
=================================
Two styles of acquisition are supported: implicit and explicit
acquisition.
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 inheritance.
An attribute can be implicitly acquired if its name does not begin
with an underscore.
To support implicit acquisition, your class 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_acquire must be
used. For example::
>>> print c.a.aq_acquire('color')
red
To support explicit acquisition, your class should inherit from the
mix-in class ``Acquisition.Explicit``.
Controlling Acquisition
-----------------------
A class (or instance) can provide attribute by attribute control over
acquisition. Your should subclass from ``Acquisition.Explicit``, and set
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::
>>> class C(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__``. Note that the ``__roles__``
attribute is acquired even though its name begins with an
underscore. In fact, the special ``Acquisition.Acquired`` value can be
used in ``Acquisition.Implicit`` objects to implicitly acquire
selected objects that smell like private objects.
Sometimes, you want to dynamically make an implicitly acquiring object
acquire explicitly. You can do this by getting the object's
aq_explicit attribute. This attribute provides the object with an
explicit wrapper that replaces the original implicit wrapper.
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.
Here's an example::
>>> 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.
Filtered acquisition is rarely used in Zope.
Acquiring from Context
======================
Normally acquisition allows objects to acquire data from their
containers. However an object can acquire from objects that aren't its
containers.
Most of the examples we've seen so far show establishing of an
acquisition context using getattr semantics. For example, ``a.b`` is a
reference to ``b`` in the context of ``a``.
You can also manually set acquisition context using the ``__of__``
method. For example::
>>> from Acquisition import Implicit
>>> class C(Implicit): pass
...
>>> a = C()
>>> b = C()
>>> a.color = "red"
>>> print b.__of__(a).color
red
In this case, ``a`` does not contain ``b``, but it is put in ``b``'s
context using the ``__of__`` method.
Here's another subtler example that shows how you can construct an
acquisition context that includes non-container objects::
>>> from Acquisition import Implicit
>>> class C(Implicit):
... def __init__(self, name):
... self.name = name
>>> a = C("a")
>>> a.b = C("b")
>>> a.b.color = "red"
>>> a.x = C("x")
>>> print a.b.x.color
red
Even though ``b`` does not contain ``x``, ``x`` can acquire the color
attribute from ``b``. This works because in this case, ``x`` is accessed
in the context of ``b`` even though it is not contained by ``b``.
Here acquisition context is defined by the objects used to access
another object.
Containment Before Context
==========================
If in the example above suppose both a and b have an color attribute::
>>> a = C("a")
>>> a.color = "green"
>>> a.b = C("b")
>>> a.b.color = "red"
>>> a.x = C("x")
>>> print a.b.x.color
green
Why does ``a.b.x.color`` acquire color from ``a`` and not from ``b``?
The answer is that an object acquires from its containers before
non-containers in its context.
To see why consider this example in terms of expressions using the
``__of__`` method::
a.x -> x.__of__(a)
a.b -> b.__of__(a)
a.b.x -> x.__of__(a).__of__(b.__of__(a))
Keep in mind that attribute lookup in a wrapper is done by trying to
look up the attribute in the wrapped object first and then in the
parent object. So in the expressions above proceeds from left to
right.
The upshot of these rules is that attributes are looked up by
containment before context.
This rule holds true also for more complex examples. For example,
``a.b.c.d.e.f.g.attribute`` would search for attribute in ``g`` and
all its containers first. (Containers are searched in order from the
innermost parent to the outermost container.) If the attribute is not
found in ``g`` or any of its containers, then the search moves to
``f`` and all its containers, and so on.
Additional Attributes and Methods
=================================
You can use the special method ``aq_inner`` to access an object
wrapped only by containment. So in the example above,
``a.b.x.aq_inner`` is equivalent to ``a.x``.
You can find out the acquisition context of an object using the
aq_chain method like so:
>>> [obj.name for obj in a.b.x.aq_chain]
['x', 'b', 'a']
You can find out if an object is in the containment context of another
object using the ``aq_inContextOf`` method. For example:
>>> a.b.aq_inContextOf(a)
1
.. Note: as of this writing the aq_inContextOf examples don't work the
way they should be working. According to Jim, this is because
aq_inContextOf works by comparing object pointer addresses, which
(because they are actually different wrapper objects) doesn't give
you the expected results. He acknowledges that this behavior is
controversial, and says that there is a collector entry to change
it so that you would get the answer you expect in the above. (We
just need to get to it).
Acquisition Module Functions
============================
In addition to using acquisition attributes and methods directly on
objects you can use similar functions defined in the ``Acquisition``
module. These functions have the advantage that you don't need to
check to make sure that the object has the method or attribute before
calling it.
``aq_acquire(object, name [, filter, extra, explicit, default, containment])``
Acquires an object with the given name.
This function can be used to explictly acquire when using explicit
acquisition and to acquire names that wouldn't normally be
acquired.
The function accepts a number of optional arguments:
``filter``
A callable filter object that is used to decide if an object
should be acquired.
The filter 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 argument passed to aq_acquire.
If the filter returns a true object that the object found is
returned, otherwise, the acquisition search continues.
``extra``
Extra data to be passed as the last argument to the filter.
``explicit``
A flag (boolean value) indicating whether explicit acquisition
should be used. The default value is true. If the flag is
true, then acquisition will proceed regardless of whether
wrappers encountered in the search of the acquisition
hierarchy are explicit or implicit wrappers. If the flag is
false, then parents of explicit wrappers are not searched.
This argument is useful if you want to apply a filter without
overriding explicit wrappers.
``default``
A default value to return if no value can be acquired.
``containment``
A flag indicating whether the search should be limited to the
containment hierarchy.
In addition, arguments can be provided as keywords.
``aq_base(object)``
Return the object with all wrapping removed.
``aq_chain(object [, containment])``
Return a list containing the object and it's acquisition
parents. The optional argument, containment, controls whether the
containment or access hierarchy is used.
``aq_get(object, name [, default, containment])``
Acquire an attribute, name. A default value can be provided, as
can a flag that limits search to the containment hierarchy.
``aq_inner(object)``
Return the object with all but the innermost layer of wrapping
removed.
``aq_parent(object)``
Return the acquisition parent of the object or None if the object
is unwrapped.
``aq_self(object)``
Return the object with one layer of wrapping removed, unless the
object is unwrapped, in which case the object is returned.
In most cases it is more convenient to use these module functions
instead of the acquisition attributes and methods directly.
Acquisition and Methods
=======================
Python methods of objects that support acquisition can use acquired
attributes. 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. In practice, you will seldom
find this a problem.
Conclusion
==========
Acquisition provides a powerful way to dynamically share information
between objects. Zope 2 uses acquisition for a number of its key
features including security, object publishing, and DTML variable
lookup. Acquisition also provides an elegant solution to the problem
of circular references for many classes of problems. While acquisition
is powerful, you should take care when using acquisition in your
applications. The details can get complex, especially with the
differences between acquiring from context and acquiring from
containment.
Changelog
=========
2.13.9 (2015-02-17)
-------------------
- Tolerate Unicode attribute names (ASCII only). LP #143358.
- Make module-level ``aq_acquire`` API respect the ``default`` parameter.
LP #1387363.
- Don't raise an attribute error for ``__iter__`` if the fallback to
``__getitem__`` succeeds. LP #1155760.
2.13.8 (2011-06-11)
-------------------
- Fixed a segfault on 64bit platforms when providing the `explicit` argument to
the aq_acquire method of an Acquisition wrapper. Thx to LP #675064 for the
hint to the solution. The code passed an int instead of a pointer into a
function.
2.13.7 (2011-03-02)
-------------------
- Fixed bug: When an object did not implement ``__unicode__``, calling
``unicode(wrapped)`` was calling ``__str__`` with an unwrapped ``self``.
2.13.6 (2011-02-19)
-------------------
- Add ``aq_explicit`` to ``IAcquisitionWrapper``.
- Fixed bug: ``unicode(wrapped)`` was not calling a ``__unicode__``
method on wrapped objects.
2.13.5 (2010-09-29)
-------------------
- Fixed unit tests that failed on 64bit Python on Windows machines.
2.13.4 (2010-08-31)
-------------------
- LP 623665: Fixed typo in Acquisition.h.
2.13.3 (2010-04-19)
-------------------
- Use the doctest module from the standard library and no longer depend on
zope.testing.
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)
-------------------
- Update to include ExtensionClass 2.13.0.
- Fix the ``tp_name`` of the ImplicitAcquisitionWrapper and
ExplicitAcquisitionWrapper to match their Python visible names and thus have
a correct ``__name__``.
- Expand the ``tp_name`` of our extension types to hold the fully qualified
name. This ensures classes have their ``__module__`` set correctly.
2.13.0 (2010-02-14)
-------------------
- Added support for method cache in Acquisition. Patch contributed by
Yoshinori K. Okuji. See https://bugs.launchpad.net/zope2/+bug/486182.
2.12.4 (2009-10-29)
-------------------
- Fix iteration proxying to pass `self` acquisition-wrapped into both
`__iter__` as well as `__getitem__` (this fixes
https://bugs.launchpad.net/zope2/+bug/360761).
- Add tests for the __getslice__ proxying, including open-ended slicing.
2.12.3 (2009-08-08)
-------------------
- More 64-bit fixes in Py_BuildValue calls.
- More 64-bit issues fixed: Use correct integer size for slice operations.
2.12.2 (2009-08-02)
-------------------
- Fixed 64-bit compatibility issues for Python 2.5.x / 2.6.x. See
http://www.python.org/dev/peps/pep-0353/ for details.
2.12.1 (2009-04-15)
-------------------
- Update for iteration proxying: The proxy for `__iter__` must not rely on the
object to have an `__iter__` itself, but also support fall-back iteration via
`__getitem__` (this fixes https://bugs.launchpad.net/zope2/+bug/360761).
2.12 (2009-01-25)
-----------------
- Release as separate package.
Platform: UNKNOWN
Please refer to src/Acquisition/README.txt.
##############################################################################
#
# Copyright (c) 2006 Zope Foundation 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.
#
##############################################################################
"""Bootstrap a buildout-based project
Simply run this script in a directory containing a buildout.cfg.
The script accepts buildout command-line options, so you can
use the -c option to specify an alternate configuration file.
"""
import os
import shutil
import sys
import tempfile
from optparse import OptionParser
tmpeggs = tempfile.mkdtemp()
usage = '''\
[DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options]
Bootstraps a buildout-based project.
Simply run this script in a directory containing a buildout.cfg, using the
Python that you want bin/buildout to use.
Note that by using --find-links to point to local resources, you can keep
this script from going over the network.
'''
parser = OptionParser(usage=usage)
parser.add_option("-v", "--version", help="use a specific zc.buildout version")
parser.add_option("-t", "--accept-buildout-test-releases",
dest='accept_buildout_test_releases',
action="store_true", default=False,
help=("Normally, if you do not specify a --version, the "
"bootstrap script and buildout gets the newest "
"*final* versions of zc.buildout and its recipes and "
"extensions for you. If you use this flag, "
"bootstrap and buildout will get the newest releases "
"even if they are alphas or betas."))
parser.add_option("-c", "--config-file",
help=("Specify the path to the buildout configuration "
"file to be used."))
parser.add_option("-f", "--find-links",
help=("Specify a URL to search for buildout releases"))
parser.add_option("--allow-site-packages",
action="store_true", default=False,
help=("Let bootstrap.py use existing site packages"))
parser.add_option("--setuptools-version",
help="use a specific setuptools version")
options, args = parser.parse_args()
######################################################################
# load/install setuptools
try:
if options.allow_site_packages:
import setuptools
import pkg_resources
from urllib.request import urlopen
except ImportError:
from urllib2 import urlopen
ez = {}
exec(urlopen('https://bootstrap.pypa.io/ez_setup.py').read(), ez)
if not options.allow_site_packages:
# ez_setup imports site, which adds site packages
# this will remove them from the path to ensure that incompatible versions
# of setuptools are not in the path
import site
# inside a virtualenv, there is no 'getsitepackages'.
# We can't remove these reliably
if hasattr(site, 'getsitepackages'):
for sitepackage_path in site.getsitepackages():
sys.path[:] = [x for x in sys.path if sitepackage_path not in x]
setup_args = dict(to_dir=tmpeggs, download_delay=0)
if options.setuptools_version is not None:
setup_args['version'] = options.setuptools_version
ez['use_setuptools'](**setup_args)
import setuptools
import pkg_resources
# This does not (always?) update the default working set. We will
# do it.
for path in sys.path:
if path not in pkg_resources.working_set.entries:
pkg_resources.working_set.add_entry(path)
######################################################################
# Install buildout
ws = pkg_resources.working_set
cmd = [sys.executable, '-c',
'from setuptools.command.easy_install import main; main()',
'-mZqNxd', tmpeggs]
find_links = os.environ.get(
'bootstrap-testing-find-links',
options.find_links or
('http://downloads.buildout.org/'
if options.accept_buildout_test_releases else None)
)
if find_links:
cmd.extend(['-f', find_links])
setuptools_path = ws.find(
pkg_resources.Requirement.parse('setuptools')).location
requirement = 'zc.buildout'
version = options.version
if version is None and not options.accept_buildout_test_releases:
# Figure out the most recent final version of zc.buildout.
import setuptools.package_index
_final_parts = '*final-', '*final'
def _final_version(parsed_version):
try:
return not parsed_version.is_prerelease
except AttributeError:
# Older setuptools
for part in parsed_version:
if (part[:1] == '*') and (part not in _final_parts):
return False
return True
index = setuptools.package_index.PackageIndex(
search_path=[setuptools_path])
if find_links:
index.add_find_links((find_links,))
req = pkg_resources.Requirement.parse(requirement)
if index.obtain(req) is not None:
best = []
bestv = None
for dist in index[req.project_name]:
distv = dist.parsed_version
if _final_version(distv):
if bestv is None or distv > bestv:
best = [dist]
bestv = distv
elif distv == bestv:
best.append(dist)
if best:
best.sort()
version = best[-1].version
if version:
requirement = '=='.join((requirement, version))
cmd.append(requirement)
import subprocess
if subprocess.call(cmd, env=dict(os.environ, PYTHONPATH=setuptools_path)) != 0:
raise Exception(
"Failed to execute command:\n%s" % repr(cmd)[1:-1])
######################################################################
# Import and run buildout
ws.add_entry(tmpeggs)
ws.require(requirement)
import zc.buildout.buildout
if not [a for a in args if '=' not in a]:
args.append('bootstrap')
# if -c was provided, we push it back into args for buildout' main function
if options.config_file is not None:
args[0:0] = ['-c', options.config_file]
zc.buildout.buildout.main(args)
shutil.rmtree(tmpeggs)
[buildout]
develop = .
parts = interpreter test
[interpreter]
recipe = zc.recipe.egg
interpreter = python
eggs = Acquisition
[test]
recipe = zc.recipe.testrunner
eggs = Acquisition
/*****************************************************************************
Copyright (c) 1996-2002 Zope Foundation 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
****************************************************************************/
/*
$Id$
Extension Class Definitions
Implementing base extension classes
A base extension class is implemented in much the same way that an
extension type is implemented, except:
- The include file, 'ExtensionClass.h', must be included.
- The type structure is declared to be of type
'PyExtensionClass', rather than of type 'PyTypeObject'.
- The type structure has an additional member that must be defined
after the documentation string. This extra member is a method chain
('PyMethodChain') containing a linked list of method definition
('PyMethodDef') lists. Method chains can be used to implement
method inheritance in C. Most extensions don't use method chains,
but simply define method lists, which are null-terminated arrays
of method definitions. A macro, 'METHOD_CHAIN' is defined in
'ExtensionClass.h' that converts a method list to a method chain.
(See the example below.)
- Module functions that create new instances must be replaced by an
'__init__' method that initializes, but does not create storage for
instances.
- The extension class must be initialized and exported to the module
with::
PyExtensionClass_Export(d,"name",type);
where 'name' is the module name and 'type' is the extension class
type object.
Attribute lookup
Attribute lookup is performed by calling the base extension class
'getattr' operation for the base extension class that includes C
data, or for the first base extension class, if none of the base
extension classes include C data. 'ExtensionClass.h' defines a
macro 'Py_FindAttrString' that can be used to find an object's
attributes that are stored in the object's instance dictionary or
in the object's class or base classes::
v = Py_FindAttrString(self,name);
In addition, a macro is provided that replaces 'Py_FindMethod'
calls with logic to perform the same sort of lookup that is
provided by 'Py_FindAttrString'.
Linking
The extension class mechanism was designed to be useful with
dynamically linked extension modules. Modules that implement
extension classes do not have to be linked against an extension
class library. The macro 'PyExtensionClass_Export' imports the
'ExtensionClass' module and uses objects imported from this module
to initialize an extension class with necessary behavior.
*/
#ifndef EXTENSIONCLASS_H
#define EXTENSIONCLASS_H
#include "Python.h"
#include "import.h"
/* Declarations for objects of type ExtensionClass */
#define EC PyTypeObject
#define PyExtensionClass PyTypeObject
#define EXTENSIONCLASS_BINDABLE_FLAG 1 << 2
#define EXTENSIONCLASS_NOINSTDICT_FLAG 1 << 5
typedef struct {
PyObject_HEAD
} _emptyobject;
static struct ExtensionClassCAPIstruct {
/*****************************************************************************
WARNING: THIS STRUCT IS PRIVATE TO THE EXTENSION CLASS INTERFACE
IMPLEMENTATION AND IS SUBJECT TO CHANGE !!!
*****************************************************************************/
PyObject *(*EC_findiattrs_)(PyObject *self, char *cname);
int (*PyExtensionClass_Export_)(PyObject *dict, char *name,
PyTypeObject *typ);
PyObject *(*PyECMethod_New_)(PyObject *callable, PyObject *inst);
PyExtensionClass *ECBaseType_;
PyExtensionClass *ECExtensionClassType_;
} *PyExtensionClassCAPI = NULL;
#define ECBaseType (PyExtensionClassCAPI->ECBaseType_)
#define ECExtensionClassType (PyExtensionClassCAPI->ECExtensionClassType_)
/* Following are macros that are needed or useful for defining extension
classes:
*/
/* This macro redefines Py_FindMethod to do attribute for an attribute
name given by a C string lookup using extension class meta-data.
This is used by older getattr implementations.
This macro is used in base class implementations of tp_getattr to
lookup methods or attributes that are not managed by the base type
directly. The macro is generally used to search for attributes
after other attribute searches have failed.
Note that in Python 1.4, a getattr operation may be provided that
uses an object argument. Classes that support this new operation
should use Py_FindAttr.
*/
#define EC_findiattrs (PyExtensionClassCAPI->EC_findiattrs_)
#define Py_FindMethod(M,SELF,NAME) (EC_findiattrs((SELF),(NAME)))
/* Do method or attribute lookup for an attribute name given by a C
string using extension class meta-data.
This macro is used in base class implementations of tp_getattro to
lookup methods or attributes that are not managed by the base type
directly. The macro is generally used to search for attributes
after other attribute searches have failed.
Note that in Python 1.4, a getattr operation may be provided that
uses an object argument. Classes that support this new operation
should use Py_FindAttr.
*/
#define Py_FindAttrString(SELF,NAME) (EC_findiattrs((SELF),(NAME)))
/* Do method or attribute lookup using extension class meta-data.
This macro is used in base class implementations of tp_getattr to
lookup methods or attributes that are not managed by the base type
directly. The macro is generally used to search for attributes
after other attribute searches have failed. */
#define Py_FindAttr (ECBaseType->tp_getattro)
/* Do attribute assignment for an attribute.
This macro is used in base class implementations of tp_setattro to
set attributes that are not managed by the base type directly. The
macro is generally used to assign attributes after other attribute
attempts to assign attributes have failed.
*/
#define PyEC_SetAttr(SELF,NAME,V) (ECBaseType->tp_setattro(SELF, NAME, V))
/* Convert a method list to a method chain. */
#define METHOD_CHAIN(DEF) (traverseproc)(DEF)
/* The following macro checks whether a type is an extension class: */
#define PyExtensionClass_Check(TYPE) \
PyObject_TypeCheck((PyObject*)(TYPE), ECExtensionClassType)
/* The following macro checks whether an instance is an extension instance: */
#define PyExtensionInstance_Check(INST) \
PyObject_TypeCheck(((PyObject*)(INST))->ob_type, ECExtensionClassType)
#define CHECK_FOR_ERRORS(MESS)
/* The following macro can be used to define an extension base class
that only provides method and that is used as a pure mix-in class. */
#define PURE_MIXIN_CLASS(NAME,DOC,METHODS) \
static PyExtensionClass NAME ## Type = { PyObject_HEAD_INIT(NULL) 0, # NAME, \
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
0 , DOC, (traverseproc)METHODS, }
/* The following macros provide limited access to extension-class
method facilities. */
/* Test for an ExtensionClass method: */
#define PyECMethod_Check(O) PyMethod_Check((O))
/* Create a method object that wraps a callable object and an
instance. Note that if the callable object is an extension class
method, then the new method will wrap the callable object that is
wrapped by the extension class method. Also note that if the
callable object is an extension class method with a reference
count of 1, then the callable object will be rebound to the
instance and returned with an incremented reference count.
*/
#define PyECMethod_New(CALLABLE, INST) \
PyExtensionClassCAPI->PyECMethod_New_((CALLABLE),(INST))
/* Return the instance that is bound by an extension class method. */
#define PyECMethod_Self(M) \
(PyMethod_Check((M)) ? ((PyMethodObject*)(M))->im_self : NULL)
/* Check whether an object has an __of__ method for returning itself
in the context of it's container. */
#define has__of__(O) (PyObject_TypeCheck((O)->ob_type, ECExtensionClassType) \
&& (O)->ob_type->tp_descr_get != NULL)
/* The following macros are used to check whether an instance
or a class' instanses have instance dictionaries: */
#define HasInstDict(O) (_PyObject_GetDictPtr(O) != NULL)
#define ClassHasInstDict(C) ((C)->tp_dictoffset > 0))
/* Get an object's instance dictionary. Use with caution */
#define INSTANCE_DICT(inst) (_PyObject_GetDictPtr(O))
/* Test whether an ExtensionClass, S, is a subclass of ExtensionClass C. */
#define ExtensionClassSubclass_Check(S,C) PyType_IsSubtype((S), (C))
/* Test whether an ExtensionClass instance , I, is a subclass of
ExtensionClass C. */
#define ExtensionClassSubclassInstance_Check(I,C) PyObject_TypeCheck((I), (C))
/* Export an Extension Base class in a given module dictionary with a
given name and ExtensionClass structure.
*/
#define PyExtensionClass_Export(D,N,T) \
if (! ExtensionClassImported || \
PyExtensionClassCAPI->PyExtensionClass_Export_((D),(N),&(T)) < 0) return;
#define ExtensionClassImported \
((PyExtensionClassCAPI != NULL) || \
(PyExtensionClassCAPI = PyCObject_Import("ExtensionClass","CAPI2")))
/* These are being overridded to use tp_free when used with
new-style classes. This is to allow old extention-class code
to work.
*/
#undef PyMem_DEL
#undef PyObject_DEL
#define PyMem_DEL(O) \
if (((O)->ob_type->tp_flags & Py_TPFLAGS_HAVE_CLASS) \
&& ((O)->ob_type->tp_free != NULL)) \
(O)->ob_type->tp_free((PyObject*)(O)); \
else \
PyObject_FREE((O));
#define PyObject_DEL(O) PyMem_DEL(O)
#endif /* EXTENSIONCLASS_H */
[egg_info]
tag_build =
tag_date = 0
tag_svn_revision = 0
##############################################################################
#
# Copyright (c) 2007 Zope Foundation 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.
#
##############################################################################
"""Setup for the Acquisition distribution
"""
import os
from setuptools import setup, find_packages, Extension
setup(name='Acquisition',
version = '2.13.9',
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 Foundation and Contributors',
author_email='zope-dev@zope.org',
long_description=open(
os.path.join('src', 'Acquisition', 'README.txt')).read() + '\n' +
open('CHANGES.txt').read(),
packages=find_packages('src'),
package_dir={'': 'src'},
ext_modules=[Extension("Acquisition._Acquisition",
[os.path.join('src', 'Acquisition',
'_Acquisition.c')],
include_dirs=['include', 'src']),
],
install_requires=[
'ExtensionClass<4.0.dev',
'zope.interface'],
include_package_data=True,
zip_safe=False,
)
/*****************************************************************************
Copyright (c) 1996-2002 Zope Foundation 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
****************************************************************************/
#ifndef __ACQUISITION_H_
#define __ACQUISITION_H_
typedef struct {
PyObject *(*AQ_Acquire) (PyObject *obj, PyObject *name, PyObject *filter,
PyObject *extra, int explicit, PyObject *deflt,
int containment);
PyObject *(*AQ_Get) (PyObject *obj, PyObject *name, PyObject *deflt,
int containment);
int (*AQ_IsWrapper) (PyObject *obj);
PyObject *(*AQ_Base) (PyObject *obj);
PyObject *(*AQ_Parent) (PyObject *obj);
PyObject *(*AQ_Self) (PyObject *obj);
PyObject *(*AQ_Inner) (PyObject *obj);
PyObject *(*AQ_Chain) (PyObject *obj, int containment);
} ACQUISITIONCAPI;
#ifndef _IN_ACQUISITION_C
#define aq_Acquire(obj, name, filter, extra, explicit, deflt, containment ) (AcquisitionCAPI == NULL ? NULL : (AcquisitionCAPI->AQ_Acquire(obj, name, filter, extra, explicit, deflt, containment)))
#define aq_acquire(obj, name) (AcquisitionCAPI == NULL ? NULL : (AcquisitionCAPI->AQ_Acquire(obj, name, NULL, NULL, 1, NULL, 0)))
#define aq_get(obj, name, deflt, containment) (AcquisitionCAPI == NULL ? NULL : (AcquisitionCAPI->AQ_Get(obj, name, deflt, containment)))
#define aq_isWrapper(obj) (AcquisitionCAPI == NULL ? -1 : (AcquisitionCAPI->AQ_IsWrapper(obj)))
#define aq_base(obj) (AcquisitionCAPI == NULL ? NULL : (AcquisitionCAPI->AQ_Base(obj)))
#define aq_parent(obj) (AcquisitionCAPI == NULL ? NULL : (AcquisitionCAPI->AQ_Parent(obj)))
#define aq_self(obj) (AcquisitionCAPI == NULL ? NULL : (AcquisitionCAPI->AQ_Self(obj)))
#define aq_inner(obj) (AcquisitionCAPI == NULL ? NULL : (AcquisitionCAPI->AQ_Inner(obj)))
#define aq_chain(obj, containment) (AcquisitionCAPI == NULL ? NULL : (AcquisitionCAPI->AQ_CHain(obj, containment)))
static ACQUISITIONCAPI *AcquisitionCAPI = NULL;
#define aq_init() { \
PyObject *module; \
PyObject *api; \
if (! (module = PyImport_ImportModule("Acquisition"))) return; \
if (! (api = PyObject_GetAttrString(module,"AcquisitionCAPI"))) return; \
Py_DECREF(module); \
AcquisitionCAPI = PyCObject_AsVoidPtr(api); \
Py_DECREF(api); \
}
#endif
#endif
.. contents::
Introductory Example
====================
Zope implements acquisition with "Extension Class" mix-in classes. To
use acquisition your classes must inherit from an acquisition base
class. For 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 its
environment, where its 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``.
Acquisition wrappers provide access to the wrapped objects through the
attributes ``aq_parent``, ``aq_self``, ``aq_base``. Continue the
example from above::
>>> c.a.aq_parent is c
True
>>> c.a.aq_self is a
True
Explicit and Implicit Acquisition
=================================
Two styles of acquisition are supported: implicit and explicit
acquisition.
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 inheritance.
An attribute can be implicitly acquired if its name does not begin
with an underscore.
To support implicit acquisition, your class 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_acquire must be
used. For example::
>>> print c.a.aq_acquire('color')
red
To support explicit acquisition, your class should inherit from the
mix-in class ``Acquisition.Explicit``.
Controlling Acquisition
-----------------------
A class (or instance) can provide attribute by attribute control over
acquisition. Your should subclass from ``Acquisition.Explicit``, and set
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::
>>> class C(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__``. Note that the ``__roles__``
attribute is acquired even though its name begins with an
underscore. In fact, the special ``Acquisition.Acquired`` value can be
used in ``Acquisition.Implicit`` objects to implicitly acquire
selected objects that smell like private objects.
Sometimes, you want to dynamically make an implicitly acquiring object
acquire explicitly. You can do this by getting the object's
aq_explicit attribute. This attribute provides the object with an
explicit wrapper that replaces the original implicit wrapper.
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.
Here's an example::
>>> 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.
Filtered acquisition is rarely used in Zope.
Acquiring from Context
======================
Normally acquisition allows objects to acquire data from their
containers. However an object can acquire from objects that aren't its
containers.
Most of the examples we've seen so far show establishing of an
acquisition context using getattr semantics. For example, ``a.b`` is a
reference to ``b`` in the context of ``a``.
You can also manually set acquisition context using the ``__of__``
method. For example::
>>> from Acquisition import Implicit
>>> class C(Implicit): pass
...
>>> a = C()
>>> b = C()
>>> a.color = "red"
>>> print b.__of__(a).color
red
In this case, ``a`` does not contain ``b``, but it is put in ``b``'s
context using the ``__of__`` method.
Here's another subtler example that shows how you can construct an
acquisition context that includes non-container objects::
>>> from Acquisition import Implicit
>>> class C(Implicit):
... def __init__(self, name):
... self.name = name
>>> a = C("a")
>>> a.b = C("b")
>>> a.b.color = "red"
>>> a.x = C("x")
>>> print a.b.x.color
red
Even though ``b`` does not contain ``x``, ``x`` can acquire the color
attribute from ``b``. This works because in this case, ``x`` is accessed
in the context of ``b`` even though it is not contained by ``b``.
Here acquisition context is defined by the objects used to access
another object.
Containment Before Context
==========================
If in the example above suppose both a and b have an color attribute::
>>> a = C("a")
>>> a.color = "green"
>>> a.b = C("b")
>>> a.b.color = "red"
>>> a.x = C("x")
>>> print a.b.x.color
green
Why does ``a.b.x.color`` acquire color from ``a`` and not from ``b``?
The answer is that an object acquires from its containers before
non-containers in its context.
To see why consider this example in terms of expressions using the
``__of__`` method::
a.x -> x.__of__(a)
a.b -> b.__of__(a)
a.b.x -> x.__of__(a).__of__(b.__of__(a))
Keep in mind that attribute lookup in a wrapper is done by trying to
look up the attribute in the wrapped object first and then in the
parent object. So in the expressions above proceeds from left to
right.
The upshot of these rules is that attributes are looked up by
containment before context.
This rule holds true also for more complex examples. For example,
``a.b.c.d.e.f.g.attribute`` would search for attribute in ``g`` and
all its containers first. (Containers are searched in order from the
innermost parent to the outermost container.) If the attribute is not
found in ``g`` or any of its containers, then the search moves to
``f`` and all its containers, and so on.
Additional Attributes and Methods
=================================
You can use the special method ``aq_inner`` to access an object
wrapped only by containment. So in the example above,
``a.b.x.aq_inner`` is equivalent to ``a.x``.
You can find out the acquisition context of an object using the
aq_chain method like so:
>>> [obj.name for obj in a.b.x.aq_chain]
['x', 'b', 'a']
You can find out if an object is in the containment context of another
object using the ``aq_inContextOf`` method. For example:
>>> a.b.aq_inContextOf(a)
1
.. Note: as of this writing the aq_inContextOf examples don't work the
way they should be working. According to Jim, this is because
aq_inContextOf works by comparing object pointer addresses, which
(because they are actually different wrapper objects) doesn't give
you the expected results. He acknowledges that this behavior is
controversial, and says that there is a collector entry to change
it so that you would get the answer you expect in the above. (We
just need to get to it).
Acquisition Module Functions
============================
In addition to using acquisition attributes and methods directly on
objects you can use similar functions defined in the ``Acquisition``
module. These functions have the advantage that you don't need to
check to make sure that the object has the method or attribute before
calling it.
``aq_acquire(object, name [, filter, extra, explicit, default, containment])``
Acquires an object with the given name.
This function can be used to explictly acquire when using explicit
acquisition and to acquire names that wouldn't normally be
acquired.
The function accepts a number of optional arguments:
``filter``
A callable filter object that is used to decide if an object
should be acquired.
The filter 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 argument passed to aq_acquire.
If the filter returns a true object that the object found is
returned, otherwise, the acquisition search continues.
``extra``
Extra data to be passed as the last argument to the filter.
``explicit``
A flag (boolean value) indicating whether explicit acquisition
should be used. The default value is true. If the flag is
true, then acquisition will proceed regardless of whether
wrappers encountered in the search of the acquisition
hierarchy are explicit or implicit wrappers. If the flag is
false, then parents of explicit wrappers are not searched.
This argument is useful if you want to apply a filter without
overriding explicit wrappers.
``default``
A default value to return if no value can be acquired.
``containment``
A flag indicating whether the search should be limited to the
containment hierarchy.
In addition, arguments can be provided as keywords.
``aq_base(object)``
Return the object with all wrapping removed.
``aq_chain(object [, containment])``
Return a list containing the object and it's acquisition
parents. The optional argument, containment, controls whether the
containment or access hierarchy is used.
``aq_get(object, name [, default, containment])``
Acquire an attribute, name. A default value can be provided, as
can a flag that limits search to the containment hierarchy.
``aq_inner(object)``
Return the object with all but the innermost layer of wrapping
removed.
``aq_parent(object)``
Return the acquisition parent of the object or None if the object
is unwrapped.
``aq_self(object)``
Return the object with one layer of wrapping removed, unless the
object is unwrapped, in which case the object is returned.
In most cases it is more convenient to use these module functions
instead of the acquisition attributes and methods directly.
Acquisition and Methods
=======================
Python methods of objects that support acquisition can use acquired
attributes. 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. In practice, you will seldom
find this a problem.
Conclusion
==========
Acquisition provides a powerful way to dynamically share information
between objects. Zope 2 uses acquisition for a number of its key
features including security, object publishing, and DTML variable
lookup. Acquisition also provides an elegant solution to the problem
of circular references for many classes of problems. While acquisition
is powerful, you should take care when using acquisition in your
applications. The details can get complex, especially with the
differences between acquiring from context and acquiring from
containment.
/*****************************************************************************
Copyright (c) 1996-2003 Zope Foundation 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
****************************************************************************/
#include "ExtensionClass/ExtensionClass.h"
#define _IN_ACQUISITION_C
#include "Acquisition/Acquisition.h"
static ACQUISITIONCAPI AcquisitionCAPI;
static void
PyVar_Assign(PyObject **v, PyObject *e)
{
Py_XDECREF(*v);
*v=e;
}
#define ASSIGN(V,E) PyVar_Assign(&(V),(E))
#define UNLESS(E) if (!(E))
#define UNLESS_ASSIGN(V,E) ASSIGN(V,E); UNLESS(V)
#define OBJECT(O) ((PyObject*)(O))
#define FORMAT_N "n"
#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
typedef int Py_ssize_t;
typedef Py_ssize_t (*lenfunc)(PyObject *);
typedef PyObject *(*ssizeargfunc)(PyObject *, Py_ssize_t);
typedef PyObject *(*ssizessizeargfunc)(PyObject *, Py_ssize_t, Py_ssize_t);
typedef int(*ssizeobjargproc)(PyObject *, Py_ssize_t, PyObject *);
typedef int(*ssizessizeobjargproc)(PyObject *, Py_ssize_t, Py_ssize_t, PyObject *);
#define PY_SSIZE_T_MAX INT_MAX
#define PY_SSIZE_T_MIN INT_MIN
#define FORMAT_N "i"
#endif
static PyObject *py__add__, *py__sub__, *py__mul__, *py__div__,
*py__mod__, *py__pow__, *py__divmod__, *py__lshift__, *py__rshift__,
*py__and__, *py__or__, *py__xor__, *py__coerce__, *py__neg__,
*py__pos__, *py__abs__, *py__nonzero__, *py__invert__, *py__int__,
*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__unicode__,
*py__cmp__, *py__parent__, *py__iter__;
static PyObject *Acquired=0;
static void
init_py_names(void)
{
#define INIT_PY_NAME(N) py ## N = PyString_FromString(#N)
INIT_PY_NAME(__add__);
INIT_PY_NAME(__sub__);
INIT_PY_NAME(__mul__);
INIT_PY_NAME(__div__);
INIT_PY_NAME(__mod__);
INIT_PY_NAME(__pow__);
INIT_PY_NAME(__divmod__);
INIT_PY_NAME(__lshift__);
INIT_PY_NAME(__rshift__);
INIT_PY_NAME(__and__);
INIT_PY_NAME(__or__);
INIT_PY_NAME(__xor__);
INIT_PY_NAME(__coerce__);
INIT_PY_NAME(__neg__);
INIT_PY_NAME(__pos__);
INIT_PY_NAME(__abs__);
INIT_PY_NAME(__nonzero__);
INIT_PY_NAME(__invert__);
INIT_PY_NAME(__int__);
INIT_PY_NAME(__long__);
INIT_PY_NAME(__float__);
INIT_PY_NAME(__oct__);
INIT_PY_NAME(__hex__);
INIT_PY_NAME(__getitem__);
INIT_PY_NAME(__setitem__);
INIT_PY_NAME(__delitem__);
INIT_PY_NAME(__getslice__);
INIT_PY_NAME(__setslice__);
INIT_PY_NAME(__delslice__);
INIT_PY_NAME(__contains__);
INIT_PY_NAME(__len__);
INIT_PY_NAME(__of__);
INIT_PY_NAME(__call__);
INIT_PY_NAME(__repr__);
INIT_PY_NAME(__str__);
INIT_PY_NAME(__unicode__);
INIT_PY_NAME(__cmp__);
INIT_PY_NAME(__parent__);
INIT_PY_NAME(__iter__);
#undef INIT_PY_NAME
}
static PyObject *
CallMethodO(PyObject *self, PyObject *name,
PyObject *args, PyObject *kw)
{
if (! args && PyErr_Occurred()) return NULL;
UNLESS(name=PyObject_GetAttr(self,name)) {
if (args) { Py_DECREF(args); }
return NULL;
}
ASSIGN(name,PyEval_CallObjectWithKeywords(name,args,kw));
if (args) { Py_DECREF(args); }
return name;
}
#define Build Py_BuildValue
/* For obscure reasons, we need to use tp_richcompare instead of tp_compare.
* The comparisons here all most naturally compute a cmp()-like result.
* This little helper turns that into a bool result for rich comparisons.
*/
static PyObject *
diff_to_bool(int diff, int op)
{
PyObject *result;
int istrue;
switch (op) {
case Py_EQ: istrue = diff == 0; break;
case Py_NE: istrue = diff != 0; break;
case Py_LE: istrue = diff <= 0; break;
case Py_GE: istrue = diff >= 0; break;
case Py_LT: istrue = diff < 0; break;
case Py_GT: istrue = diff > 0; break;
default:
assert(! "op unknown");
istrue = 0; /* To shut up compiler */
}
result = istrue ? Py_True : Py_False;
Py_INCREF(result);
return result;
}
/* Declarations for objects of type Wrapper */
typedef struct {
PyObject_HEAD
PyObject *obj;
PyObject *container;
} Wrapper;
staticforward PyExtensionClass Wrappertype, XaqWrappertype;
#define isWrapper(O) ((O)->ob_type==(PyTypeObject*)&Wrappertype || \
(O)->ob_type==(PyTypeObject*)&XaqWrappertype)
#define WRAPPER(O) ((Wrapper*)(O))
static int
Wrapper__init__(Wrapper *self, PyObject *args, PyObject *kwargs)
{
PyObject *obj, *container;
if (kwargs && PyDict_Size(kwargs) != 0)
{
PyErr_SetString(PyExc_TypeError,
"kwyword arguments not allowed");
return -1;
}
UNLESS(PyArg_ParseTuple(args, "OO:__init__", &obj, &container)) return -1;
if (self == WRAPPER(obj)) {
PyErr_SetString(PyExc_ValueError,
"Cannot wrap acquisition wrapper in itself (Wrapper__init__)");
return -1;
}
Py_INCREF(obj);
self->obj=obj;
if (container != Py_None)
{
Py_INCREF(container);
self->container=container;
}
return 0;
}
/* ---------------------------------------------------------------- */
static PyObject *
__of__(PyObject *inst, PyObject *parent)
{
PyObject *r, *t;
UNLESS(r=PyObject_GetAttr(inst, py__of__)) return NULL;
UNLESS(t=PyTuple_New(1)) goto err;
Py_INCREF(parent);
PyTuple_SET_ITEM(t,0,parent);
ASSIGN(r,PyObject_CallObject(r,t));
Py_DECREF(t);
if (r != NULL
&& isWrapper(r)
&& WRAPPER(r)->container && isWrapper(WRAPPER(r)->container)
)
while (WRAPPER(r)->obj && isWrapper(WRAPPER(r)->obj)
&& (WRAPPER(WRAPPER(r)->obj)->container ==
WRAPPER(WRAPPER(r)->container)->obj)
)
{
if (r->ob_refcnt !=1 )
{
t = PyObject_CallFunctionObjArgs((PyObject *)(r->ob_type),
WRAPPER(r)->obj,
WRAPPER(r)->container,
NULL);
Py_DECREF(r);
if (t==NULL)
return NULL;
r = t;
}
/* Simplify wrapper */
Py_XINCREF(WRAPPER(WRAPPER(r)->obj)->obj);
ASSIGN(WRAPPER(r)->obj, WRAPPER(WRAPPER(r)->obj)->obj);
}
return r;
err:
Py_DECREF(r);
return NULL;
}
static PyObject *
Wrapper_descrget(Wrapper *self, PyObject *inst, PyObject *cls)
{
if (inst == NULL)
{
Py_INCREF(self);
return (PyObject *)self;
}
return __of__((PyObject *)self, inst);
}
#define newWrapper(obj, container, Wrappertype) \
PyObject_CallFunctionObjArgs(OBJECT(Wrappertype), obj, container, NULL)
static int
Wrapper_traverse(Wrapper *self, visitproc visit, void *arg)
{
int vret;
if (self->obj) {
vret = visit(self->obj, arg);
if (vret != 0)
return vret;
}
if (self->container) {
vret = visit(self->container, arg);
if (vret != 0)
return vret;
}
return 0;
}
static int
Wrapper_clear(Wrapper *self)
{
PyObject *tmp;
tmp = self->obj;
self->obj = NULL;
Py_XDECREF(tmp);
tmp = self->container;
self->container = NULL;
Py_XDECREF(tmp);
return 0;
}
static void
Wrapper_dealloc(Wrapper *self)
{
Wrapper_clear(self);
self->ob_type->tp_free((PyObject*)self);
}
static PyObject *
Wrapper_special(Wrapper *self, char *name, PyObject *oname)
{
PyObject *r=0;
switch(*name)
{
case 'b':
if (strcmp(name,"base")==0)
{
if (self->obj)
{
r=self->obj;
while (isWrapper(r) && WRAPPER(r)->obj) r=WRAPPER(r)->obj;
}
else r=Py_None;
Py_INCREF(r);
return r;
}
break;
case 'p':
if (strcmp(name,"parent")==0)
{
if (self->container) r=self->container;
else r=Py_None;
Py_INCREF(r);
return r;
}
break;
case 's':
if (strcmp(name,"self")==0)
{
if (self->obj) r=self->obj;
else r=Py_None;
Py_INCREF(r);
return r;
}
break;
case 'e':
if (strcmp(name,"explicit")==0)
{
if (self->ob_type != (PyTypeObject *)&XaqWrappertype)
return newWrapper(self->obj, self->container,
(PyTypeObject *)&XaqWrappertype);
Py_INCREF(self);
return OBJECT(self);
}
break;
case 'a':
if (strcmp(name,"acquire")==0)
{
return Py_FindAttr(OBJECT(self),oname);
}
break;
case 'c':
if (strcmp(name,"chain")==0)
{
if ((r = PyList_New(0)))
while (1)
{
if (PyList_Append(r,OBJECT(self)) >= 0)
{
if (isWrapper(self) && self->container)
{
self=WRAPPER(self->container);
continue;
}
}
else
{
Py_DECREF(r);
}
break;
}
return r;
}
break;
case 'i':
if (strcmp(name,"inContextOf")==0)
{
return Py_FindAttr(OBJECT(self),oname);
}
if (strcmp(name,"inner")==0)
{
if (self->obj)
{
r=self->obj;
while (isWrapper(r) && WRAPPER(r)->obj)
{
self=WRAPPER(r);
r=WRAPPER(r)->obj;
}
r=OBJECT(self);
}
else r=Py_None;
Py_INCREF(r);
return r;
}
break;
case 'u':
if (strcmp(name,"uncle")==0)
{
return PyString_FromString("Bob");
}
break;
}
return NULL;
}
static int
apply_filter(PyObject *filter, PyObject *inst, PyObject *oname, PyObject *r,
PyObject *extra, PyObject *orig)
{
/* Calls the filter, passing arguments.
Returns 1 if the filter accepts the value, 0 if not, -1 if an
exception occurred.
Note the special reference counting rule: This function decrements
the refcount of 'r' when it returns 0 or -1. When it returns 1, it
leaves the refcount unchanged.
*/
PyObject *fr;
int ir;
UNLESS(fr=PyTuple_New(5)) goto err;
PyTuple_SET_ITEM(fr,0,orig);
Py_INCREF(orig);
PyTuple_SET_ITEM(fr,1,inst);
Py_INCREF(inst);
PyTuple_SET_ITEM(fr,2,oname);
Py_INCREF(oname);
PyTuple_SET_ITEM(fr,3,r);
Py_INCREF(r);
PyTuple_SET_ITEM(fr,4,extra);
Py_INCREF(extra);
UNLESS_ASSIGN(fr,PyObject_CallObject(filter, fr)) goto err;
ir=PyObject_IsTrue(fr);
Py_DECREF(fr);
if (ir) return 1;
Py_DECREF(r);
return 0;
err:
Py_DECREF(r);
return -1;
}
static PyObject *
Wrapper_acquire(Wrapper *self, PyObject *oname,
PyObject *filter, PyObject *extra, PyObject *orig,
int explicit, int containment);
static PyObject *
Wrapper_findattr(Wrapper *self, PyObject *oname,
PyObject *filter, PyObject *extra, PyObject *orig,
int sob, int sco, int explicit, int containment)
/*
Parameters:
sob
Search self->obj for the 'oname' attribute
sco
Search self->container for the 'oname' attribute
explicit
Explicitly acquire 'oname' attribute from container (assumed with
implicit acquisition wrapper)
containment
Use the innermost wrapper ("aq_inner") for looking up the 'oname'
attribute.
*/
{
PyObject *r, *v, *tb, *tmp;
char *name="";
if (PyString_Check(oname)) name=PyString_AS_STRING(oname);
if (PyUnicode_Check(oname)) {
tmp=PyUnicode_AsASCIIString(oname);
if (tmp==NULL) return NULL;
name=PyString_AS_STRING(tmp);
Py_DECREF(tmp);
}
if ((*name=='a' && name[1]=='q' && name[2]=='_') ||
(strcmp(name, "__parent__")==0))
{
/* __parent__ is an alias to aq_parent */
if (strcmp(name, "__parent__")==0)
name = "parent";
else
name = name + 3;
if ((r=Wrapper_special(self, name, oname)))
{
if (filter)
switch(apply_filter(filter,OBJECT(self),oname,r,extra,orig))
{
case -1: return NULL;
case 1: return r;
}
else return r;
}
else PyErr_Clear();
}
else if (*name=='_' && name[1]=='_' &&
(strcmp(name+2,"reduce__")==0 ||
strcmp(name+2,"reduce_ex__")==0 ||
strcmp(name+2,"getstate__")==0
))
return PyObject_GenericGetAttr(((PyObject*)self), oname);
/* If we are doing a containment search, then replace self with aq_inner */
if (containment)
while (self->obj && isWrapper(self->obj))
self=WRAPPER(self->obj);
if (sob && self->obj)
{
if (isWrapper(self->obj))
{
if (self == WRAPPER(self->obj)) {
PyErr_SetString(PyExc_RuntimeError,
"Recursion detected in acquisition wrapper");
return NULL;
}
if ((r=Wrapper_findattr(WRAPPER(self->obj),
oname, filter, extra, orig, 1,
/* Search object container if explicit,
or object is implicit acquirer */
explicit ||
self->obj->ob_type ==
(PyTypeObject*)&Wrappertype,
explicit, containment)))
{
if (PyECMethod_Check(r) && PyECMethod_Self(r)==self->obj)
ASSIGN(r,PyECMethod_New(r,OBJECT(self)));
else if (has__of__(r)) ASSIGN(r,__of__(r,OBJECT(self)));
return r;
}
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;
}
/* normal attribute lookup */
else if ((r=PyObject_GetAttr(self->obj,oname)))
{
if (r==Acquired)
{
Py_DECREF(r);
return Wrapper_acquire(self, oname, filter, extra, orig, 1,
containment);
}
if (PyECMethod_Check(r) && PyECMethod_Self(r)==self->obj)
ASSIGN(r,PyECMethod_New(r,OBJECT(self)));
else if (has__of__(r)) ASSIGN(r,__of__(r,OBJECT(self)));
if (r && filter)
switch(apply_filter(filter,OBJECT(self),oname,r,extra,orig))
{
case -1: return NULL;
case 1: return r;
}
else return r;
}
else {
PyErr_Fetch(&r,&v,&tb);
if (r != PyExc_AttributeError)
{
PyErr_Restore(r,v,tb);
return NULL;
}
Py_XDECREF(r); Py_XDECREF(v); Py_XDECREF(tb);
r=NULL;
}
PyErr_Clear();
}
/* Lookup has failed, acquire it from parent. */
if (sco && (*name != '_' || explicit))
return Wrapper_acquire(self, oname, filter, extra, orig, explicit,
containment);
PyErr_SetObject(PyExc_AttributeError,oname);
return NULL;
}
static PyObject *
Wrapper_acquire(Wrapper *self, PyObject *oname,
PyObject *filter, PyObject *extra, PyObject *orig,
int explicit, int containment)
{
PyObject *r, *v, *tb;
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))
{
/* Try to optimize search by recognizing repeated
objects in path. */
if (WRAPPER(self->obj)->container==
WRAPPER(self->container)->container)
sco=0;
else if (WRAPPER(self->obj)->container==
WRAPPER(self->container)->obj)
sob=0;
}
/* Don't search the container when the container of the
container is the same object as 'self'. */
if (WRAPPER(self->container)->container == WRAPPER(self)->obj)
{
sco=0;
containment=1;
}
r=Wrapper_findattr((Wrapper*)self->container,
oname, filter, extra, orig, sob, sco, explicit,
containment);
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 ((r = PyObject_GetAttr(self->container, py__parent__)))
{
ASSIGN(self->container, newWrapper(self->container, r,
(PyTypeObject*)&Wrappertype));
/* Don't search the container when the parent of the parent
is the same object as 'self' */
if (WRAPPER(r)->obj == WRAPPER(self)->obj)
sco=0;
Py_DECREF(r); /* don't need __parent__ anymore */
r=Wrapper_findattr((Wrapper*)self->container,
oname, filter, extra, orig, sob, sco, explicit,
containment);
/* There's no need to DECREF the wrapper here because it's
not stored in self->container, thus 'self' owns its
reference now */
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). */
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);
}
else {
if (filter) {
switch(apply_filter(filter,self->container,oname,r,
extra,orig))
{
case -1:
return NULL;
case 1:
if (has__of__(r)) ASSIGN(r,__of__(r,OBJECT(self)));
return r;
}
}
else {
if (has__of__(r)) ASSIGN(r,__of__(r,OBJECT(self)));
return r;
}
}
}
else {
/* May be AttributeError or some other kind of error */
return NULL;
}
}
}
PyErr_SetObject(PyExc_AttributeError, oname);
return NULL;
}
static PyObject *
Wrapper_getattro(Wrapper *self, PyObject *oname)
{
if (self->obj || self->container)
return Wrapper_findattr(self, oname, NULL, NULL, NULL, 1, 1, 0, 0);
/* Maybe we are getting initialized? */
return Py_FindAttr(OBJECT(self),oname);
}
static PyObject *
Xaq_getattro(Wrapper *self, PyObject *oname)
{
PyObject *tmp;
char *name="";
/* Special case backward-compatible acquire method. */
if (PyString_Check(oname)) name=PyString_AS_STRING(oname);
if (PyUnicode_Check(oname)) {
tmp=PyUnicode_AsASCIIString(oname);
if (tmp==NULL) return NULL;
name=PyString_AS_STRING(tmp);
Py_DECREF(tmp);
}
if (*name=='a' && name[1]=='c' && strcmp(name+2,"quire")==0)
return Py_FindAttr(OBJECT(self),oname);
if (self->obj || self->container)
return Wrapper_findattr(self, oname, NULL, NULL, NULL, 1, 0, 0, 0);
/* Maybe we are getting initialized? */
return Py_FindAttr(OBJECT(self),oname);
}
static int
Wrapper_setattro(Wrapper *self, PyObject *oname, PyObject *v)
{
PyObject *tmp;
char *name="";
/* Allow assignment to parent, to change context. */
if (PyString_Check(oname)) name=PyString_AS_STRING(oname);
if (PyUnicode_Check(oname)) {
tmp=PyUnicode_AsASCIIString(oname);
if (tmp==NULL) return -1;
name=PyString_AS_STRING(tmp);
Py_DECREF(tmp);
}
if ((*name=='a' && name[1]=='q' && name[2]=='_'
&& strcmp(name+3,"parent")==0) || (strcmp(name, "__parent__")==0))
{
Py_XINCREF(v);
ASSIGN(self->container, v);
return 0;
}
if (self->obj)
{
/* Unwrap passed in wrappers! */
while (v && isWrapper(v))
v=WRAPPER(v)->obj;
if (v) return PyObject_SetAttr(self->obj, oname, v);
else return PyObject_DelAttr(self->obj, oname);
}
PyErr_SetString(PyExc_AttributeError,
"Attempt to set attribute on empty acquisition wrapper");
return -1;
}
static int
Wrapper_compare(Wrapper *self, PyObject *w)
{
PyObject *obj, *wobj;
PyObject *m;
int r;
if (OBJECT(self) == w) return 0;
UNLESS (m=PyObject_GetAttr(OBJECT(self), py__cmp__))
{
/* Unwrap self completely -> obj. */
while (self->obj && isWrapper(self->obj))
self=WRAPPER(self->obj);
obj = self->obj;
/* Unwrap w completely -> wobj. */
if (isWrapper(w))
{
while (WRAPPER(w)->obj && isWrapper(WRAPPER(w)->obj))
w=WRAPPER(w)->obj;
wobj = WRAPPER(w)->obj;
}
else wobj = w;
PyErr_Clear();
if (obj == wobj) return 0;
return (obj < w) ? -1 : 1;
}
ASSIGN(m, PyObject_CallFunction(m, "O", w));
UNLESS (m) return -1;
r=PyInt_AsLong(m);
Py_DECREF(m);
return r;
}
static PyObject *
Wrapper_richcompare(Wrapper *self, PyObject *w, int op)
{
int diff = Wrapper_compare(self, w);
return diff_to_bool(diff, op);
}
static PyObject *
Wrapper_repr(Wrapper *self)
{
PyObject *r;
if ((r=PyObject_GetAttr(OBJECT(self),py__repr__)))
{
ASSIGN(r,PyObject_CallFunction(r,NULL,NULL));
return r;
}
else
{
PyErr_Clear();
return PyObject_Repr(self->obj);
}
}
static PyObject *
Wrapper_str(Wrapper *self)
{
PyObject *r;
if ((r=PyObject_GetAttr(OBJECT(self),py__str__)))
{
ASSIGN(r,PyObject_CallFunction(r,NULL,NULL));
return r;
}
else
{
PyErr_Clear();
return PyObject_Str(self->obj);
}
}
static PyObject *
Wrapper_unicode(Wrapper *self)
{
PyObject *r;
if ((r=PyObject_GetAttr(OBJECT(self),py__unicode__)))
{
ASSIGN(r,PyObject_CallFunction(r,NULL,NULL));
return r;
}
else
{
PyErr_Clear();
return Wrapper_str(self);
}
}
static long
Wrapper_hash(Wrapper *self)
{
return PyObject_Hash(self->obj);
}
static PyObject *
Wrapper_call(Wrapper *self, PyObject *args, PyObject *kw)
{
Py_INCREF(args);
return CallMethodO(OBJECT(self),py__call__,args,kw);
}
/* Code to handle accessing Wrapper objects as sequence objects */
static Py_ssize_t
Wrapper_length(Wrapper *self)
{
long l;
PyObject *r;
UNLESS(r=PyObject_GetAttr(OBJECT(self), py__len__)) return -1;
UNLESS_ASSIGN(r,PyObject_CallObject(r,NULL)) return -1;
l=PyInt_AsLong(r);
Py_DECREF(r);
return l;
}
static PyObject *
Wrapper_add(Wrapper *self, PyObject *bb)
{
return CallMethodO(OBJECT(self),py__add__,Build("(O)", bb) ,NULL);
}
static PyObject *
Wrapper_mul(Wrapper *self, Py_ssize_t n)
{
return CallMethodO(OBJECT(self),py__mul__,Build("(" FORMAT_N ")", n),NULL);
}
static PyObject *
Wrapper_item(Wrapper *self, Py_ssize_t i)
{
return CallMethodO(OBJECT(self),py__getitem__, Build("(" FORMAT_N ")", i),NULL);
}
static PyObject *
Wrapper_slice(Wrapper *self, Py_ssize_t ilow, Py_ssize_t ihigh)
{
return CallMethodO(OBJECT(self),py__getslice__,
Build("(" FORMAT_N FORMAT_N ")", ilow, ihigh),NULL);
}
static int
Wrapper_ass_item(Wrapper *self, Py_ssize_t i, PyObject *v)
{
if (v)
{
UNLESS(v=CallMethodO(OBJECT(self),py__setitem__,
Build("(" FORMAT_N "O)", i, v),NULL))
return -1;
}
else
{
UNLESS(v=CallMethodO(OBJECT(self),py__delitem__,
Build("(" FORMAT_N ")", i),NULL))
return -1;
}
Py_DECREF(v);
return 0;
}
static int
Wrapper_ass_slice(Wrapper *self, Py_ssize_t ilow, Py_ssize_t ihigh, PyObject *v)
{
if (v)
{
UNLESS(v=CallMethodO(OBJECT(self),py__setslice__,
Build("(" FORMAT_N FORMAT_N "O)", ilow, ihigh, v),NULL))
return -1;
}
else
{
UNLESS(v=CallMethodO(OBJECT(self),py__delslice__,
Build("(" FORMAT_N FORMAT_N ")", ilow, ihigh),NULL))
return -1;
}
Py_DECREF(v);
return 0;
}
static int
Wrapper_contains(Wrapper *self, PyObject *v)
{
long c;
UNLESS(v=CallMethodO(OBJECT(self),py__contains__,Build("(O)", v) ,NULL))
return -1;
c = PyInt_AsLong(v);
Py_DECREF(v);
return c;
}
/* Support for iteration cannot rely on the internal implementation of
`PyObject_GetIter`, since the `self` passed into `__iter__` and
`__getitem__` should be acquisition-wrapped (also see LP 360761): The
wrapper obviously supports the iterator protocol so simply calling
`PyObject_GetIter(OBJECT(self))` results in an infinite recursion.
Instead the base object needs to be checked and the wrapper must only
be used when actually calling `__getitem__` or setting up a sequence
iterator. */
static PyObject *
Wrapper_iter(Wrapper *self)
{
PyObject *obj = self->obj;
PyObject *res;
if ((res=PyObject_GetAttr(OBJECT(self),py__iter__))) {
ASSIGN(res,PyObject_CallFunction(res,NULL,NULL));
if (res != NULL && !PyIter_Check(res)) {
PyErr_Format(PyExc_TypeError,
"iter() returned non-iterator "
"of type '%.100s'",
res->ob_type->tp_name);
Py_DECREF(res);
res = NULL;
}
} else if (PySequence_Check(obj)) {
PyErr_Clear();
ASSIGN(res,PySeqIter_New(OBJECT(self)));
} else {
res = PyErr_Format(PyExc_TypeError, "iteration over non-sequence");
}
return res;
}
static PySequenceMethods Wrapper_as_sequence = {
(lenfunc)Wrapper_length, /*sq_length*/
(binaryfunc)Wrapper_add, /*sq_concat*/
(ssizeargfunc)Wrapper_mul, /*sq_repeat*/
(ssizeargfunc)Wrapper_item, /*sq_item*/
(ssizessizeargfunc)Wrapper_slice, /*sq_slice*/
(ssizeobjargproc)Wrapper_ass_item, /*sq_ass_item*/
(ssizessizeobjargproc)Wrapper_ass_slice, /*sq_ass_slice*/
(objobjproc)Wrapper_contains, /*sq_contains*/
};
/* -------------------------------------------------------------- */
/* Code to access Wrapper objects as mappings */
static PyObject *
Wrapper_subscript(Wrapper *self, PyObject *key)
{
return CallMethodO(OBJECT(self),py__getitem__,Build("(O)", key),NULL);
}
static int
Wrapper_ass_sub(Wrapper *self, PyObject *key, PyObject *v)
{
if (v)
{
UNLESS(v=CallMethodO(OBJECT(self),py__setitem__,
Build("(OO)", key, v),NULL))
return -1;
}
else
{
UNLESS(v=CallMethodO(OBJECT(self),py__delitem__,
Build("(O)", key),NULL))
return -1;
}
Py_XDECREF(v);
return 0;
}
static PyMappingMethods Wrapper_as_mapping = {
(lenfunc)Wrapper_length, /*mp_length*/
(binaryfunc)Wrapper_subscript, /*mp_subscript*/
(objobjargproc)Wrapper_ass_sub, /*mp_ass_subscript*/
};
/* -------------------------------------------------------------- */
/* Code to access Wrapper objects as numbers */
static PyObject *
Wrapper_sub(Wrapper *self, PyObject *o)
{
return CallMethodO(OBJECT(self),py__sub__,Build("(O)", o),NULL);
}
static PyObject *
Wrapper_div(Wrapper *self, PyObject *o)
{
return CallMethodO(OBJECT(self),py__div__,Build("(O)", o),NULL);
}
static PyObject *
Wrapper_mod(Wrapper *self, PyObject *o)
{
return CallMethodO(OBJECT(self),py__mod__,Build("(O)", o),NULL);
}
static PyObject *
Wrapper_divmod(Wrapper *self, PyObject *o)
{
return CallMethodO(OBJECT(self),py__divmod__,Build("(O)", o),NULL);
}
static PyObject *
Wrapper_pow(Wrapper *self, PyObject *o, PyObject *m)
{
return CallMethodO(OBJECT(self),py__pow__,Build("(OO)", o, m),NULL);
}
static PyObject *
Wrapper_neg(Wrapper *self)
{
return CallMethodO(OBJECT(self), py__neg__, NULL, NULL);
}
static PyObject *
Wrapper_pos(Wrapper *self)
{
return CallMethodO(OBJECT(self), py__pos__, NULL, NULL);
}
static PyObject *
Wrapper_abs(Wrapper *self)
{
return CallMethodO(OBJECT(self), py__abs__, NULL, NULL);
}
static PyObject *
Wrapper_invert(Wrapper *self)
{
return CallMethodO(OBJECT(self), py__invert__, NULL, NULL);
}
static PyObject *
Wrapper_lshift(Wrapper *self, PyObject *o)
{
return CallMethodO(OBJECT(self),py__lshift__,Build("(O)", o),NULL);
}
static PyObject *
Wrapper_rshift(Wrapper *self, PyObject *o)
{
return CallMethodO(OBJECT(self),py__rshift__,Build("(O)", o),NULL);
}
static PyObject *
Wrapper_and(Wrapper *self, PyObject *o)
{
return CallMethodO(OBJECT(self),py__and__,Build("(O)", o),NULL);
}
static PyObject *
Wrapper_xor(Wrapper *self, PyObject *o)
{
return CallMethodO(OBJECT(self),py__xor__,Build("(O)", o),NULL);
}
static PyObject *
Wrapper_or(Wrapper *self, PyObject *o)
{
return CallMethodO(OBJECT(self),py__or__,Build("(O)", o),NULL);
}
static int
Wrapper_coerce(Wrapper **self, PyObject **o)
{
PyObject *m;
UNLESS (m=PyObject_GetAttr(OBJECT(*self), py__coerce__))
{
PyErr_Clear();
Py_INCREF(*self);
Py_INCREF(*o);
return 0;
}
ASSIGN(m, PyObject_CallFunction(m, "O", *o));
UNLESS (m) return -1;
UNLESS (PyArg_ParseTuple(m,"OO", self, o)) goto err;
Py_INCREF(*self);
Py_INCREF(*o);
Py_DECREF(m);
return 0;
err:
Py_DECREF(m);
return -1;
}
static PyObject *
Wrapper_int(Wrapper *self)
{
return CallMethodO(OBJECT(self), py__int__, NULL, NULL);
}
static PyObject *
Wrapper_long(Wrapper *self)
{
return CallMethodO(OBJECT(self), py__long__, NULL, NULL);
}
static PyObject *
Wrapper_float(Wrapper *self)
{
return CallMethodO(OBJECT(self), py__float__, NULL, NULL);
}
static PyObject *
Wrapper_oct(Wrapper *self)
{
return CallMethodO(OBJECT(self), py__oct__, NULL, NULL);
}
static PyObject *
Wrapper_hex(Wrapper *self)
{
return CallMethodO(OBJECT(self), py__hex__, NULL, NULL);
}
static int
Wrapper_nonzero(Wrapper *self)
{
long l;
PyObject *r;
UNLESS(r=PyObject_GetAttr(OBJECT(self), py__nonzero__))
{
PyErr_Clear();
/* Try len */
UNLESS(r=PyObject_GetAttr(OBJECT(self), py__len__))
{
/* No len, it's true :-) */
PyErr_Clear();
return 1;
}
}
UNLESS_ASSIGN(r,PyObject_CallObject(r,NULL)) return -1;
l=PyInt_AsLong(r);
Py_DECREF(r);
return l;
}
static PyNumberMethods Wrapper_as_number = {
(binaryfunc)Wrapper_add, /*nb_add*/
(binaryfunc)Wrapper_sub, /*nb_subtract*/
(binaryfunc)Wrapper_mul, /*nb_multiply*/
(binaryfunc)Wrapper_div, /*nb_divide*/
(binaryfunc)Wrapper_mod, /*nb_remainder*/
(binaryfunc)Wrapper_divmod, /*nb_divmod*/
(ternaryfunc)Wrapper_pow, /*nb_power*/
(unaryfunc)Wrapper_neg, /*nb_negative*/
(unaryfunc)Wrapper_pos, /*nb_positive*/
(unaryfunc)Wrapper_abs, /*nb_absolute*/
(inquiry)Wrapper_nonzero, /*nb_nonzero*/
(unaryfunc)Wrapper_invert, /*nb_invert*/
(binaryfunc)Wrapper_lshift, /*nb_lshift*/
(binaryfunc)Wrapper_rshift, /*nb_rshift*/
(binaryfunc)Wrapper_and, /*nb_and*/
(binaryfunc)Wrapper_xor, /*nb_xor*/
(binaryfunc)Wrapper_or, /*nb_or*/
(coercion)Wrapper_coerce, /*nb_coerce*/
(unaryfunc)Wrapper_int, /*nb_int*/
(unaryfunc)Wrapper_long, /*nb_long*/
(unaryfunc)Wrapper_float, /*nb_float*/
(unaryfunc)Wrapper_oct, /*nb_oct*/
(unaryfunc)Wrapper_hex, /*nb_hex*/
};
/* -------------------------------------------------------- */
static char *acquire_args[] = {"object", "name", "filter", "extra", "explicit",
"default", "containment", NULL};
static PyObject *
Wrapper_acquire_method(Wrapper *self, PyObject *args, PyObject *kw)
{
PyObject *name, *filter=0, *extra=Py_None;
PyObject *expl=0, *defalt=0;
int explicit=1;
int containment=0;
PyObject *result; /* DM 2005-08-25: argument "default" ignored */
UNLESS (PyArg_ParseTupleAndKeywords(
args, kw, "O|OOOOi", acquire_args+1,
&name, &filter, &extra, &expl, &defalt, &containment
))
return NULL;
if (expl) explicit=PyObject_IsTrue(expl);
if (filter==Py_None) filter=0;
/* DM 2005-08-25: argument "default" ignored -- fix it! */
# if 0
return Wrapper_findattr(self,name,filter,extra,OBJECT(self),1,
explicit ||
self->ob_type==(PyTypeObject*)&Wrappertype,
explicit, containment);
# else
result = Wrapper_findattr(self,name,filter,extra,OBJECT(self),1,
explicit ||
self->ob_type==(PyTypeObject*)&Wrappertype,
explicit, containment);
if (result == NULL && defalt != NULL) {
/* as "Python/bltinmodule.c:builtin_getattr" turn
only 'AttributeError' into a default value, such
that e.g. "ConflictError" and errors raised by the filter
are not mapped to the default value.
*/
if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
PyErr_Clear();
Py_INCREF(defalt);
result = defalt;
}
}
return result;
# endif
}
/* forward declaration so that we can use it in Wrapper_inContextOf */
static PyObject * capi_aq_inContextOf(PyObject *self, PyObject *o, int inner);
static PyObject *
Wrapper_inContextOf(Wrapper *self, PyObject *args)
{
PyObject *o;
int inner=1;
UNLESS(PyArg_ParseTuple(args,"O|i",&o,&inner)) return NULL;
return capi_aq_inContextOf((PyObject*)self, o, inner);
}
PyObject *
Wrappers_are_not_picklable(PyObject *wrapper, PyObject *args)
{
PyErr_SetString(PyExc_TypeError,
"Can't pickle objects in acquisition wrappers.");
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,
"Get an attribute, acquiring it if necessary"},
{"aq_acquire", (PyCFunction)Wrapper_acquire_method,
METH_VARARGS|METH_KEYWORDS,
"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,
"Wrappers are not picklable"},
{"__reduce_ex__", (PyCFunction)Wrappers_are_not_picklable, METH_VARARGS,
"Wrappers are not picklable"},
{"__unicode__", (PyCFunction)Wrapper_unicode, METH_NOARGS,
"Unicode"},
{NULL, NULL} /* sentinel */
};
static PyExtensionClass Wrappertype = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"Acquisition.ImplicitAcquisitionWrapper", /*tp_name*/
sizeof(Wrapper), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)Wrapper_dealloc, /*tp_dealloc*/
(printfunc)0, /*tp_print*/
(getattrfunc)0, /*tp_getattr*/
(setattrfunc)0, /*tp_setattr*/
(cmpfunc)0, /*tp_compare*/
(reprfunc)Wrapper_repr, /*tp_repr*/
&Wrapper_as_number, /*tp_as_number*/
&Wrapper_as_sequence, /*tp_as_sequence*/
&Wrapper_as_mapping, /*tp_as_mapping*/
(hashfunc)Wrapper_hash, /*tp_hash*/
(ternaryfunc)Wrapper_call, /*tp_call*/
(reprfunc)Wrapper_str, /*tp_str*/
(getattrofunc)Wrapper_getattro, /*tp_getattr with object key*/
(setattrofunc)Wrapper_setattro, /*tp_setattr with object key*/
/* tp_as_buffer */ 0,
/* tp_flags */ Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_BASETYPE
| Py_TPFLAGS_HAVE_GC
#ifdef Py_TPFLAGS_HAVE_VERSION_TAG
| Py_TPFLAGS_HAVE_VERSION_TAG
#endif
,
"Wrapper object for implicit acquisition", /* Documentation string */
/* tp_traverse */ (traverseproc)Wrapper_traverse,
/* tp_clear */ (inquiry)Wrapper_clear,
/* tp_richcompare */ (richcmpfunc)Wrapper_richcompare,
/* tp_weaklistoffset */ (long)0,
(getiterfunc)Wrapper_iter, /*tp_iter*/
/* tp_iternext */ (iternextfunc)0,
/* tp_methods */ Wrapper_methods,
/* tp_members */ 0,
/* tp_getset */ 0,
/* tp_base */ 0,
/* tp_dict */ 0,
/* tp_descr_get */ (descrgetfunc)Wrapper_descrget,
/* tp_descr_set */ 0,
/* tp_dictoffset */ 0,
/* tp_init */ (initproc)Wrapper__init__,
};
static PyExtensionClass XaqWrappertype = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"Acquisition.ExplicitAcquisitionWrapper", /*tp_name*/
sizeof(Wrapper), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)Wrapper_dealloc, /*tp_dealloc*/
(printfunc)0, /*tp_print*/
(getattrfunc)0, /*tp_getattr*/
(setattrfunc)0, /*tp_setattr*/
(cmpfunc)0, /*tp_compare*/
(reprfunc)Wrapper_repr, /*tp_repr*/
&Wrapper_as_number, /*tp_as_number*/
&Wrapper_as_sequence, /*tp_as_sequence*/
&Wrapper_as_mapping, /*tp_as_mapping*/
(hashfunc)Wrapper_hash, /*tp_hash*/
(ternaryfunc)Wrapper_call, /*tp_call*/
(reprfunc)Wrapper_str, /*tp_str*/
(getattrofunc)Xaq_getattro, /*tp_getattr with object key*/
(setattrofunc)Wrapper_setattro, /*tp_setattr with object key*/
/* tp_as_buffer */ 0,
/* tp_flags */ Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_BASETYPE
| Py_TPFLAGS_HAVE_GC
#ifdef Py_TPFLAGS_HAVE_VERSION_TAG
| Py_TPFLAGS_HAVE_VERSION_TAG
#endif
,
"Wrapper object for implicit acquisition", /* Documentation string */
/* tp_traverse */ (traverseproc)Wrapper_traverse,
/* tp_clear */ (inquiry)Wrapper_clear,
/* tp_richcompare */ (richcmpfunc)Wrapper_richcompare,
/* tp_weaklistoffset */ (long)0,
(getiterfunc)Wrapper_iter, /*tp_iter*/
/* tp_iternext */ (iternextfunc)0,
/* tp_methods */ Wrapper_methods,
/* tp_members */ 0,
/* tp_getset */ 0,
/* tp_base */ 0,
/* tp_dict */ 0,
/* tp_descr_get */ (descrgetfunc)Wrapper_descrget,
/* tp_descr_set */ 0,
/* tp_dictoffset */ 0,
/* tp_init */ (initproc)Wrapper__init__,
};
static PyObject *
acquire_of(PyObject *self, PyObject *args)
{
PyObject *inst;
UNLESS(PyArg_ParseTuple(args, "O", &inst)) return NULL;
UNLESS(PyExtensionInstance_Check(inst))
{
PyErr_SetString(PyExc_TypeError,
"attempt to wrap extension method using an object that\n"
"is not an extension class instance.");
return NULL;
}
return newWrapper(self, inst, (PyTypeObject *)&Wrappertype);
}
static PyObject *
xaq_of(PyObject *self, PyObject *args)
{
PyObject *inst;
UNLESS(PyArg_ParseTuple(args, "O", &inst)) return NULL;
UNLESS(PyExtensionInstance_Check(inst))
{
PyErr_SetString(PyExc_TypeError,
"attempt to wrap extension method using an object that\n"
"is not an extension class instance.");
return NULL;
}
return newWrapper(self, inst, (PyTypeObject *)&XaqWrappertype);
}
static struct PyMethodDef Acquirer_methods[] = {
{"__of__",(PyCFunction)acquire_of, METH_VARARGS,
"__of__(context) -- return the object in a context"},
{NULL, NULL} /* sentinel */
};
static struct PyMethodDef Xaq_methods[] = {
{"__of__",(PyCFunction)xaq_of, METH_VARARGS,""},
{NULL, NULL} /* sentinel */
};
static PyObject *
capi_aq_acquire(PyObject *self, PyObject *name, PyObject *filter,
PyObject *extra, int explicit, PyObject *defalt, int containment)
{
PyObject *result, *v, *tb;
if (filter==Py_None) filter=0;
/* We got a wrapped object, so business as usual */
if (isWrapper(self)) {
result = Wrapper_findattr(
WRAPPER(self), name, filter, extra, OBJECT(self),1,
explicit ||
WRAPPER(self)->ob_type==(PyTypeObject*)&Wrappertype,
explicit, containment);
goto check_default;
}
/* Not wrapped; check if we have a __parent__ pointer. If that's
the case, create a wrapper and pretend it's business as usual. */
else if ((result = PyObject_GetAttr(self, py__parent__)))
{
self = newWrapper(self, result, (PyTypeObject*)&Wrappertype);
Py_DECREF(result); /* don't need __parent__ anymore */
result = Wrapper_findattr(WRAPPER(self), name, filter, extra,
OBJECT(self), 1, 1, explicit, containment);
/* Get rid of temporary wrapper */
Py_DECREF(self);
goto check_default;
}
/* No wrapper and no __parent__, so just getattr. */
else
{
/* Clean up the AttributeError from the previous getattr
(because it has clearly failed). */
PyErr_Fetch(&result,&v,&tb);
if (result && (result != PyExc_AttributeError))
{
PyErr_Restore(result,v,tb);
return NULL;
}
Py_XDECREF(result); Py_XDECREF(v); Py_XDECREF(tb);
if (! filter) {
result = PyObject_GetAttr(self, name);
goto check_default;
}
/* Crap, we've got to construct a wrapper so we can use
Wrapper_findattr */
UNLESS (self=newWrapper(self, Py_None, (PyTypeObject*)&Wrappertype))
return NULL;
result=Wrapper_findattr(WRAPPER(self), name, filter, extra, OBJECT(self),
1, 1, explicit, containment);
/* Get rid of temporary wrapper */
Py_DECREF(self);
goto check_default;
}
check_default:
if (result == NULL && defalt != NULL) {
/* as "Python/bltinmodule.c:builtin_getattr" turn
only 'AttributeError' into a default value, such
that e.g. "ConflictError" and errors raised by the filter
are not mapped to the default value.
*/
if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
PyErr_Clear();
Py_INCREF(defalt);
result = defalt;
}
}
return result;
}
static PyObject *
module_aq_acquire(PyObject *ignored, PyObject *args, PyObject *kw)
{
PyObject *self;
PyObject *name, *filter=0, *extra=Py_None;
PyObject *expl=0, *defalt=0;
int explicit=1, containment=0;
UNLESS (PyArg_ParseTupleAndKeywords(
args, kw, "OO|OOOOi", acquire_args,
&self, &name, &filter, &extra, &expl, &defalt, &containment
))
return NULL;
if (expl) explicit=PyObject_IsTrue(expl);
return capi_aq_acquire(self, name, filter, extra, explicit, defalt,
containment);
}
static PyObject *
capi_aq_get(PyObject *self, PyObject *name, PyObject *defalt, int containment)
{
PyObject *result = NULL, *v, *tb;
/* We got a wrapped object, so business as usual */
if (isWrapper(self))
result=Wrapper_findattr(WRAPPER(self), name, 0, 0, OBJECT(self), 1, 1, 1,
containment);
/* Not wrapped; check if we have a __parent__ pointer. If that's
the case, create a wrapper and pretend it's business as usual. */
else if ((result = PyObject_GetAttr(self, py__parent__)))
{
self=newWrapper(self, result, (PyTypeObject*)&Wrappertype);
Py_DECREF(result); /* don't need __parent__ anymore */
result=Wrapper_findattr(WRAPPER(self), name, 0, 0, OBJECT(self),
1, 1, 1, containment);
Py_DECREF(self); /* Get rid of temporary wrapper. */
}
else
{
/* Clean up the AttributeError from the previous getattr
(because it has clearly failed). */
PyErr_Fetch(&result,&v,&tb);
if (result && (result != PyExc_AttributeError))
{
PyErr_Restore(result,v,tb);
return NULL;
}
Py_XDECREF(result); Py_XDECREF(v); Py_XDECREF(tb);
result=PyObject_GetAttr(self, name);
}
if (! result && defalt)
{
PyErr_Clear();
result=defalt;
Py_INCREF(result);
}
return result;
}
static PyObject *
module_aq_get(PyObject *r, PyObject *args)
{
PyObject *self, *name, *defalt=0;
int containment=0;
UNLESS (PyArg_ParseTuple(args, "OO|Oi",
&self, &name, &defalt, &containment
)) return NULL;
return capi_aq_get(self, name, defalt, containment);
}
static int
capi_aq_iswrapper(PyObject *self) {
return isWrapper(self);
}
static PyObject *
capi_aq_base(PyObject *self)
{
PyObject *result;
if (! isWrapper(self))
{
Py_INCREF(self);
return self;
}
if (WRAPPER(self)->obj)
{
result=WRAPPER(self)->obj;
while (isWrapper(result) && WRAPPER(result)->obj)
result=WRAPPER(result)->obj;
}
else result=Py_None;
Py_INCREF(result);
return result;
}
static PyObject *
module_aq_base(PyObject *ignored, PyObject *args)
{
PyObject *self;
UNLESS (PyArg_ParseTuple(args, "O", &self)) return NULL;
return capi_aq_base(self);
}
static PyObject *
capi_aq_parent(PyObject *self)
{
PyObject *result, *v, *tb;
if (isWrapper(self) && WRAPPER(self)->container)
{
result=WRAPPER(self)->container;
Py_INCREF(result);
return result;
}
else if ((result=PyObject_GetAttr(self, py__parent__)))
/* We already own the reference to result (PyObject_GetAttr gives
it to us), no need to INCREF here */
return result;
else
{
/* We need to clean up the AttributeError from the previous
getattr (because it has clearly failed) */
PyErr_Fetch(&result,&v,&tb);
if (result && (result != PyExc_AttributeError))
{
PyErr_Restore(result,v,tb);
return NULL;
}
Py_XDECREF(result); Py_XDECREF(v); Py_XDECREF(tb);
result=Py_None;
Py_INCREF(result);
return result;
}
}
static PyObject *
module_aq_parent(PyObject *ignored, PyObject *args)
{
PyObject *self;
UNLESS (PyArg_ParseTuple(args, "O", &self)) return NULL;
return capi_aq_parent(self);
}
static PyObject *
capi_aq_self(PyObject *self)
{
PyObject *result;
if (! isWrapper(self))
{
Py_INCREF(self);
return self;
}
if (WRAPPER(self)->obj) result=WRAPPER(self)->obj;
else result=Py_None;
Py_INCREF(result);
return result;
}
static PyObject *
module_aq_self(PyObject *ignored, PyObject *args)
{
PyObject *self;
UNLESS (PyArg_ParseTuple(args, "O", &self)) return NULL;
return capi_aq_self(self);
}
static PyObject *
capi_aq_inner(PyObject *self)
{
PyObject *result;
if (! isWrapper(self))
{
Py_INCREF(self);
return self;
}
if (WRAPPER(self)->obj)
{
result=WRAPPER(self)->obj;
while (isWrapper(result) && WRAPPER(result)->obj)
{
self=result;
result=WRAPPER(result)->obj;
}
result=self;
}
else result=Py_None;
Py_INCREF(result);
return result;
}
static PyObject *
module_aq_inner(PyObject *ignored, PyObject *args)
{
PyObject *self;
UNLESS (PyArg_ParseTuple(args, "O", &self)) return NULL;
return capi_aq_inner(self);
}
static PyObject *
capi_aq_chain(PyObject *self, int containment)
{
PyObject *result, *v, *tb;
UNLESS (result=PyList_New(0)) return NULL;
while (1)
{
if (isWrapper(self))
{
if (WRAPPER(self)->obj)
{
if (containment)
while (WRAPPER(self)->obj && isWrapper(WRAPPER(self)->obj))
self=WRAPPER(self)->obj;
if (PyList_Append(result,OBJECT(self)) < 0)
goto err;
}
if (WRAPPER(self)->container)
{
self=WRAPPER(self)->container;
continue;
}
}
else
{
if (PyList_Append(result, self) < 0)
goto err;
if ((self=PyObject_GetAttr(self, py__parent__)))
{
Py_DECREF(self); /* We don't need our own reference. */
if (self!=Py_None)
continue;
}
else
{
PyErr_Fetch(&self,&v,&tb);
if (self && (self != PyExc_AttributeError))
{
PyErr_Restore(self,v,tb);
return NULL;
}
Py_XDECREF(self); Py_XDECREF(v); Py_XDECREF(tb);
}
}
break;
}
return result;
err:
Py_DECREF(result);
return result;
}
static PyObject *
module_aq_chain(PyObject *ignored, PyObject *args)
{
PyObject *self;
int containment=0;
UNLESS (PyArg_ParseTuple(args, "O|i", &self, &containment))
return NULL;
return capi_aq_chain(self, containment);
}
static PyObject *
capi_aq_inContextOf(PyObject *self, PyObject *o, int inner)
{
PyObject *next, *c;
/* next = self
o = aq_base(o) */
next = self;
while (isWrapper(o) && WRAPPER(o)->obj)
o=WRAPPER(o)->obj;
while (1) {
/* if aq_base(next) is o: return 1 */
c = next;
while (isWrapper(c) && WRAPPER(c)->obj) c = WRAPPER(c)->obj;
if (c == o) return PyInt_FromLong(1);
if (inner)
{
self = capi_aq_inner(next);
Py_DECREF(self); /* We're not holding on to the inner wrapper */
if (self == Py_None) break;
}
else
self = next;
next = capi_aq_parent(self);
Py_DECREF(next); /* We're not holding on to the parent */
if (next == Py_None) break;
}
return PyInt_FromLong(0);
}
static PyObject *
module_aq_inContextOf(PyObject *ignored, PyObject *args)
{
PyObject *self, *o;
int inner=1;
UNLESS (PyArg_ParseTuple(args, "OO|i", &self, &o, &inner))
return NULL;
return capi_aq_inContextOf(self, o, inner);
}
static struct PyMethodDef methods[] = {
{"aq_acquire", (PyCFunction)module_aq_acquire, METH_VARARGS|METH_KEYWORDS,
"aq_acquire(ob, name [, filter, extra, explicit]) -- "
"Get an attribute, acquiring it if necessary"
},
{"aq_get", (PyCFunction)module_aq_get, METH_VARARGS,
"aq_get(ob, name [, default]) -- "
"Get an attribute, acquiring it if necessary."
},
{"aq_base", (PyCFunction)module_aq_base, METH_VARARGS,
"aq_base(ob) -- Get the object unwrapped"},
{"aq_parent", (PyCFunction)module_aq_parent, METH_VARARGS,
"aq_parent(ob) -- Get the parent of an object"},
{"aq_self", (PyCFunction)module_aq_self, METH_VARARGS,
"aq_self(ob) -- Get the object with the outermost wrapper removed"},
{"aq_inner", (PyCFunction)module_aq_inner, METH_VARARGS,
"aq_inner(ob) -- "
"Get the object with all but the innermost wrapper removed"},
{"aq_chain", (PyCFunction)module_aq_chain, METH_VARARGS,
"aq_chain(ob [, containment]) -- "
"Get a list of objects in the acquisition environment"},
{"aq_inContextOf", (PyCFunction)module_aq_inContextOf, METH_VARARGS,
"aq_inContextOf(base, ob [, inner]) -- "
"Determine whether the object is in the acquisition context of base."},
{NULL, NULL}
};
void
init_Acquisition(void)
{
PyObject *m, *d;
PyObject *api;
PURE_MIXIN_CLASS(Acquirer,
"Base class for objects that implicitly"
" acquire attributes from containers\n"
, Acquirer_methods);
PURE_MIXIN_CLASS(ExplicitAcquirer,
"Base class for objects that explicitly"
" acquire attributes from containers\n"
, Xaq_methods);
UNLESS(ExtensionClassImported) return;
UNLESS(Acquired=PyString_FromStringAndSize(NULL,42)) return;
strcpy(PyString_AsString(Acquired),
"<Special Object Used to Force Acquisition>");
/* Create the module and add the functions */
m = Py_InitModule4("_Acquisition", methods,
"Provide base classes for acquiring objects\n\n"
"$Id$\n",
OBJECT(NULL),PYTHON_API_VERSION);
d = PyModule_GetDict(m);
init_py_names();
PyExtensionClass_Export(d,"Acquirer",AcquirerType);
PyExtensionClass_Export(d,"ImplicitAcquisitionWrapper",Wrappertype);
PyExtensionClass_Export(d,"ExplicitAcquirer",ExplicitAcquirerType);
PyExtensionClass_Export(d,"ExplicitAcquisitionWrapper",XaqWrappertype);
/* Create aliases */
PyDict_SetItemString(d,"Implicit",OBJECT(&AcquirerType));
PyDict_SetItemString(d,"Explicit",OBJECT(&ExplicitAcquirerType));
PyDict_SetItemString(d,"Acquired",Acquired);
AcquisitionCAPI.AQ_Acquire = capi_aq_acquire;
AcquisitionCAPI.AQ_Get = capi_aq_get;
AcquisitionCAPI.AQ_IsWrapper = capi_aq_iswrapper;
AcquisitionCAPI.AQ_Base = capi_aq_base;
AcquisitionCAPI.AQ_Parent = capi_aq_parent;
AcquisitionCAPI.AQ_Self = capi_aq_self;
AcquisitionCAPI.AQ_Inner = capi_aq_inner;
AcquisitionCAPI.AQ_Chain = capi_aq_chain;
api = PyCObject_FromVoidPtr(&AcquisitionCAPI, NULL);
PyDict_SetItemString(d, "AcquisitionCAPI", api);
Py_DECREF(api);
}
from zope.interface import classImplements
from _Acquisition import *
from interfaces import IAcquirer
from interfaces import IAcquisitionWrapper
classImplements(Explicit, IAcquirer)
classImplements(ExplicitAcquisitionWrapper, IAcquisitionWrapper)
classImplements(Implicit, IAcquirer)
classImplements(ImplicitAcquisitionWrapper, IAcquisitionWrapper)
##############################################################################
#
# 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.
# 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 z3 interfaces.
$Id$
"""
from zope.interface import Attribute
from zope.interface import Interface
class IAcquirer(Interface):
"""Acquire attributes from containers.
"""
def __of__(context):
"""Get the object in a context.
"""
class IAcquisitionWrapper(Interface):
"""Wrapper object for acquisition.
"""
def aq_acquire(name, filter=None, extra=None, explicit=True, default=0,
containment=0):
"""Get an attribute, acquiring it if necessary.
"""
def aq_inContextOf(obj, inner=1):
"""Test whether the object is currently in the context of the argument.
"""
aq_base = Attribute(
"""Get the object unwrapped."""
)
aq_parent = Attribute(
"""Get the parent of an object."""
)
aq_self = Attribute(
"""Get the object with the outermost wrapper removed."""
)
aq_inner = Attribute(
"""Get the object with all but the innermost wrapper removed."""
)
aq_chain = Attribute(
"""Get a list of objects in the acquisition environment."""
)
aq_explicit = Attribute(
"""Get the object with an explicit acquisition wrapper."""
)
##############################################################################
#
# Copyright (c) 2003 Zope Foundation 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
>>> c.__parent__
Traceback (most recent call last):
...
AttributeError: __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
>>> Acquisition.aq_inContextOf(a.b.c, a)
1
>>> Acquisition.aq_inContextOf(a.b.c, a.b)
1
>>> Acquisition.aq_inContextOf(a.b.c, 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
A wrapper's __parent__ attribute (which is equivalent to its
aq_parent attribute) points to the Acquisition parent.
>>> a.b.c.__parent__ == a.b.c.aq_parent
True
>>> a.b.c.__parent__ == a.b
True
"""
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('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_aq_inContextOf():
"""
>>> 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
>>> class Location(object):
... __parent__ = None
>>> 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
>>> l = Location()
>>> l.__parent__ = b.c
>>> def checkContext(self, o):
... # Python equivalent to aq_inContextOf
... from Acquisition import aq_base, aq_parent, aq_inner
... next = self
... o = aq_base(o)
... while 1:
... if aq_base(next) is o:
... return 1
... self = aq_inner(next)
... if self is None:
... break
... next = aq_parent(self)
... if next is None:
... break
... return 0
>>> checkContext(b.c, b)
1
>>> not checkContext(b.c, b.a)
1
>>> checkContext(l, b)
1
>>> checkContext(l, b.c)
1
>>> not checkContext(l, b.a)
1
Acquisition.aq_inContextOf works the same way:
>>> Acquisition.aq_inContextOf(b.c, b)
1
>>> Acquisition.aq_inContextOf(b.c, b.a)
0
>>> Acquisition.aq_inContextOf(l, b)
1
>>> Acquisition.aq_inContextOf(l, b.c)
1
>>> Acquisition.aq_inContextOf(l, b.a)
0
>>> 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
>>> b.c.aq_inContextOf(foo)
0
>>> b.c.aq_inContextOf(b.a)
0
>>> b.a.aq_inContextOf('somestring')
0
"""
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 / __parent__ on a wrapper:
>>> x = B()
>>> x.color = 'green'
>>> w.aq_parent = x
>>> w.color
'green'
>>> y = B()
>>> y.color = 'blue'
>>> w.__parent__ = y
>>> w.color
'blue'
Note that messing with the wrapper won't in any way affect the
wrapped object:
>>> Acquisition.aq_base(w).__parent__
Traceback (most recent call last):
...
AttributeError: __parent__
>>> 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_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:
>>> 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 sys
>>> 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
... def __iter__(self):
... print 'iterating...'
... return iter((42,))
... def __getslice__(self, start, end):
... print 'slicing...'
... return (start, end)
The naked class behaves like this:
>>> c = C()
>>> 3 in c
contains 3
False
>>> 5 in c
contains 5
True
>>> list(c)
iterating...
[42]
>>> c[5:10]
slicing...
(5, 10)
>>> c[5:] == (5, sys.maxsize)
slicing...
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
>>> list(i.c)
iterating...
[42]
>>> i.c[5:10]
slicing...
(5, 10)
>>> i.c[5:] == (5, sys.maxsize)
slicing...
True
Let's let's test the same again with an explicit wrapper:
>>> import Acquisition
>>> class Impl(Acquisition.Explicit):
... pass
>>> class C(Acquisition.Explicit):
... 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
... def __iter__(self):
... print 'iterating...'
... return iter((42,))
... def __getslice__(self, start, end):
... print 'slicing...'
... return (start, end)
The naked class behaves like this:
>>> c = C()
>>> 3 in c
contains 3
False
>>> 5 in c
contains 5
True
>>> list(c)
iterating...
[42]
>>> c[5:10]
slicing...
(5, 10)
>>> c[5:] == (5, sys.maxsize)
slicing...
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
>>> list(i.c)
iterating...
[42]
>>> i.c[5:10]
slicing...
(5, 10)
>>> i.c[5:] == (5, sys.maxsize)
slicing...
True
Next let's check that the wrapper's __iter__ proxy falls back
to using the object's __getitem__ if it has no __iter__. See
https://bugs.launchpad.net/zope2/+bug/360761 .
>>> class C(Acquisition.Implicit):
... l=[1,2,3]
... def __getitem__(self, i):
... return self.l[i]
>>> c1 = C()
>>> type(iter(c1))
<type 'iterator'>
>>> list(c1)
[1, 2, 3]
>>> c2 = C().__of__(c1)
>>> type(iter(c2))
<type 'iterator'>
>>> list(c2)
[1, 2, 3]
The __iter__proxy should also pass the wrapped object as self to
the __iter__ of objects defining __iter__::
>>> class C(Acquisition.Implicit):
... def __iter__(self):
... print 'iterating...'
... for i in range(5):
... yield i, self.aq_parent.name
>>> c = C()
>>> i = Impl()
>>> i.c = c
>>> i.name = 'i'
>>> list(i.c)
iterating...
[(0, 'i'), (1, 'i'), (2, 'i'), (3, 'i'), (4, 'i')]
And it should pass the wrapped object as self to
the __getitem__ of objects without an __iter__::
>>> class C(Acquisition.Implicit):
... def __getitem__(self, i):
... return self.aq_parent.l[i]
>>> c = C()
>>> i = Impl()
>>> i.c = c
>>> i.l = range(5)
>>> list(i.c)
[0, 1, 2, 3, 4]
Finally let's make sure errors are still correctly raised after having
to use a modified version of `PyObject_GetIter` for iterator support::
>>> class C(Acquisition.Implicit):
... pass
>>> c = C()
>>> i = Impl()
>>> i.c = c
>>> list(i.c)
Traceback (most recent call last):
...
TypeError: iteration over non-sequence
>>> class C(Acquisition.Implicit):
... def __iter__(self):
... return [42]
>>> c = C()
>>> i = Impl()
>>> i.c = c
>>> list(i.c)
Traceback (most recent call last):
...
TypeError: iter() returned non-iterator of type 'list'
"""
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 as 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
as does ``aq_get``:
>>> Acquisition.aq_get(x, 'hello')
'world'
>>> Acquisition.aq_get(x, 'foo')
42
>>> Acquisition.aq_get(x, 'bar')
3.145
and ``aq_parent``:
>>> Acquisition.aq_parent(x) is y
True
>>> Acquisition.aq_parent(y) is z
True
as well as ``aq_chain``:
>>> Acquisition.aq_chain(x) == [x, y, z]
True
"""
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 Impl(Acquisition.Implicit):
... foo = 42
>>> y = Impl().__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
as does ``aq_get``:
>>> Acquisition.aq_get(x, 'hello')
'world'
>>> Acquisition.aq_get(x, 'foo')
42
>>> Acquisition.aq_get(x, 'bar')
3.145
and ``aq_parent``:
>>> Acquisition.aq_parent(x) is y
True
>>> Acquisition.aq_parent(y) is z
True
as well as ``aq_chain``:
>>> Acquisition.aq_chain(x) == [x, y, z]
True
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
Just as much as you can assign to aq_parent, you can also assign
to __parent__ to change the acquisition context of the wrapper:
>>> newroot = Root()
>>> y.__parent__ = newroot
>>> y.__parent__ is z
False
>>> y.__parent__ is newroot
True
Note that messing with the wrapper won't in any way affect the
wrapped object:
>>> Acquisition.aq_base(y).__parent__
Traceback (most recent call last):
...
AttributeError: __parent__
"""
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 Expl(Acquisition.Explicit):
... foo = 42
>>> y = Expl().__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
as does ``aq_get``:
>>> Acquisition.aq_get(x, 'hello')
'world'
>>> Acquisition.aq_get(x, 'foo')
42
>>> Acquisition.aq_get(x, 'bar')
3.145
and ``aq_parent``:
>>> Acquisition.aq_parent(x) is y
True
>>> Acquisition.aq_parent(y) is z
True
as well as ``aq_chain``:
>>> Acquisition.aq_chain(x) == [x, y, z]
True
Note that also the (explicit) 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
Just as much as you can assign to aq_parent, you can also assign
to __parent__ to change the acquisition context of the wrapper:
>>> newroot = Root()
>>> y.__parent__ = newroot
>>> y.__parent__ is z
False
>>> y.__parent__ is newroot
True
Note that messing with the wrapper won't in any way affect the
wrapped object:
>>> Acquisition.aq_base(y).__parent__
Traceback (most recent call last):
...
AttributeError: __parent__
"""
def test_implicit_wrapper_has_nonwrapper_as_aq_parent():
"""Let's do this the other way around: The root and the
intermediate parent is an object that doesn't support acquisition,
>>> y = ECLocation()
>>> z = Location()
>>> y.__parent__ = z
>>> y.foo = 42
>>> z.foo = 43 # this should not be found
>>> z.bar = 3.145
only the outmost object does:
>>> class Impl(Acquisition.Implicit):
... hello = 'world'
>>> x = Impl().__of__(y)
Again, acquiring objects works as usual:
>>> Acquisition.aq_acquire(x, 'hello')
'world'
>>> Acquisition.aq_acquire(x, 'foo')
42
>>> Acquisition.aq_acquire(x, 'bar')
3.145
as does ``aq_get``:
>>> Acquisition.aq_get(x, 'hello')
'world'
>>> Acquisition.aq_get(x, 'foo')
42
>>> Acquisition.aq_get(x, 'bar')
3.145
and ``aq_parent``:
>>> Acquisition.aq_parent(x) == y
True
>>> x.aq_parent == y
True
>>> x.aq_parent.aq_parent == z
True
>>> Acquisition.aq_parent(y) is z
True
as well as ``aq_chain``:
>>> Acquisition.aq_chain(x) == [x, y, z]
True
>>> x.aq_chain == [x, y, z]
True
Because the outmost object, ``x``, is wrapped in an implicit
acquisition wrapper, we can also use direct attribute access:
>>> x.hello
'world'
>>> x.foo
42
>>> x.bar
3.145
"""
def test_explicit_wrapper_has_nonwrapper_as_aq_parent():
"""Let's do this the other way around: The root and the
intermediate parent is an object that doesn't support acquisition,
>>> y = ECLocation()
>>> z = Location()
>>> y.__parent__ = z
>>> y.foo = 42
>>> z.foo = 43 # this should not be found
>>> z.bar = 3.145
only the outmost object does:
>>> class Expl(Acquisition.Explicit):
... hello = 'world'
>>> x = Expl().__of__(y)
Again, acquiring objects works as usual:
>>> Acquisition.aq_acquire(x, 'hello')
'world'
>>> Acquisition.aq_acquire(x, 'foo')
42
>>> Acquisition.aq_acquire(x, 'bar')
3.145
as does ``aq_get``:
>>> Acquisition.aq_get(x, 'hello')
'world'
>>> Acquisition.aq_get(x, 'foo')
42
>>> Acquisition.aq_get(x, 'bar')
3.145
and ``aq_parent``:
>>> Acquisition.aq_parent(x) == y
True
>>> x.aq_parent == y
True
>>> x.aq_parent.aq_parent == z
True
>>> Acquisition.aq_parent(y) is z
True
as well as ``aq_chain``:
>>> Acquisition.aq_chain(x) == [x, y, z]
True
>>> x.aq_chain == [x, y, z]
True
"""
def test___parent__aq_parent_circles():
"""
As a general safety belt, Acquisition won't follow a mixture of
circular __parent__ pointers and aq_parent wrappers. These can
occurr when code that uses implicit acquisition wrappers meets
code that uses __parent__ pointers.
>>> class Impl(Acquisition.Implicit):
... hello = 'world'
>>> class Impl2(Acquisition.Implicit):
... hello = 'world2'
... only = 'here'
>>> x = Impl()
>>> y = Impl2().__of__(x)
>>> x.__parent__ = y
>>> x.__parent__.aq_base is y.aq_base
True
>>> x.__parent__.__parent__ is x
True
>>> x.hello
'world'
>>> Acquisition.aq_acquire(x, 'hello')
'world'
>>> x.only
Traceback (most recent call last):
...
AttributeError: only
>>> Acquisition.aq_acquire(x, 'only')
'here'
>>> Acquisition.aq_acquire(x, 'non_existant_attr')
Traceback (most recent call last):
...
AttributeError: non_existant_attr
>>> Acquisition.aq_acquire(y, 'non_existant_attr')
Traceback (most recent call last):
...
AttributeError: non_existant_attr
>>> x.non_existant_attr
Traceback (most recent call last):
...
AttributeError: non_existant_attr
>>> y.non_existant_attr
Traceback (most recent call last):
...
AttributeError: non_existant_attr
"""
def test__iter__after_AttributeError():
""" See https://bugs.launchpad.net/zope2/+bug/1155760
>>> from Acquisition import Implicit
>>> class C(Implicit):
... l = [0, 1, 2, 3, 4]
... def __getitem__(self, i):
... return self.l[i]
>>> a = C()
>>> b = C().__of__(a)
>>> import time
>>> try:
... for n in b:
... t = time.gmtime()
... except AttributeError:
... raise
"""
import unittest
from doctest import DocTestSuite, DocFileSuite
class TestParent(unittest.TestCase):
def test_parent_parent_circles(self):
class Impl(Acquisition.Implicit):
hello = 'world'
class Impl2(Acquisition.Implicit):
hello = 'world2'
only = 'here'
x = Impl()
y = Impl2()
x.__parent__ = y
y.__parent__ = x
self.assertTrue(x.__parent__.__parent__ is x)
self.assertEqual(Acquisition.aq_acquire(x, 'hello'), 'world')
self.assertEqual(Acquisition.aq_acquire(x, 'only'), 'here')
self.assertRaises(AttributeError, Acquisition.aq_acquire,
x, 'non_existant_attr')
self.assertRaises(AttributeError, Acquisition.aq_acquire,
y, 'non_existant_attr')
def test_parent_parent_parent_circles(self):
class Impl(Acquisition.Implicit):
hello = 'world'
class Impl2(Acquisition.Implicit):
hello = 'world'
class Impl3(Acquisition.Implicit):
hello = 'world2'
only = 'here'
a = Impl()
b = Impl2()
c = Impl3()
a.__parent__ = b
b.__parent__ = c
c.__parent__ = a
# This is not quite what you'd expect, an AQ circle with an
# intermediate object gives strange results
self.assertTrue(a.__parent__.__parent__ is a)
self.assertTrue(a.__parent__.__parent__.__parent__.aq_base is b)
self.assertTrue(b.__parent__.__parent__ is b)
self.assertTrue(c.__parent__.__parent__ is c)
self.assertEqual(Acquisition.aq_acquire(a, 'hello'), 'world')
self.assertEqual(Acquisition.aq_acquire(b, 'hello'), 'world')
self.assertEqual(Acquisition.aq_acquire(c, 'hello'), 'world2')
self.assertRaises(AttributeError, Acquisition.aq_acquire,
a, 'only')
self.assertEqual(Acquisition.aq_acquire(b, 'only'), 'here')
self.assertEqual(Acquisition.aq_acquire(c, 'only'), 'here')
self.assertRaises(AttributeError, Acquisition.aq_acquire,
a, 'non_existant_attr')
self.assertRaises(AttributeError, Acquisition.aq_acquire,
b, 'non_existant_attr')
self.assertRaises(AttributeError, Acquisition.aq_acquire,
c, 'non_existant_attr')
class TestAcquire(unittest.TestCase):
def setUp(self):
class Impl(Acquisition.Implicit):
pass
class Expl(Acquisition.Explicit):
pass
a = Impl('a')
a.y = 42
a.b = Expl('b')
a.b.z = 3
a.b.c = Impl('c')
self.a = a
self.acquire = Acquisition.aq_acquire
def test_explicit_module_default(self):
self.assertEqual(self.acquire(self.a.b.c, 'z'), 3)
def test_explicit_module_true(self):
self.assertEqual(self.acquire(self.a.b.c, 'z', explicit=True), 3)
def test_explicit_module_false(self):
self.assertEqual(self.acquire(self.a.b.c, 'z', explicit=False), 3)
def test_explicit_wrapper_default(self):
self.assertEqual(self.a.b.c.aq_acquire('z'), 3)
def test_explicit_wrapper_true(self):
self.assertEqual(self.a.b.c.aq_acquire('z', explicit=True), 3)
def test_explicit_wrapper_false(self):
self.assertEqual(self.a.b.c.aq_acquire('z', explicit=False), 3)
def test_wrapper_falls_back_to_default(self):
self.assertEqual(self.acquire(self.a.b.c, 'nonesuch', default=4), 4)
def test_no_wrapper_but___parent___falls_back_to_default(self):
class NotWrapped(object):
pass
child = NotWrapped()
parent = child.__parent__ = NotWrapped()
self.assertEqual(self.acquire(child, 'nonesuch', default=4), 4)
def test_unwrapped_falls_back_to_default(self):
self.assertEqual(self.acquire(object(), 'nonesuch', default=4), 4)
def test_w_unicode_attr_name(self):
# See https://bugs.launchpad.net/acquisition/+bug/143358
found = self.acquire(self.a.b.c, u'aq_parent')
self.assertTrue(found.aq_self is self.a.b.aq_self)
class TestUnicode(unittest.TestCase):
def test_implicit_aq_unicode_should_be_called(self):
class A(Acquisition.Implicit):
def __unicode__(self):
return u'unicode was called'
wrapped = A().__of__(A())
self.assertEqual(u'unicode was called', unicode(wrapped))
self.assertEqual(str(wrapped), repr(wrapped))
def test_explicit_aq_unicode_should_be_called(self):
class A(Acquisition.Explicit):
def __unicode__(self):
return u'unicode was called'
wrapped = A().__of__(A())
self.assertEqual(u'unicode was called', unicode(wrapped))
self.assertEqual(str(wrapped), repr(wrapped))
def test_implicit_should_fall_back_to_str(self):
class A(Acquisition.Implicit):
def __str__(self):
return 'str was called'
wrapped = A().__of__(A())
self.assertEqual(u'str was called', unicode(wrapped))
self.assertEqual('str was called', str(wrapped))
def test_explicit_should_fall_back_to_str(self):
class A(Acquisition.Explicit):
def __str__(self):
return 'str was called'
wrapped = A().__of__(A())
self.assertEqual(u'str was called', unicode(wrapped))
self.assertEqual('str was called', str(wrapped))
def test_str_fallback_should_be_called_with_wrapped_self(self):
class A(Acquisition.Implicit):
def __str__(self):
return str(self.aq_parent == outer)
outer = A()
inner = A().__of__(outer)
self.assertEqual(u'True', unicode(inner))
def test_unicode_should_be_called_with_wrapped_self(self):
class A(Acquisition.Implicit):
def __unicode__(self):
return str(self.aq_parent == outer)
outer = A()
inner = A().__of__(outer)
self.assertEqual(u'True', unicode(inner))
def test_suite():
return unittest.TestSuite((
DocTestSuite(),
DocFileSuite('README.txt', package='Acquisition'),
unittest.makeSuite(TestParent),
unittest.makeSuite(TestAcquire),
unittest.makeSuite(TestUnicode),
))
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