We’ve seen the basic registration and lookup facilities of a repoze.component registry. You can provide additional functionality to your applications if you use it as an “component registry”. Using a registry as a component registry makes it possible to use the repoze.component registry for the same purposes as something like zope.component.
The extra method exposed by a repoze.component registry that allow you to treat it as a component regstry is resolve. The resolve method simply accepts a provides value and a sequence of objects that supply component types. When called with a list of objects that supply component types, resolve returns a matching component.
Objects used as “requires” arguments to the resolve method of a component registry usually supply a component type. This is done by adding using the provides function against objects passed to these methods.
The provides function may be used within a class definition, it may be used as a class decorator, or it may be applied against an instance. Within a class definition and as a class decorator, it accepts as positional arguments the component types.
from repoze.component import provides class MyObject(object): provides('mytype')
The followng is also legal.
from repoze.component import provides class MyObject(object): provides('mytype', 'anothertype')
Likewise, it’s also legal to do (in Python 2.6):
from repoze.component import provides @provides('mytype', 'anothertype') class MyObject(object): pass
You can also use the provides function against an instance, in which case the first argument is assumed to be the object you’re trying to assign component types to:
from repoze.component import provides class MyObject(object): pass obj = MyObject() provides(obj, 'mytype', 'anothertype')
Note that objects don’t explicitly need to have a provides attribute attribute for simple usage; the class of an object is an implicit component type that can be used in registrations.
“Under the hood”, the provides function sets the __component_types__ attribute on an instance or the __inherited_component_types__ attribute on a class.
There is also an onlyprovides API which performs the same action except inherited types are overwritten with the values passed to the API. For example:
.. code-block:: python
from repoze.component import onlyprovides
- class MyObject(object):
obj = MyObject() onlyprovides(obj, ‘mytype’, ‘anothertype’)
When a component type is computed for an object, the object is searched in the following order. All values are collected and used to construct the final “requires” argument used.
We’ll use the following set of objects as examples:
from repoze.component import provides class A(object): provides('a', 'hello') class B(A): provides('b') class C(B): provides('c') instance = C() provides(instance, 'i') provides(instance, 'i2')
When the preceding set of statements are made:
If “instance” is subsequently used as an argument to the resolve method of an component registry:
Thus our “requires” argument for this particular object is ('i2', 'i', 'c', 'b', 'a', 'hello', C, None). Every object supplied as a “requires” argument to the resolve method of a component registry has its requires values computed this way. We then find a component based on the set of requires arguments passed in ala Component Lookup Order When Requires Arguments are Specified.
Zope and Twisted developers (and any other developer who has used zope.component) will find repoze.component familiar. repoze.component steals concepts shamelessly from zope.component. repoze.component differs primarily from zope.component by abandoning the high-level concept of an interface. In zope.component, component lookups and registrations are done in terms of interfaces, which are very specific kinds of Python objects. In repoze.component, interfaces are not used. Instead, components (such as “adapters” and “utilities”) are registered using marker “component types”, which are usually just strings although they can be any hashable type.
One major difference between repoze.component and zope.component is that repoze.component has no real support for the concept of an “adapter”. The things that you register into a component registry are simply components. You can register a callable against some set of arguments, but repoze.component will not call it for you. You have to retrieve it and call it yourself.
In the examples below, where a zope.component API might expect an interface object (e.g. the interface ISomething), the repoze.component API expects a component type (e.g. the string something). Also, in the examples below, whereas zope.component users typically rely on APIs that consult a “global registry”, repoze.component provides no such facility. Thus examples that refer to registry below refer to a plugin registry created by parsing a configuration file (or constructed manually).
The repoze.component equivalent of utility = zope.component.getUtility(ISomething) is the following:
utility = registry.lookup('something')
The repoze.component equivalent of implementation = zope.component.getAdapter(context, ISomething, name='foo') is the following:
adapter = registry.resolve('something', context, name='foo') implementation = adapter(context)
The repoze.component equivalent of implementation = getMultiAdapter((context1, context2), ISomething, name='foo') is the following:
adapter = registry.resolve('something', context1, context2, name='foo') implementation = adapter(context1, context2)
Likewise, the repoze.component equivalent of implementation = getMultiAdapter((context1, context2, context3), ISomething, name='foo') is the following:
adapter = registry.resolve('something', context1, context2, context3, name='foo') implementation = adapter(context1, context2, context3)