================== 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 >>> binding.getParentComponent(binding) >>> binding.Component >>> binding.getParentComponent(binding.Component) 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) >>> binding.getRootComponent(binding.Component) >>> 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') XXX IComponentKey Assembling Components ===================== XXX notifyUponAssembly, suggestParentComponent, IAttachable