[Zope3-checkins] SVN: Zope3/trunk/src/zope/component/ Added a
simplified API for registering global subscription adapters.
Jim Fulton
jim at zope.com
Wed Mar 16 08:19:20 EST 2005
Log message for revision 29491:
Added a simplified API for registering global subscription adapters.
Changed:
U Zope3/trunk/src/zope/component/README.txt
U Zope3/trunk/src/zope/component/__init__.py
U Zope3/trunk/src/zope/component/interfaces.py
-=-
Modified: Zope3/trunk/src/zope/component/README.txt
===================================================================
--- Zope3/trunk/src/zope/component/README.txt 2005-03-16 12:42:54 UTC (rev 29490)
+++ Zope3/trunk/src/zope/component/README.txt 2005-03-16 13:19:20 UTC (rev 29491)
@@ -12,14 +12,14 @@
looked up by an interface and a name. Let's look at a trivial utility
definition:
- >>> import zope.interface
+ >>> from zope import interface
- >>> class IGreeter(zope.interface.Interface):
+ >>> class IGreeter(interface.Interface):
... def greet():
... "say hello"
>>> class Greeter:
- ... zope.interface.implements(IGreeter)
+ ... interface.implements(IGreeter)
...
... def __init__(self, other="world"):
... self.other = other
@@ -29,26 +29,26 @@
We can register an instance this class using `provideUtility` [1]_:
- >>> import zope.component
+ >>> from zope import component
>>> greet = Greeter('bob')
- >>> zope.component.provideUtility(greet, IGreeter, 'robert')
+ >>> component.provideUtility(greet, IGreeter, 'robert')
In this example we registered the utility as providing the `IGreeter`
interface with a name of 'bob'. We can look the interface up with
either `queryUtility` or `getUtility`:
- >>> zope.component.queryUtility(IGreeter, 'robert').greet()
+ >>> component.queryUtility(IGreeter, 'robert').greet()
Hello bob
- >>> zope.component.getUtility(IGreeter, 'robert').greet()
+ >>> component.getUtility(IGreeter, 'robert').greet()
Hello bob
`queryUtility` and `getUtility` differ in how failed lookups are handled:
- >>> zope.component.queryUtility(IGreeter, 'ted')
- >>> zope.component.queryUtility(IGreeter, 'ted', 42)
+ >>> component.queryUtility(IGreeter, 'ted')
+ >>> component.queryUtility(IGreeter, 'ted', 42)
42
- >>> zope.component.getUtility(IGreeter, 'ted')
+ >>> component.getUtility(IGreeter, 'ted')
... # doctest: +ELLIPSIS
Traceback (most recent call last):
...
@@ -58,15 +58,15 @@
then we can omit the provided interface from the call to `provideUtility`:
>>> ted = Greeter('ted')
- >>> zope.component.provideUtility(ted, name='ted')
- >>> zope.component.queryUtility(IGreeter, 'ted').greet()
+ >>> component.provideUtility(ted, name='ted')
+ >>> component.queryUtility(IGreeter, 'ted').greet()
Hello ted
The name defaults to an empty string:
>>> world = Greeter()
- >>> zope.component.provideUtility(world)
- >>> zope.component.queryUtility(IGreeter).greet()
+ >>> component.provideUtility(world)
+ >>> component.queryUtility(IGreeter).greet()
Hello world
Adapters
@@ -78,13 +78,13 @@
create a greeter for persons, so we can provide personalized greetings
for different people:
- >>> class IPerson(zope.interface.Interface):
- ... name = zope.interface.Attribute("Name")
+ >>> class IPerson(interface.Interface):
+ ... name = interface.Attribute("Name")
>>> class PersonGreeter:
...
- ... zope.component.adapts(IPerson)
- ... zope.interface.implements(IGreeter)
+ ... component.adapts(IPerson)
+ ... interface.implements(IGreeter)
...
... def __init__(self, person):
... self.person = person
@@ -95,15 +95,15 @@
The class defines a constructor that takes an argument for every
object adapted.
-We used `zope.component.adapts` to declare what we adapt. We can find
+We used `component.adapts` to declare what we adapt. We can find
out if an object declares that it adapts anything using adaptedBy:
- >>> list(zope.component.adaptedBy(PersonGreeter)) == [IPerson]
+ >>> list(component.adaptedBy(PersonGreeter)) == [IPerson]
True
If an object makes no declaration, then None is returned:
- >>> zope.component.adaptedBy(Greeter()) is None
+ >>> component.adaptedBy(Greeter()) is None
True
@@ -111,14 +111,14 @@
interface, as in the example above, then we can provide the adapter
very simply [1]_:
- >>> zope.component.provideAdapter(PersonGreeter)
+ >>> component.provideAdapter(PersonGreeter)
For adapters that adapt a single interface to a single interface
without a name, we can get the adapter by simply calling the
interface:
>>> class Person:
- ... zope.interface.implements(IPerson)
+ ... interface.implements(IPerson)
...
... def __init__(self, name):
... self.name = name
@@ -134,7 +134,7 @@
... def greet(self):
... print "Hello", self.person.name, "my name is", self.name
- >>> zope.component.provideAdapter(
+ >>> component.provideAdapter(
... BobPersonGreeter, [IPerson], IGreeter, 'bob')
The arguments can also be provided as keyword arguments:
@@ -142,25 +142,25 @@
>>> class TedPersonGreeter(BobPersonGreeter):
... name = "Ted"
- >>> zope.component.provideAdapter(
+ >>> component.provideAdapter(
... factory=TedPersonGreeter, adapts=[IPerson],
... provides=IGreeter, name='ted')
For named adapters, use `queryAdapter`, or `getAdapter`:
- >>> zope.component.queryAdapter(Person("Sally"), IGreeter, 'bob').greet()
+ >>> component.queryAdapter(Person("Sally"), IGreeter, 'bob').greet()
Hello Sally my name is Bob
- >>> zope.component.getAdapter(Person("Sally"), IGreeter, 'ted').greet()
+ >>> component.getAdapter(Person("Sally"), IGreeter, 'ted').greet()
Hello Sally my name is Ted
If an adapter can't be found, `queryAdapter` returns a default value
and `getAdapter` raises an error:
- >>> zope.component.queryAdapter(Person("Sally"), IGreeter, 'frank')
- >>> zope.component.queryAdapter(Person("Sally"), IGreeter, 'frank', 42)
+ >>> component.queryAdapter(Person("Sally"), IGreeter, 'frank')
+ >>> component.queryAdapter(Person("Sally"), IGreeter, 'frank', 42)
42
- >>> zope.component.getAdapter(Person("Sally"), IGreeter, 'frank')
+ >>> component.getAdapter(Person("Sally"), IGreeter, 'frank')
... # doctest: +ELLIPSIS
Traceback (most recent call last):
...
@@ -170,8 +170,8 @@
>>> class TwoPersonGreeter:
...
- ... zope.component.adapts(IPerson, IPerson)
- ... zope.interface.implements(IGreeter)
+ ... component.adapts(IPerson, IPerson)
+ ... interface.implements(IGreeter)
...
... def __init__(self, person, greeter):
... self.person = person
@@ -181,12 +181,12 @@
... print "Hello", self.person.name
... print "my name is", self.greeter.name
- >>> zope.component.provideAdapter(TwoPersonGreeter)
+ >>> component.provideAdapter(TwoPersonGreeter)
To look up a multi-adapter, use either `queryMultiAdapter` or
`getMultiAdapter`:
- >>> zope.component.queryMultiAdapter((Person("Sally"), Person("Bob")),
+ >>> component.queryMultiAdapter((Person("Sally"), Person("Bob")),
... IGreeter).greet()
Hello Sally
my name is Bob
@@ -195,21 +195,21 @@
adapter decorator (in the Python 2.4 decorator sense) to declare that
a callable object adapts some interfaces (or classes):
- >>> class IJob(zope.interface.Interface):
+ >>> class IJob(interface.Interface):
... "A job"
>>> class Job:
- ... zope.interface.implements(IJob)
+ ... interface.implements(IJob)
>>> def personJob(person):
... return getattr(person, 'job', None)
- >>> personJob = zope.interface.implementer(IJob)(personJob)
- >>> personJob = zope.component.adapter(IPerson)(personJob)
+ >>> personJob = interface.implementer(IJob)(personJob)
+ >>> personJob = component.adapter(IPerson)(personJob)
(In Python 2.4, the example can be written::
- @zope.interface.implementer(IJob)
- @zope.component.adapter(IPerson)
+ @interface.implementer(IJob)
+ @component.adapter(IPerson)
def personJob(person):
return getattr(person, 'job', None)
@@ -220,7 +220,7 @@
factory can return None to indicate that adaptation wasn't possible.
Let's register this adapter and try it out:
- >>> zope.component.provideAdapter(personJob)
+ >>> component.provideAdapter(personJob)
>>> sally = Person("Sally")
>>> IJob(sally) # doctest: +ELLIPSIS
Traceback (most recent call last):
@@ -234,8 +234,95 @@
>>> sally.job = job
>>> IJob(sally) is job
True
-
+Subscription Adapters
+*********************
+
+Unlike regular adapters, subscription adapters are used when we want
+all of the adapters that adapt an object to a particular adapter.
+
+Consider a validation problem. We have objects and we want to assess
+whether they meet some sort of standards. We define a validation
+interface:
+
+ >>> class IValidate(interface.Interface):
+ ... def validate(ob):
+ ... """Determine whether the object is valid
+ ...
+ ... Return a string describing a validation problem.
+ ... An empty string is returned to indicate that the
+ ... object is valid.
+ ... """
+
+Perhaps we have documents:
+
+ >>> class IDocument(interface.Interface):
+ ... summary = interface.Attribute("Document summary")
+ ... body = interface.Attribute("Document text")
+
+ >>> class Document:
+ ... interface.implements(IDocument)
+ ... def __init__(self, summary, body):
+ ... self.summary, self.body = summary, body
+
+Now, we may want to specify various validation rules for
+documents. For example, we might require that the summary be a single
+line:
+
+ >>> class SingleLineSummary:
+ ... component.adapts(IDocument)
+ ... interface.implements(IValidate)
+ ...
+ ... def __init__(self, doc):
+ ... self.doc = doc
+ ...
+ ... def validate(self):
+ ... if '\n' in self.doc.summary:
+ ... return 'Summary should only have one line'
+ ... else:
+ ... return ''
+
+Or we might require the body to be at least 1000 characters in length:
+
+ >>> class AdequateLength:
+ ... component.adapts(IDocument)
+ ... interface.implements(IValidate)
+ ...
+ ... def __init__(self, doc):
+ ... self.doc = doc
+ ...
+ ... def validate(self):
+ ... if len(self.doc.body) < 1000:
+ ... return 'too short'
+ ... else:
+ ... return ''
+
+We can register these as subscription adapters:
+
+ >>> component.provideSubscriptionAdapter(SingleLineSummary)
+ >>> component.provideSubscriptionAdapter(AdequateLength)
+
+We can then use the subscribers to validate objects:
+
+ >>> doc = Document("A\nDocument", "blah")
+ >>> [adapter.validate()
+ ... for adapter in component.subscribers([doc], IValidate)
+ ... if adapter.validate()]
+ ['Summary should only have one line', 'too short']
+
+ >>> doc = Document("A\nDocument", "blah" * 1000)
+ >>> [adapter.validate()
+ ... for adapter in component.subscribers([doc], IValidate)
+ ... if adapter.validate()]
+ ['Summary should only have one line']
+
+ >>> doc = Document("A Document", "blah")
+ >>> [adapter.validate()
+ ... for adapter in component.subscribers([doc], IValidate)
+ ... if adapter.validate()]
+ ['too short']
+
+
.. [1] CAUTION: This API should only be used from test or
application-setup code. This API shouldn't be used by regular
library modules, as component registration is a configuration
Modified: Zope3/trunk/src/zope/component/__init__.py
===================================================================
--- Zope3/trunk/src/zope/component/__init__.py 2005-03-16 12:42:54 UTC (rev 29490)
+++ Zope3/trunk/src/zope/component/__init__.py 2005-03-16 13:19:20 UTC (rev 29491)
@@ -327,3 +327,22 @@
raise TypeError("Missing 'adapts' argument")
getGlobalSiteManager().provideAdapter(adapts, provides, name, factory)
+
+def provideSubscriptionAdapter(factory, adapts=None, provides=None):
+ if provides is None:
+ if IFactory.providedBy(factory):
+ provides = factory.getInterfaces()
+ else:
+ provides = list(implementedBy(factory))
+ if len(provides) == 1:
+ provides = provides[0]
+ else:
+ raise TypeError("Missing 'provides' argument")
+
+ if adapts is None:
+ try:
+ adapts = factory.__component_adapts__
+ except AttributeError:
+ raise TypeError("Missing 'adapts' argument")
+
+ getGlobalSiteManager().subscribe(adapts, provides, factory)
Modified: Zope3/trunk/src/zope/component/interfaces.py
===================================================================
--- Zope3/trunk/src/zope/component/interfaces.py 2005-03-16 12:42:54 UTC (rev 29490)
+++ Zope3/trunk/src/zope/component/interfaces.py 2005-03-16 13:19:20 UTC (rev 29491)
@@ -377,6 +377,26 @@
activity.
"""
+ def provideSubscriptionAdapter(factory, adapts=None, provides=None):
+ """Register an adapter globally
+
+ An adapter is registered to provide an interface with a name
+ for some number of object types. If a factory implements only
+ one interface, then the provides argument can be omitted and
+ the provided interface will be used. (In this case, a provides
+ argument can still be provided to provide a less specific
+ interface.)
+
+ If the factory has an adapts declaration, then the adapts
+ argument can be omitted and the declaration will be used. (An
+ adapts argument can be provided to override the declaration.)
+
+ CAUTION: This API should only be used from test or
+ application-setup code. This api shouldn't be used by regular
+ library modules, as component registration is a configuration
+ activity.
+ """
+
class IRegistry(Interface):
"""Object that supports component registry
"""
More information about the Zope3-Checkins
mailing list