Skip to main content

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 places 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

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

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.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

Acquisition-2.12.4.zip (64.4 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

Acquisition-2.12.4-py2.7-win-amd64.egg (60.0 kB view details)

Uploaded Egg

Acquisition-2.12.4-py2.7-win32.egg (58.9 kB view details)

Uploaded Egg

Acquisition-2.12.4-py2.6-win-amd64.egg (59.9 kB view details)

Uploaded Egg

Acquisition-2.12.4-py2.6-win32.egg (56.6 kB view details)

Uploaded Egg

Acquisition-2.12.4-py2.5-win32.egg (56.7 kB view details)

Uploaded Egg

Acquisition-2.12.4-py2.4-win32.egg (56.8 kB view details)

Uploaded Egg

File details

Details for the file Acquisition-2.12.4.zip.

File metadata

  • Download URL: Acquisition-2.12.4.zip
  • Upload date:
  • Size: 64.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No

File hashes

Hashes for Acquisition-2.12.4.zip
Algorithm Hash digest
SHA256 2d88493810ceecfe8bacf06ea154546c5f52f02dd0c9911274e935265543cdcd
MD5 b0c31576dd7dc19bb34554388b7726e8
BLAKE2b-256 0b03e067e3774c39fe109249cac26ede37e7fff209b4518f6a8056fea7afeffa

See more details on using hashes here.

File details

Details for the file Acquisition-2.12.4-py2.7-win-amd64.egg.

File metadata

File hashes

Hashes for Acquisition-2.12.4-py2.7-win-amd64.egg
Algorithm Hash digest
SHA256 dee21b74720aff0885f67c5677032be5f9251436f4c92c62118ee003dfde2646
MD5 02913a9c62d3a79adbd941242310ccff
BLAKE2b-256 3734ea1d2e820a921dab7dfc19d8fecb8958cd964db6e44c2285501080770f6f

See more details on using hashes here.

File details

Details for the file Acquisition-2.12.4-py2.7-win32.egg.

File metadata

File hashes

Hashes for Acquisition-2.12.4-py2.7-win32.egg
Algorithm Hash digest
SHA256 6cd6f6dfd90f04664dd3e6e4362fc91de57313aff240cc1833d0a7c60ec3521e
MD5 d83de2221e947004ac4fcdc45ada1552
BLAKE2b-256 dc4c5a0b9878463ba3f2a27a5ca892bee3123e0606ff32fd079f1a88f15b5286

See more details on using hashes here.

File details

Details for the file Acquisition-2.12.4-py2.6-win-amd64.egg.

File metadata

File hashes

Hashes for Acquisition-2.12.4-py2.6-win-amd64.egg
Algorithm Hash digest
SHA256 f60b132cec8be38f7700c4cfaf76058aaf3a974c2e72cbaf9287788b3bb3a3a7
MD5 22b56b8e6d00361f618ddc10f34febdf
BLAKE2b-256 599858590c0b0153d5a755ac81d4ae4fb7a9c94ffdafc0642b60ddbf1b209ad7

See more details on using hashes here.

File details

Details for the file Acquisition-2.12.4-py2.6-win32.egg.

File metadata

File hashes

Hashes for Acquisition-2.12.4-py2.6-win32.egg
Algorithm Hash digest
SHA256 93cf34d26524ffd78c8055378f60f2e6dd9d08e0d6ae053295d4cc2b107cc653
MD5 a066b6b92c4df12d57c46a407184beb7
BLAKE2b-256 d6ff40f591207ada1d0939a71a396826119ea889aa34eef4bcbe0b65ca66e003

See more details on using hashes here.

File details

Details for the file Acquisition-2.12.4-py2.5-win32.egg.

File metadata

File hashes

Hashes for Acquisition-2.12.4-py2.5-win32.egg
Algorithm Hash digest
SHA256 a24758572a597e4f33807af58d7bd27f5e9273d9d27cd3230b0860682a1e3938
MD5 1dedaaf5483de6cc9452e7cb755f65c0
BLAKE2b-256 99865bdf09cdd4705ea0b7fe2f9caacb212f98dfd40e9999904f135879a5227f

See more details on using hashes here.

File details

Details for the file Acquisition-2.12.4-py2.4-win32.egg.

File metadata

File hashes

Hashes for Acquisition-2.12.4-py2.4-win32.egg
Algorithm Hash digest
SHA256 5035c6180308db94b889b0848e1a8ef462ff8a34fcfb5e41df84d5a12a50f1a7
MD5 7ae89d15cc384d9020fd4fa41f04704f
BLAKE2b-256 5478e678d3bf00ff3c2624ee233bebc9337e2208daf6b80119bbf862c18c9a63

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page