[Zope-Checkins] SVN: Zope/trunk/lib/python/Acquisition/ Actually move the README.txt that I added to the Acquisition egg earlier to

Philipp von Weitershausen philikon at philikon.de
Fri Jul 27 14:07:04 EDT 2007


Log message for revision 78385:
  Actually move the README.txt that I added to the Acquisition egg earlier to
  the package itself and make it a doctest.
  

Changed:
  A   Zope/trunk/lib/python/Acquisition/README.txt
  U   Zope/trunk/lib/python/Acquisition/tests.py

-=-
Copied: Zope/trunk/lib/python/Acquisition/README.txt (from rev 78383, Acquisition/trunk/README.txt)
===================================================================
--- Zope/trunk/lib/python/Acquisition/README.txt	                        (rev 0)
+++ Zope/trunk/lib/python/Acquisition/README.txt	2007-07-27 18:07:04 UTC (rev 78385)
@@ -0,0 +1,417 @@
+.. 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 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
+
+.. 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.

Modified: Zope/trunk/lib/python/Acquisition/tests.py
===================================================================
--- Zope/trunk/lib/python/Acquisition/tests.py	2007-07-27 18:05:09 UTC (rev 78384)
+++ Zope/trunk/lib/python/Acquisition/tests.py	2007-07-27 18:07:04 UTC (rev 78385)
@@ -1665,11 +1665,12 @@
 
 
 import unittest
-from zope.testing.doctest import DocTestSuite
+from zope.testing.doctest import DocTestSuite, DocFileSuite
 
 def test_suite():
     return unittest.TestSuite((
         DocTestSuite(),
+        DocFileSuite('README.txt', package='Acquisition'),
         ))
 
 if __name__ == '__main__':



More information about the Zope-Checkins mailing list