Acquisition

raw JSON →
6.2 verified Thu Apr 16 auth: no python

Acquisition is a mechanism that allows objects to obtain attributes from the containment hierarchy they're in. This foundational Zope library, currently at version 6.2, facilitates dynamic attribute lookup based on an object's position within a hierarchy or explicitly set context. While it does not have a fixed release cadence, it is actively maintained by the Zope Foundation.

pip install Acquisition
error AttributeError: 'ClassName' object has no attribute 'attribute_name'
cause The requested attribute 'attribute_name' could not be found on the object itself, nor was it successfully acquired from its containment hierarchy or explicit context. This often means the attribute is missing, or the acquisition path is not correctly configured.
fix
Inspect the object's acquisition chain using obj.aq_chain() to verify the available parents and their attributes. Ensure the attribute exists on one of the objects in the expected acquisition path. Use obj.aq_acquire(attribute_name, default=None) to safely test for acquirable attributes.
error ModuleNotFoundError: No module named 'Acquisition'
cause The 'Acquisition' library is not installed in the current Python environment, or the environment is not correctly activated.
fix
Install the library using pip: pip install Acquisition. If using a virtual environment, ensure it is activated before installation and execution.
error TypeError: descriptor '__of__' requires a 'ClassName' object but received a 'AnotherClass' object
cause The `__of__` method, used to set acquisition context, expects an instance of an Acquisition-aware class (e.g., one that inherits from `Acquisition.Implicit` or `Acquisition.Explicit`). Providing an object of a different type will raise a TypeError.
fix
Ensure that both the object on which __of__ is called and the object passed as its argument are instances of classes that properly support Acquisition, typically by inheriting from Acquisition.Implicit or Acquisition.Explicit.
gotcha C methods defined in extension base classes that define their own data structures cannot use acquired attributes. This is because acquisition wrapper objects do not conform to the data structures expected by these methods, leading to potential `AttributeError` or `TypeError` if invoked on an wrapped object.
fix Avoid using Acquisition with C methods in extension base classes that rely on specific internal data structures. Consider refactoring to use Python methods or explicitly unwrapping the object (`obj.aq_base`) before calling C methods, though this removes acquisition capabilities for that call.
gotcha The behavior of acquisition can become complex due to subtle differences between 'acquiring from context' and 'acquiring from containment'. Misunderstanding these distinctions can lead to unexpected attribute resolution or attributes not being found.
fix Carefully review the official documentation on explicit vs. implicit acquisition and the roles of `__of__` (context) and object containment. Debug acquisition paths using `obj.aq_chain()` to visualize the hierarchy and `obj.aq_base` to get the unwrapped object.
gotcha Over-reliance on implicit acquisition can make code harder to reason about and debug, as attribute sources are not immediately obvious. This can lead to unexpected side effects or performance overhead from dynamic lookups.
fix Use acquisition judiciously for genuinely hierarchical patterns. For critical paths or where clarity is paramount, consider more explicit dependency injection or passing attributes directly rather than relying solely on the acquisition chain.

This example demonstrates basic implicit acquisition. An object `b` acquires attributes from `a` when `a` is set as its context using `__of__`. Subsequent objects in the context chain can also acquire attributes from higher up the hierarchy.

from Acquisition import Implicit

class C(Implicit):
    pass

a = C()
b = C()

a.color = "red"

# 'b' acquires 'color' from 'a' because 'a' is put into b's context
print(b.__of__(a).color)

class X(Implicit):
    pass

x = X().__of__(b.__of__(a))

# 'x' acquires 'color' from 'a' through 'b'
print(x.color)