[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 (4 years, 11 months 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.

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

        >>> binding.getParentComponent(c1a) is c1
        True

    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
        None

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

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

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

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

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

        >>> binding.getComponentName(protocols)
        'protocols'

        >>> binding.getComponentName(binding.Component)
        'Component'

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

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

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

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

``iterParents(component,max_depth=100)``
    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]
        True
        >>> list(binding.iterParents(c1a)) == [c1a,c1,root]
        True
        >>> list(binding.iterParents(root)) == [root]
        True
        >>> list(binding.iterParents(42)) == [42]
        True
        
    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.  :)


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

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


``getRootComponent(component)``
    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
        True
    
        >>> binding.getRootComponent(binding)
        <module 'peak' from ...>
    
        >>> binding.getRootComponent(binding.Component)
        <module 'peak' from ...>

        >>> binding.getRootComponent(42)
        42


``getComponentPath(component,relativeTo=None)``
    Return the ``binding.ComponentName`` that would traverse from `relativeTo`
    to `component`::

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

        >>> print binding.getComponentPath(binding.Component)
        /binding/components/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)
        binding/components/Component


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
    True

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

    >>> binding.acquireComponent(c2, 'c3', NOT_FOUND)
    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
    True

If the first attribute name isn't found, it's looked up in the hierarchy using
``acquireComponent()``::

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

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)
    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')
    2
    >>> binding.lookupComponent(c3, 'c2/x')
    2
    >>> binding.lookupComponent(c2, 'c3/x')
    3
    >>> binding.lookupComponent(c3, 'c3/x')
    3
    >>> 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)
    99

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


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


    >>> binding.lookupComponent(c2, './x')
    2
    >>> binding.lookupComponent(c2, '../x')
    1
    
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



cvs-admin@eby-sarna.com

Powered by ViewCVS 1.0-dev

ViewCVS and CVS Help