[Subversion] / PEAK / src / peak / binding / components.txt  

View of /PEAK/src/peak/binding/components.txt

Parent Directory | Revision Log
Revision: 2781 - (download)
Sun Nov 1 00:48:40 2015 UTC (5 years, 1 month ago) by pje
File size: 10718 byte(s)
Fix component acquisition defaults and doc/test lookupComponent()
Binding Components

    >>> from peak.api import *

The Component Hierarchy API

    >>> root = config.makeRoot()
    >>> c1 = binding.Component(root,"c1")
    >>> c2 = binding.Component(root)
    >>> c1a = binding.Component(c1)

Fundamental Operations

There are two fundamental operations in the component hierarchy API.  These
generic functions are used as a basis for all of the other operations, so to
use the rest of the hierarchy API for a given type, you need only define
methods for these two basic operations.  Definitions already exist for
PEAK-supplied types and for modules, so you only need to add methods for
types you want to use, that aren't subclasses of an already supported type.

    Return parent of `component`, or ``None`` if root or non-component, e.g.::

        >>> binding.getParentComponent(c1a) is c1

    This also works for module objects, and 'binding.ActiveClass' objects,
    for which the containing module or package is returned::

        >>> binding
        <module 'peak.binding.api' from ...>

        >>> binding.getParentComponent(binding)
        <module 'peak.binding' from ...>

        >>> binding.Component
        <class 'peak.binding.components.Component'>

        >>> binding.getParentComponent(binding.Component)
        <module 'peak.binding.components' from ...>

    This is a single-dispatch generic function, so you can add cases for
    additional object types using 'binding.getParentComponent.when()' as a
    decorator.  For example::

        >>> class MyComponent:
        ...     def __init__(self,parent): self.my_parent = parent

        >>> anOb = MyComponent(42)
        >>> print binding.getParentComponent(anOb)  # not a known type yet

        >>> if binding.getParentComponent.when(MyComponent):    # XXX @
        ...     def get_for_myComponent(component):
        ...         return component.my_parent

        >>> binding.getParentComponent(anOb)    # now it's known

    Return name of `component`, or ``None`` if unknown or a non-component::

        >>> binding.getComponentName(c1)
        >>> print binding.getComponentName(c2)
        >>> print binding.getComponentName(42)

    This also works for module objects, and 'binding.ActiveClass' objects,
    for which the module or class' '__name__' is returned::

        >>> binding.getComponentName(protocols)

        >>> binding.getComponentName(binding.Component)

    This is a single-dispatch generic function, so you can add cases for
    additional object types using 'binding.getComponentName.when()' as a

        >>> class MyComponent:
        ...     def __init__(self,name): self.my_name = name

        >>> anOb = MyComponent("blue 42")
        >>> print binding.getComponentName(anOb)  # not a known type yet

        >>> if binding.getComponentName.when(MyComponent):    # XXX @
        ...     def get_for_myComponent(component):
        ...         return component.my_name

        >>> binding.getComponentName(anOb)  # now it's known
        'blue 42'

So, any class for which the above two operations are defined can be used with
the remaining hierarchy APIs, below.

Convenience Operations

These convenience APIs are shortcuts for common usage patterns when working
with a component hierarchy.  They're defined in terms of the fundamental
operations above, so they'll work for any type you've defined the fundamental
operations for, and will degrade gracefully for other types.

    Iterate over the parents of `component`, beginning with `component` itself,
    in hierarchy order up through the root component (i.e., the first component
    whose parent is ``None``)::

        >>> list(binding.iterParents(c1)) == [c1,root]
        >>> list(binding.iterParents(c1a)) == [c1a,c1,root]
        >>> list(binding.iterParents(root)) == [root]
        >>> list(binding.iterParents(42)) == [42]
    The `max_depth` parameter controls how deeply the hierarchy can be iterated
    before a recursion error occurs.  This is intended to prevent infinite
    iteration if you accidentally create a circular hierarchy, e.g.::
        >>> x = binding.Component()
        >>> x.setParentComponent(x)

        >>> list(binding.iterParents(x))
        Traceback (most recent call last):
        RuntimeError: ('maximum recursion limit exceeded', ...)

        >>> del x

    Currently, PEAK assumes that there is little reason to have a component
    with 100 levels of parents; consider that if each component in such a
    hierarchy has two children, that's 2^100 objects, which is more objects
    than can be fit into a 64-bit computer's address space.  :)

    Is `component` within the hierarchy of `parent`?  This routine returns
    truth if ``component is parent`` or if `parent` is one of the parents of
        >>> binding.hasParent(c1a,c1a)
        >>> binding.hasParent(c1a,c1)
        >>> binding.hasParent(c1a,root)
        >>> binding.hasParent(c1a,c2)

    ``hasParent()`` is specially optimized for use with generic functions,
    so that you can define generic function methods that apply only within
    a particular component hierarchy.

    Return the root component of the tree `component` belongs to.  Basically
    this returns the first parent of `component` whose parent is ``None``::

        >>> binding.getRootComponent(c1) is root
        >>> binding.getRootComponent(binding)
        <module 'peak' from ...>
        >>> binding.getRootComponent(binding.Component)
        <module 'peak' from ...>

        >>> binding.getRootComponent(42)

    Return the ``binding.ComponentName`` that would traverse from `relativeTo`
    to `component`::

        >>> binding.getComponentPath(binding.Component)
        ComponentName(['', 'binding', 'components', 'Component'])

        >>> print binding.getComponentPath(binding.Component)

    If `relativeTo` is ``None`` or not supplied, the path returned is relative
    to the root component of `component`.  Note that if supplied, `relativeTo`
    must be an ancestor (parent, parent's parent, etc.) of `component`::

        >>> import peak
        >>> binding.getComponentPath(binding.Component, peak)
        ComponentName(['binding', 'components', 'Component'])

        >>> print binding.getComponentPath(binding.Component, peak)

Finding Components

``acquireComponent(component, name, default=NOT_GIVEN)``

If `component` has an attribute of `name`, return its value.  Otherwise,
walk the ``iterParents()`` of `component` looking for `name`.  If no parent
has the named attribute, it adapts the last parent found to a
``config.IConfigurationRoot`` and delegates the lookup via the
``nameNotFound()`` method of that interface.  (The default implementation
does a `naming.lookup()`, which in turn will raise a ``NameNotFound`` exception
or return the provided `default`.)


    >>> c1 = binding.Component(root, 'c1')
    >>> c2 = c1.c2 = binding.Component(c1, 'c2')

    >>> binding.acquireComponent(c2, 'c2') is c2

    >>> binding.acquireComponent(c2, 'c3')
    Traceback (most recent call last):
    NameNotFound:  [remainingName=CompoundName(['c3']),resolvedObj=<...>]

    >>> binding.acquireComponent(c2, 'c3', NOT_FOUND)

``lookupComponent(component, name, default=NOT_GIVEN, adaptTo=None, creationName=None, suggestParent=True)``

Lookup `name` as a component key relative to `component`.  If the key cannot be
found, an ``exceptions.NameNotFound`` error will be raised unless a `default`
other than ``NOT_GIVEN`` is provided.

`name` can be any object that implements or is adaptable to ``IComponentKey``.
Such objects include ``peak.naming`` names, interface objects, property
names, and any custom objects you may create that implement ``IComponentKey``.
Strings will be converted to a URL, or to a ``ComponentName`` if they have
no URL prefix.

``ComponentName`` names are ``/``-separated attribute paths::

    >>> binding.lookupComponent(c1, 'c2') is c2

If the first attribute name isn't found, it's looked up in the hierarchy using

    >>> binding.lookupComponent(c2, 'c2') is c2

Which of course will fail if the name isn't found::

    >>> binding.lookupComponent(c1, 'c3')
    Traceback (most recent call last):
    NameNotFound:  [remainingName=CompoundName(['c3']),resolvedObj=<...>]

Or fall back to the default if one is given::

    >>> binding.lookupComponent(c1, 'c3', 99)

This also works with multi-part names: the first part is acquired, but the
other parts must be attributes of the component acquired by the first part::

    >>> c3 = c2.c3 = binding.Component(c2, 'c3')
    >>> c1.x = 1
    >>> c2.x = 2
    >>> c3.x = 3

    >>> binding.lookupComponent(c2, 'c2/x')
    >>> binding.lookupComponent(c3, 'c2/x')
    >>> binding.lookupComponent(c2, 'c3/x')
    >>> binding.lookupComponent(c3, 'c3/x')
    >>> binding.lookupComponent(c3, 'c1/x')  # (c1 isn't an attribute of root)
    Traceback (most recent call last):
    NameNotFound:  [remainingName=CompoundName(['c1']),resolvedObj=<...>]
    >>> binding.lookupComponent(c3, 'c1/x', 99)

    >>> binding.lookupComponent(c3, 'c2/y')
    Traceback (most recent call last):
    NameNotFound:  [resolvedName=ComponentName([]),remainingName=ComponentName(['y']),resolvedObj=<...>]
    >>> binding.lookupComponent(c3, 'c2/y', 99)

Paths can be explicitly relative, using ``.`` and ``..`` to refer to the
current component or its parent::

    >>> binding.lookupComponent(c2, './x')
    >>> binding.lookupComponent(c2, '../x')
They can also be root-relative, by starting with a ``/``::

    >>> binding.lookupComponent(root, '/getParentComponent')
    <bound method ConfigurationRoot.getParentComponent...>

XXX IComponentKey

Assembling Components

XXX notifyUponAssembly, suggestParentComponent, IAttachable


Powered by ViewCVS 1.0-dev

ViewCVS and CVS Help