[Zope3-checkins]
SVN: Zope3/trunk/src/zope/component/socketexample.txt
Converted DOS EOL endings back to Unix.
Stephan Richter
srichter at cosmos.phy.tufts.edu
Wed Feb 23 17:17:22 EST 2005
Log message for revision 29268:
Converted DOS EOL endings back to Unix.
Changed:
U Zope3/trunk/src/zope/component/socketexample.txt
-=-
Modified: Zope3/trunk/src/zope/component/socketexample.txt
===================================================================
--- Zope3/trunk/src/zope/component/socketexample.txt 2005-02-23 21:56:07 UTC (rev 29267)
+++ Zope3/trunk/src/zope/component/socketexample.txt 2005-02-23 22:17:22 UTC (rev 29268)
@@ -1,597 +1,597 @@
-==================================================
-The Zope 3 Component Architecture (Socket Example)
-==================================================
-
-The component architecture provides an application framework that provides its
-functionality through loosly-connected components. A *component* can be any
-Python object and has a particular purpose associated with it. Thus, in a
-component-based applications you have many small component in contrast to
-classical object-oriented development, where yoiu have a few big objects.
-
-Components communicate via specific APIs, which are formally defined by
-interfaces, which are provided by the `zope.interface` package. *Interfaces*
-describe the methods and properties that a component is expected to
-provide. They are also used as a primary mean to provide developer-level
-documentation for the components. For more details about interfaces see
-`zope/interface/README.txt`.
-
-The two main types of components are *adapters* and *utilities*. They will be
-discussed in detail later in this document. Both component types are managed
-by the *site manager*, with which you can register and access these
-components. However, most of the site manager's functionality is hidden behind
-the component architecture's public API, which is documented in
-`IComponentArchitecture`.
-
-
-Adapters
---------
-
-Adapters are a well-established pattern. An *adapter* uses an object providing
-one interface to produce an object that provides another interface. Here an
-example: Imagine that you purchased an electric shaver in the US, and thus
-you require the US socket type. You are now traveling in Germany, where another
-socket style is used. You will need a device, an adapter, that converts from
-the German to the US socket style.
-
-The functionality of adapters is actually natively provided by the
-`zope.interface` package and is thus well decumented there. The `human.txt`
-file provides a gentle introduction to adapters, whereby `adapter.txt` is
-aimed at providing a comprehensive insight into adapters, but is too abstract
-for many as an inital read. Thus, we will only explain adapters in the context
-of the component architecture's API.
-
-So let's say that we have a German socket
-
- >>> from zope.interface import Interface, implements
-
- >>> class IGermanSocket(Interface):
- ... pass
-
- >>> class Socket(object):
- ... def __repr__(self):
- ... return '<instance of %s>' %self.__class__.__name__
-
- >>> class GermanSocket(Socket):
- ... """German wall socket."""
- ... implements(IGermanSocket)
-
-and we want to convert it to an US socket
-
- >>> class IUSSocket(Interface):
- ... pass
-
-so that our shaver can be used in Germany. So we go to a German electronics
-store to look for an adapter that we can plug in the wall:
-
- >>> class GermanToUSSocketAdapter(Socket):
- ... implements(IUSSocket)
- ... __used_by__ = IGermanSocket
- ...
- ... def __init__(self, socket):
- ... self.context = socket
-
-Note that I could have called the passed in socket any way I like, but
-`context` is the standard name accepted.
-
-
-Single Adapters
-+++++++++++++++
-
-Before we can use the adapter, we have to buy it and make it part of our
-inventory. In the component architecture we do this by registering the adapter
-with the framework, more specifically with the global site manager:
-
- >>> from zope import component as capi
- >>> gsm = capi.getGlobalSiteManager()
-
- >>> gsm.provideAdapter((IGermanSocket,), IUSSocket, '',
- ... GermanToUSSocketAdapter)
-
-`capi` is the component architecture API that is being presented by this
-file. You registered an adapter from `IGermanSocket` to `IUSSocket` having no
-name (thus the empty string).
-
-Anyways, you finally get back to your hotel room and shave, since you have not
-been able to shave in the plane. In the bathroom you discover a socket:
-
- >>> bathroomDE = GermanSocket()
- >>> IGermanSocket.providedBy(bathroomDE)
- True
-
-You now insert the adapter in the German socket
-
- >>> bathroomUS = capi.getAdapter(bathroomDE, IUSSocket, '')
-
-so that the socket now provides the US version:
-
- >>> IUSSocket.providedBy(bathroomUS)
- True
-
-Now you can insert your shaver and get on with your day.
-
-After a week you travel for a couple of days to the Prague and you notice that
-the Czech have yet another socket type:
-
- >>> class ICzechSocket(Interface):
- ... pass
-
- >>> class CzechSocket(Socket):
- ... implements(ICzechSocket)
-
- >>> czech = CzechSocket()
-
-You try to find an adapter for your shaver in your bag, but you fail, since
-you do not have one:
-
- >>> capi.getAdapter(czech, IUSSocket, '') #doctest: +NORMALIZE_WHITESPACE
- Traceback (most recent call last):
- ...
- ComponentLookupError: (<instance of CzechSocket>,
- <InterfaceClass __builtin__.IUSSocket>,
- '')
-
-or the more graceful way:
-
- >>> marker = object()
- >>> socket = capi.queryAdapter(czech, IUSSocket, '', marker)
- >>> socket is marker
- True
-
-In the component architecture API any `get*` method will fail with a specific
-exception, if a query failed, whereby methods starting with `query*` will
-always return a `default` value after a failure.
-
-
-Named Adapters
-++++++++++++++
-
-You are finally back in Germany. You also brought your DVD player and a couple
-DVDs with you, which you would like to watch. Your shaver was able to convert
-automatically from 110 volts to 240 volts, but your DVD player cannot. So you
-have to buy another adapter that also handles converting the voltage and the
-frequency of the AC current:
-
- >>> class GermanToUSSocketAdapterAndTransformer(object):
- ... implements(IUSSocket)
- ... __used_by__ = IGermanSocket
- ...
- ... def __init__(self, socket):
- ... self.context = socket
-
-Now, we need a way to keep the two adapters apart. Thus we register them with
-a name:
-
- >>> gsm.provideAdapter((IGermanSocket,), IUSSocket, 'shaver',
- ... GermanToUSSocketAdapter)
- >>> gsm.provideAdapter((IGermanSocket,), IUSSocket, 'dvd',
- ... GermanToUSSocketAdapterAndTransformer)
-
-Now we simply look up the adapters using their labels (called *name*):
-
- >>> socket = capi.getAdapter(bathroomDE, IUSSocket, 'shaver')
- >>> socket.__class__ is GermanToUSSocketAdapter
- True
-
- >>> socket = capi.getAdapter(bathroomDE, IUSSocket, 'dvd')
- >>> socket.__class__ is GermanToUSSocketAdapterAndTransformer
- True
-
-Clearly, we do not have an adapter for the MP3 player
-
- >>> capi.getAdapter(bathroomDE, IUSSocket,
- ... 'mp3') #doctest: +NORMALIZE_WHITESPACE
- Traceback (most recent call last):
- ...
- ComponentLookupError: (<instance of GermanSocket>,
- <InterfaceClass __builtin__.IUSSocket>,
- 'mp3')
-
-
-but you could use the 'dvd' adapter in this case of course. ;)
-
-Sometimes you want to know all adapters that are available. Let's say you want
-to know about all the adapters that convert a German to a US socket type:
-
- >>> sockets = capi.getAdapters((bathroomDE,), IUSSocket)
- >>> len(sockets)
- 3
- >>> names = [name for name, socket in sockets]
- >>> names.sort()
- >>> names
- [u'', u'dvd', u'shaver']
-
-`capi.getAdapters()` returns a list of tuples. The first entry of the tuple is
-the name of the adapter and the second is the adapter itself.
-
-
-Multi-Adapters
-++++++++++++++
-
-After watching all the DVDs you brought at least twice, you get tired of them
-and you want to listen to some music using your MP3 player. But darn, the MP3
-player plug has a ground pin and all the adapters you have do not support
-that:
-
- >>> class IUSGroundedSocket(IUSSocket):
- ... pass
-
-So you go out another time to buy an adapter. This time, however, you do not
-buy yet another adapter, but a piece that provides the grounding plug:
-
- >>> class IGrounder(Interface):
- ... pass
-
- >>> class Grounder(object):
- ... implements(IGrounder)
- ... def __repr__(self):
- ... return '<instance of Grounder>'
-
-
-Then together they will provided a grounded us socket:
-
- >>> class GroundedGermanToUSSocketAdapter(object):
- ... implements(IUSGroundedSocket)
- ... __used_for__ = (IGermanSocket, IGrounder)
- ... def __init__(self, socket, grounder):
- ... self.socket, self.grounder = socket, grounder
-
-You now register the combination, so that you know you can create a
-`IUSGroundedSocket`:
-
- >>> gsm.provideAdapter((IGermanSocket, IGrounder), IUSGroundedSocket, 'mp3',
- ... GroundedGermanToUSSocketAdapter)
-
-Given the grounder
-
- >>> grounder = Grounder()
-
-and a German socket
-
- >>> livingroom = GermanSocket()
-
-we can now get a gounded US socket:
-
- >>> socket = capi.getMultiAdapter((livingroom, grounder),
- ... IUSGroundedSocket, 'mp3')
-
- >>> socket.__class__ is GroundedGermanToUSSocketAdapter
- True
- >>> socket.socket is livingroom
- True
- >>> socket.grounder is grounder
- True
-
-Of course, you do not have a 'dvd' grounded US socket available:
-
- >>> capi.getMultiAdapter((livingroom, grounder), IUSGroundedSocket,
- ... 'dvd') #doctest: +NORMALIZE_WHITESPACE
- Traceback (most recent call last):
- ...
- ComponentLookupError: ((<instance of GermanSocket>,
- <instance of Grounder>),
- <InterfaceClass __builtin__.IUSGroundedSocket>,
- 'dvd')
-
-
- >>> socket = capi.queryMultiAdapter((livingroom, grounder),
- ... IUSGroundedSocket, 'dvd', marker)
- >>> socket is marker
- True
-
-Again, you might want to read `adapter.txt` in `zope.interface` for a more
-comprehensive coverage of multi-adapters.
-
-Subscribers
------------
-
-While subscribers are directly supported by the adapter registry and are
-adapters for all theoretical purposes, practically it might be better to think
-of them as separate components. Subscribers are particularly useful for
-events.
-
-Let's say one of our adapters overheated and caused a small fire:
-
- >>> class IFire(Interface):
- ... pass
-
- >>> class Fire(object):
- ... implements(IFire)
-
- >>> fire = Fire()
-
-We want to use all available objects to put out the fire:
-
- >>> class IFireExtinguisher(Interface):
- ... def extinguish():
- ... pass
-
- >>> class FireExtinguisher(object):
- ... def __init__(self, fire):
- ... pass
- ... def extinguish(self):
- ... "Place extinguish code here."
- ... print 'Used ' + self.__class__.__name__ + '.'
-
-Here some specific methods to put out the fire:
-
- >>> class PowderExtinguisher(FireExtinguisher):
- ... pass
- >>> gsm.subscribe((IFire,), IFireExtinguisher, PowderExtinguisher)
-
- >>> class Blanket(FireExtinguisher):
- ... pass
- >>> gsm.subscribe((IFire,), IFireExtinguisher, Blanket)
-
- >>> class SprinklerSystem(FireExtinguisher):
- ... pass
- >>> gsm.subscribe((IFire,), IFireExtinguisher, SprinklerSystem)
-
-Now let use all these things to put out the fire:
-
- >>> extinguishers = capi.subscribers((fire,), IFireExtinguisher)
- >>> extinguishers.sort()
- >>> for extinguisher in extinguishers:
- ... extinguisher.extinguish()
- Used Blanket.
- Used PowderExtinguisher.
- Used SprinklerSystem.
-
-If no subscribers are found for a particular object, then an empty list is
-returned:
-
- >>> capi.subscribers((object(),), IFireExtinguisher)
- []
-
-
-Utilities
----------
-
-Utilities are the second type of component, the component architecture
-implements. *Utilities* are simply components that provide an interface. When
-you register an utility, you always register an instance (in cotrast to a
-factory for adapters) since the initialization and setup process of a utility
-might be complex and is not well defined. In some ways a utility is much more
-fundamental than an adapter, because an adapter cannot be used without another
-component, but a utility is always self-contained. I like to think of
-utilities as the foundation of your application and adapters as components
-extending beyond this foundation.
-
-Back to our story...
-
-After your vacation is over you fly back home to Tampa, Florida. But it is
-August now, the middle of the Hurrican season. And, believe it or not, you are
-worried that you will not be able to shave when the power goes out for several
-days. (You just hate wet shavers.)
-
-So you decide to go to your favorite hardware store and by a Diesel-powered
-electric generator. The generator provides of course a US-style socket:
-
- >>> class Generator(object):
- ... implements(IUSSocket)
- ... def __repr__(self):
- ... return '<instance of Generator>'
-
- >>> generator = Generator()
-
-Like for adapters, we now have to add the newly-acquired generator to our
-inventory by registering it as a utility:
-
- >>> gsm.provideUtility(IUSSocket, generator)
-
-We can now get the utility using
-
- >>> utility = capi.getUtility(IUSSocket)
- >>> utility is generator
- True
-
-As you can see, it is very simple to register and retrieve utilities. If a
-utility does not exsist for a particular interface, such as the German socket,
-then the lookup fails
-
- >>> capi.getUtility(IGermanSocket)
- Traceback (most recent call last):
- ...
- ComponentLookupError: (<InterfaceClass __builtin__.IGermanSocket>, '')
-
-or more gracefully when specifying a default value:
-
- >>> default = object()
- >>> utility = capi.queryUtility(IGermanSocket, default=default)
- >>> utility is default
- True
-
-Note: The only difference between `getUtility()` and `queryUtility()` is the
-fact that you can specify a default value for the latter function, so that it
-will never cause a `ComponentLookupError`.
-
-
-Named Utilities
-+++++++++++++++
-
-It is often desirable to have several utilities providing the same interface
-per site. This way you can implement any sort of registry using utilities. For
-this reason, utilities -- like adapters -- can be named.
-
-In the context of our story, we might want to do the following: You really do
-not trust gas stations either. What if the roads are blocked after a hurricane
-and the gas stations run out of oil. So you look for another renewable power
-source. Then you think about solar panels! After a storm there is usually very
-nice weather, so why not? Via the Web you order a set of 110V/120W solar
-panels that provide a regular US-style socket as output:
-
- >>> class SolarPanel(object):
- ... implements(IUSSocket)
- ... def __repr__(self):
- ... return '<instance of Solar Panel>'
-
- >>> panel = SolarPanel()
-
-Once it arrives, we add it to our inventory:
-
- >>> gsm.provideUtility(IUSSocket, panel, 'Solar Panel')
-
-You can now access the solar panel using
-
- >>> utility = capi.getUtility(IUSSocket, 'Solar Panel')
- >>> utility is panel
- True
-
-Of course, if a utility is not available, then the lookup will simply fail
-
- >>> capi.getUtility(IUSSocket, 'Wind Mill')
- Traceback (most recent call last):
- ...
- ComponentLookupError: (<InterfaceClass __builtin__.IUSSocket>, 'Wind Mill')
-
-or more gracefully when specifying a default value:
-
- >>> default = object()
- >>> utility = capi.queryUtility(IUSSocket, 'Wind Mill', default=default)
- >>> utility is default
- True
-
-Now you want to look at all the utilities you have for a particular kind. The
-following API function will return a list of name/utility pairs:
-
- >>> utils = list(capi.getUtilitiesFor(IUSSocket))
- >>> utils.sort()
- >>> utils #doctest: +NORMALIZE_WHITESPACE
- [(u'', <instance of Generator>),
- (u'Solar Panel', <instance of Solar Panel>)]
-
-Another method of looking up all utilities is by using
-`getAllUtilitiesRegisteredFor(iface)`. This function will return an iteratable
-of utilities (without names); however, it will also return overridden
-utilities. If you are not using multiple site managers, you will not actually
-need this method.
-
- >>> utils = list(capi.getAllUtilitiesRegisteredFor(IUSSocket))
- >>> utils.sort()
- >>> utils
- [<instance of Generator>, <instance of Solar Panel>]
-
-
-Factories
-+++++++++
-
-A *factory* is a special kind of utility that exists to create other
-components. A factory is always identified by a name. It also provides a title
-and description and is able to tell the developer what interfaces the created
-object will provide. The advantage of using a factory to create an object
-instead of directly isntantiating a class or executing any other callable is
-that we can refer to the factory by name. As long as the name stays fixed, the
-implementation of the callable can be renamed or moved without a breakage in
-code.
-
-Let's say that our solar panel comes in parts and they have to be
-assembled. This assembly would be done by a factory, so let's create one for
-the solar panel. To do this, we can use a standard implementation of the
-`IFactory` interface:
-
- >>> from zope.component.factory import Factory
- >>> factory = Factory(SolarPanel,
- ... 'Solar Panel',
- ... 'This factory creates a solar panel.')
-
-Optionally, I could have also specifed the interfaces that the created object
-will provide, but the factory class is smart enough to determine the
-implemented interface from the class. We now register the factory:
-
- >>> from zope.component.interfaces import IFactory
- >>> gsm.provideUtility(IFactory, factory, 'SolarPanel')
-
-We can now get a list of interfaces the produced object will provide:
-
- >>> ifaces = capi.getFactoryInterfaces('SolarPanel')
- >>> IUSSocket in ifaces
- True
-
-By the way, this is equivalent to
-
- >>> ifaces2 = factory.getInterfaces()
- >>> ifaces is ifaces2
- True
-
-Of course you can also just create an object:
-
- >>> panel = capi.createObject('SolarPanel')
- >>> panel.__class__ is SolarPanel
- True
-
-Note: Ignore the first argument (`None`) for now; it is the context of the
-utility lookup, which is usually an optional argument, but cannot be in this
-case, since all other arguments beside the `name` are passed in as arguments
-to the specified callable.
-
-Once you register several factories
-
- >>> gsm.provideUtility(IFactory, Factory(Generator), 'Generator')
-
-you can also determine, which available factories will create objects
-providing a certian interface:
-
- >>> factories = capi.getFactoriesFor(IUSSocket)
- >>> factories = [(name, factory.__class__) for name, factory in factories]
- >>> factories.sort()
- >>> factories #doctest: +NORMALIZE_WHITESPACE
- [(u'Generator', <class 'zope.component.factory.Factory'>),
- (u'SolarPanel', <class 'zope.component.factory.Factory'>)]
-
-
-Site Managers
--------------
-
-Why do we need site managers? Why is the component architecture API not
-sufficient? Some applications, including Zope 3, have a concept of
-locations. It is often desireable to have different configurations for these
-location; this can be done by overwriting existing or adding new component
-registrations. Site managers in locations below the root location, should be
-able to delegate requests to their parent locations. The root site manager is
-commonly known as *global site manager*, since it is always available. You can
-always get the global site manager using the API:
-
- >>> gsm = capi.getGlobalSiteManager()
-
- >>> from zope.component.site import globalSiteManager
- >>> gsm is globalSiteManager
- True
- >>> from zope.component.interfaces import ISiteManager
- >>> ISiteManager.providedBy(gsm)
- True
- >>> from zope.component.site import IGlobalSiteManager
- >>> IGlobalSiteManager.providedBy(gsm)
- True
-
-You can also lookup at site manager in a given context. The only requirement
-is that the context can be adapted to a site manager. So let's create a
-special site manager:
-
- >>> from zope.component.site import SiteManager
- >>> sm = SiteManager()
-
-Now we create a context that adapts to the site manager via the `__conform__`
-method as specied in PEP 246.
-
- >>> class Context(object):
- ... def __init__(self, sm):
- ... self.sm = sm
- ... def __conform__(self, interface):
- ... if interface.isOrExtends(ISiteManager):
- ... return self.sm
-
-We now instantiate the `Context` with our special site manager:
-
- >>> context = Context(sm)
- >>> context.sm is sm
- True
-
-We can now ask for the site manager of this context:
-
- >>> lsm = capi.getSiteManager(context)
- >>> lsm is sm
- True
-
-The site manager instance `lsm` is formally known as a *local site manager* of
-`context`.
-
-
+==================================================
+The Zope 3 Component Architecture (Socket Example)
+==================================================
+
+The component architecture provides an application framework that provides its
+functionality through loosly-connected components. A *component* can be any
+Python object and has a particular purpose associated with it. Thus, in a
+component-based applications you have many small component in contrast to
+classical object-oriented development, where yoiu have a few big objects.
+
+Components communicate via specific APIs, which are formally defined by
+interfaces, which are provided by the `zope.interface` package. *Interfaces*
+describe the methods and properties that a component is expected to
+provide. They are also used as a primary mean to provide developer-level
+documentation for the components. For more details about interfaces see
+`zope/interface/README.txt`.
+
+The two main types of components are *adapters* and *utilities*. They will be
+discussed in detail later in this document. Both component types are managed
+by the *site manager*, with which you can register and access these
+components. However, most of the site manager's functionality is hidden behind
+the component architecture's public API, which is documented in
+`IComponentArchitecture`.
+
+
+Adapters
+--------
+
+Adapters are a well-established pattern. An *adapter* uses an object providing
+one interface to produce an object that provides another interface. Here an
+example: Imagine that you purchased an electric shaver in the US, and thus
+you require the US socket type. You are now traveling in Germany, where another
+socket style is used. You will need a device, an adapter, that converts from
+the German to the US socket style.
+
+The functionality of adapters is actually natively provided by the
+`zope.interface` package and is thus well decumented there. The `human.txt`
+file provides a gentle introduction to adapters, whereby `adapter.txt` is
+aimed at providing a comprehensive insight into adapters, but is too abstract
+for many as an inital read. Thus, we will only explain adapters in the context
+of the component architecture's API.
+
+So let's say that we have a German socket
+
+ >>> from zope.interface import Interface, implements
+
+ >>> class IGermanSocket(Interface):
+ ... pass
+
+ >>> class Socket(object):
+ ... def __repr__(self):
+ ... return '<instance of %s>' %self.__class__.__name__
+
+ >>> class GermanSocket(Socket):
+ ... """German wall socket."""
+ ... implements(IGermanSocket)
+
+and we want to convert it to an US socket
+
+ >>> class IUSSocket(Interface):
+ ... pass
+
+so that our shaver can be used in Germany. So we go to a German electronics
+store to look for an adapter that we can plug in the wall:
+
+ >>> class GermanToUSSocketAdapter(Socket):
+ ... implements(IUSSocket)
+ ... __used_by__ = IGermanSocket
+ ...
+ ... def __init__(self, socket):
+ ... self.context = socket
+
+Note that I could have called the passed in socket any way I like, but
+`context` is the standard name accepted.
+
+
+Single Adapters
++++++++++++++++
+
+Before we can use the adapter, we have to buy it and make it part of our
+inventory. In the component architecture we do this by registering the adapter
+with the framework, more specifically with the global site manager:
+
+ >>> from zope import component as capi
+ >>> gsm = capi.getGlobalSiteManager()
+
+ >>> gsm.provideAdapter((IGermanSocket,), IUSSocket, '',
+ ... GermanToUSSocketAdapter)
+
+`capi` is the component architecture API that is being presented by this
+file. You registered an adapter from `IGermanSocket` to `IUSSocket` having no
+name (thus the empty string).
+
+Anyways, you finally get back to your hotel room and shave, since you have not
+been able to shave in the plane. In the bathroom you discover a socket:
+
+ >>> bathroomDE = GermanSocket()
+ >>> IGermanSocket.providedBy(bathroomDE)
+ True
+
+You now insert the adapter in the German socket
+
+ >>> bathroomUS = capi.getAdapter(bathroomDE, IUSSocket, '')
+
+so that the socket now provides the US version:
+
+ >>> IUSSocket.providedBy(bathroomUS)
+ True
+
+Now you can insert your shaver and get on with your day.
+
+After a week you travel for a couple of days to the Prague and you notice that
+the Czech have yet another socket type:
+
+ >>> class ICzechSocket(Interface):
+ ... pass
+
+ >>> class CzechSocket(Socket):
+ ... implements(ICzechSocket)
+
+ >>> czech = CzechSocket()
+
+You try to find an adapter for your shaver in your bag, but you fail, since
+you do not have one:
+
+ >>> capi.getAdapter(czech, IUSSocket, '') #doctest: +NORMALIZE_WHITESPACE
+ Traceback (most recent call last):
+ ...
+ ComponentLookupError: (<instance of CzechSocket>,
+ <InterfaceClass __builtin__.IUSSocket>,
+ '')
+
+or the more graceful way:
+
+ >>> marker = object()
+ >>> socket = capi.queryAdapter(czech, IUSSocket, '', marker)
+ >>> socket is marker
+ True
+
+In the component architecture API any `get*` method will fail with a specific
+exception, if a query failed, whereby methods starting with `query*` will
+always return a `default` value after a failure.
+
+
+Named Adapters
+++++++++++++++
+
+You are finally back in Germany. You also brought your DVD player and a couple
+DVDs with you, which you would like to watch. Your shaver was able to convert
+automatically from 110 volts to 240 volts, but your DVD player cannot. So you
+have to buy another adapter that also handles converting the voltage and the
+frequency of the AC current:
+
+ >>> class GermanToUSSocketAdapterAndTransformer(object):
+ ... implements(IUSSocket)
+ ... __used_by__ = IGermanSocket
+ ...
+ ... def __init__(self, socket):
+ ... self.context = socket
+
+Now, we need a way to keep the two adapters apart. Thus we register them with
+a name:
+
+ >>> gsm.provideAdapter((IGermanSocket,), IUSSocket, 'shaver',
+ ... GermanToUSSocketAdapter)
+ >>> gsm.provideAdapter((IGermanSocket,), IUSSocket, 'dvd',
+ ... GermanToUSSocketAdapterAndTransformer)
+
+Now we simply look up the adapters using their labels (called *name*):
+
+ >>> socket = capi.getAdapter(bathroomDE, IUSSocket, 'shaver')
+ >>> socket.__class__ is GermanToUSSocketAdapter
+ True
+
+ >>> socket = capi.getAdapter(bathroomDE, IUSSocket, 'dvd')
+ >>> socket.__class__ is GermanToUSSocketAdapterAndTransformer
+ True
+
+Clearly, we do not have an adapter for the MP3 player
+
+ >>> capi.getAdapter(bathroomDE, IUSSocket,
+ ... 'mp3') #doctest: +NORMALIZE_WHITESPACE
+ Traceback (most recent call last):
+ ...
+ ComponentLookupError: (<instance of GermanSocket>,
+ <InterfaceClass __builtin__.IUSSocket>,
+ 'mp3')
+
+
+but you could use the 'dvd' adapter in this case of course. ;)
+
+Sometimes you want to know all adapters that are available. Let's say you want
+to know about all the adapters that convert a German to a US socket type:
+
+ >>> sockets = capi.getAdapters((bathroomDE,), IUSSocket)
+ >>> len(sockets)
+ 3
+ >>> names = [name for name, socket in sockets]
+ >>> names.sort()
+ >>> names
+ [u'', u'dvd', u'shaver']
+
+`capi.getAdapters()` returns a list of tuples. The first entry of the tuple is
+the name of the adapter and the second is the adapter itself.
+
+
+Multi-Adapters
+++++++++++++++
+
+After watching all the DVDs you brought at least twice, you get tired of them
+and you want to listen to some music using your MP3 player. But darn, the MP3
+player plug has a ground pin and all the adapters you have do not support
+that:
+
+ >>> class IUSGroundedSocket(IUSSocket):
+ ... pass
+
+So you go out another time to buy an adapter. This time, however, you do not
+buy yet another adapter, but a piece that provides the grounding plug:
+
+ >>> class IGrounder(Interface):
+ ... pass
+
+ >>> class Grounder(object):
+ ... implements(IGrounder)
+ ... def __repr__(self):
+ ... return '<instance of Grounder>'
+
+
+Then together they will provided a grounded us socket:
+
+ >>> class GroundedGermanToUSSocketAdapter(object):
+ ... implements(IUSGroundedSocket)
+ ... __used_for__ = (IGermanSocket, IGrounder)
+ ... def __init__(self, socket, grounder):
+ ... self.socket, self.grounder = socket, grounder
+
+You now register the combination, so that you know you can create a
+`IUSGroundedSocket`:
+
+ >>> gsm.provideAdapter((IGermanSocket, IGrounder), IUSGroundedSocket, 'mp3',
+ ... GroundedGermanToUSSocketAdapter)
+
+Given the grounder
+
+ >>> grounder = Grounder()
+
+and a German socket
+
+ >>> livingroom = GermanSocket()
+
+we can now get a gounded US socket:
+
+ >>> socket = capi.getMultiAdapter((livingroom, grounder),
+ ... IUSGroundedSocket, 'mp3')
+
+ >>> socket.__class__ is GroundedGermanToUSSocketAdapter
+ True
+ >>> socket.socket is livingroom
+ True
+ >>> socket.grounder is grounder
+ True
+
+Of course, you do not have a 'dvd' grounded US socket available:
+
+ >>> capi.getMultiAdapter((livingroom, grounder), IUSGroundedSocket,
+ ... 'dvd') #doctest: +NORMALIZE_WHITESPACE
+ Traceback (most recent call last):
+ ...
+ ComponentLookupError: ((<instance of GermanSocket>,
+ <instance of Grounder>),
+ <InterfaceClass __builtin__.IUSGroundedSocket>,
+ 'dvd')
+
+
+ >>> socket = capi.queryMultiAdapter((livingroom, grounder),
+ ... IUSGroundedSocket, 'dvd', marker)
+ >>> socket is marker
+ True
+
+Again, you might want to read `adapter.txt` in `zope.interface` for a more
+comprehensive coverage of multi-adapters.
+
+Subscribers
+-----------
+
+While subscribers are directly supported by the adapter registry and are
+adapters for all theoretical purposes, practically it might be better to think
+of them as separate components. Subscribers are particularly useful for
+events.
+
+Let's say one of our adapters overheated and caused a small fire:
+
+ >>> class IFire(Interface):
+ ... pass
+
+ >>> class Fire(object):
+ ... implements(IFire)
+
+ >>> fire = Fire()
+
+We want to use all available objects to put out the fire:
+
+ >>> class IFireExtinguisher(Interface):
+ ... def extinguish():
+ ... pass
+
+ >>> class FireExtinguisher(object):
+ ... def __init__(self, fire):
+ ... pass
+ ... def extinguish(self):
+ ... "Place extinguish code here."
+ ... print 'Used ' + self.__class__.__name__ + '.'
+
+Here some specific methods to put out the fire:
+
+ >>> class PowderExtinguisher(FireExtinguisher):
+ ... pass
+ >>> gsm.subscribe((IFire,), IFireExtinguisher, PowderExtinguisher)
+
+ >>> class Blanket(FireExtinguisher):
+ ... pass
+ >>> gsm.subscribe((IFire,), IFireExtinguisher, Blanket)
+
+ >>> class SprinklerSystem(FireExtinguisher):
+ ... pass
+ >>> gsm.subscribe((IFire,), IFireExtinguisher, SprinklerSystem)
+
+Now let use all these things to put out the fire:
+
+ >>> extinguishers = capi.subscribers((fire,), IFireExtinguisher)
+ >>> extinguishers.sort()
+ >>> for extinguisher in extinguishers:
+ ... extinguisher.extinguish()
+ Used Blanket.
+ Used PowderExtinguisher.
+ Used SprinklerSystem.
+
+If no subscribers are found for a particular object, then an empty list is
+returned:
+
+ >>> capi.subscribers((object(),), IFireExtinguisher)
+ []
+
+
+Utilities
+---------
+
+Utilities are the second type of component, the component architecture
+implements. *Utilities* are simply components that provide an interface. When
+you register an utility, you always register an instance (in cotrast to a
+factory for adapters) since the initialization and setup process of a utility
+might be complex and is not well defined. In some ways a utility is much more
+fundamental than an adapter, because an adapter cannot be used without another
+component, but a utility is always self-contained. I like to think of
+utilities as the foundation of your application and adapters as components
+extending beyond this foundation.
+
+Back to our story...
+
+After your vacation is over you fly back home to Tampa, Florida. But it is
+August now, the middle of the Hurrican season. And, believe it or not, you are
+worried that you will not be able to shave when the power goes out for several
+days. (You just hate wet shavers.)
+
+So you decide to go to your favorite hardware store and by a Diesel-powered
+electric generator. The generator provides of course a US-style socket:
+
+ >>> class Generator(object):
+ ... implements(IUSSocket)
+ ... def __repr__(self):
+ ... return '<instance of Generator>'
+
+ >>> generator = Generator()
+
+Like for adapters, we now have to add the newly-acquired generator to our
+inventory by registering it as a utility:
+
+ >>> gsm.provideUtility(IUSSocket, generator)
+
+We can now get the utility using
+
+ >>> utility = capi.getUtility(IUSSocket)
+ >>> utility is generator
+ True
+
+As you can see, it is very simple to register and retrieve utilities. If a
+utility does not exsist for a particular interface, such as the German socket,
+then the lookup fails
+
+ >>> capi.getUtility(IGermanSocket)
+ Traceback (most recent call last):
+ ...
+ ComponentLookupError: (<InterfaceClass __builtin__.IGermanSocket>, '')
+
+or more gracefully when specifying a default value:
+
+ >>> default = object()
+ >>> utility = capi.queryUtility(IGermanSocket, default=default)
+ >>> utility is default
+ True
+
+Note: The only difference between `getUtility()` and `queryUtility()` is the
+fact that you can specify a default value for the latter function, so that it
+will never cause a `ComponentLookupError`.
+
+
+Named Utilities
++++++++++++++++
+
+It is often desirable to have several utilities providing the same interface
+per site. This way you can implement any sort of registry using utilities. For
+this reason, utilities -- like adapters -- can be named.
+
+In the context of our story, we might want to do the following: You really do
+not trust gas stations either. What if the roads are blocked after a hurricane
+and the gas stations run out of oil. So you look for another renewable power
+source. Then you think about solar panels! After a storm there is usually very
+nice weather, so why not? Via the Web you order a set of 110V/120W solar
+panels that provide a regular US-style socket as output:
+
+ >>> class SolarPanel(object):
+ ... implements(IUSSocket)
+ ... def __repr__(self):
+ ... return '<instance of Solar Panel>'
+
+ >>> panel = SolarPanel()
+
+Once it arrives, we add it to our inventory:
+
+ >>> gsm.provideUtility(IUSSocket, panel, 'Solar Panel')
+
+You can now access the solar panel using
+
+ >>> utility = capi.getUtility(IUSSocket, 'Solar Panel')
+ >>> utility is panel
+ True
+
+Of course, if a utility is not available, then the lookup will simply fail
+
+ >>> capi.getUtility(IUSSocket, 'Wind Mill')
+ Traceback (most recent call last):
+ ...
+ ComponentLookupError: (<InterfaceClass __builtin__.IUSSocket>, 'Wind Mill')
+
+or more gracefully when specifying a default value:
+
+ >>> default = object()
+ >>> utility = capi.queryUtility(IUSSocket, 'Wind Mill', default=default)
+ >>> utility is default
+ True
+
+Now you want to look at all the utilities you have for a particular kind. The
+following API function will return a list of name/utility pairs:
+
+ >>> utils = list(capi.getUtilitiesFor(IUSSocket))
+ >>> utils.sort()
+ >>> utils #doctest: +NORMALIZE_WHITESPACE
+ [(u'', <instance of Generator>),
+ (u'Solar Panel', <instance of Solar Panel>)]
+
+Another method of looking up all utilities is by using
+`getAllUtilitiesRegisteredFor(iface)`. This function will return an iteratable
+of utilities (without names); however, it will also return overridden
+utilities. If you are not using multiple site managers, you will not actually
+need this method.
+
+ >>> utils = list(capi.getAllUtilitiesRegisteredFor(IUSSocket))
+ >>> utils.sort()
+ >>> utils
+ [<instance of Generator>, <instance of Solar Panel>]
+
+
+Factories
++++++++++
+
+A *factory* is a special kind of utility that exists to create other
+components. A factory is always identified by a name. It also provides a title
+and description and is able to tell the developer what interfaces the created
+object will provide. The advantage of using a factory to create an object
+instead of directly isntantiating a class or executing any other callable is
+that we can refer to the factory by name. As long as the name stays fixed, the
+implementation of the callable can be renamed or moved without a breakage in
+code.
+
+Let's say that our solar panel comes in parts and they have to be
+assembled. This assembly would be done by a factory, so let's create one for
+the solar panel. To do this, we can use a standard implementation of the
+`IFactory` interface:
+
+ >>> from zope.component.factory import Factory
+ >>> factory = Factory(SolarPanel,
+ ... 'Solar Panel',
+ ... 'This factory creates a solar panel.')
+
+Optionally, I could have also specifed the interfaces that the created object
+will provide, but the factory class is smart enough to determine the
+implemented interface from the class. We now register the factory:
+
+ >>> from zope.component.interfaces import IFactory
+ >>> gsm.provideUtility(IFactory, factory, 'SolarPanel')
+
+We can now get a list of interfaces the produced object will provide:
+
+ >>> ifaces = capi.getFactoryInterfaces('SolarPanel')
+ >>> IUSSocket in ifaces
+ True
+
+By the way, this is equivalent to
+
+ >>> ifaces2 = factory.getInterfaces()
+ >>> ifaces is ifaces2
+ True
+
+Of course you can also just create an object:
+
+ >>> panel = capi.createObject('SolarPanel')
+ >>> panel.__class__ is SolarPanel
+ True
+
+Note: Ignore the first argument (`None`) for now; it is the context of the
+utility lookup, which is usually an optional argument, but cannot be in this
+case, since all other arguments beside the `name` are passed in as arguments
+to the specified callable.
+
+Once you register several factories
+
+ >>> gsm.provideUtility(IFactory, Factory(Generator), 'Generator')
+
+you can also determine, which available factories will create objects
+providing a certian interface:
+
+ >>> factories = capi.getFactoriesFor(IUSSocket)
+ >>> factories = [(name, factory.__class__) for name, factory in factories]
+ >>> factories.sort()
+ >>> factories #doctest: +NORMALIZE_WHITESPACE
+ [(u'Generator', <class 'zope.component.factory.Factory'>),
+ (u'SolarPanel', <class 'zope.component.factory.Factory'>)]
+
+
+Site Managers
+-------------
+
+Why do we need site managers? Why is the component architecture API not
+sufficient? Some applications, including Zope 3, have a concept of
+locations. It is often desireable to have different configurations for these
+location; this can be done by overwriting existing or adding new component
+registrations. Site managers in locations below the root location, should be
+able to delegate requests to their parent locations. The root site manager is
+commonly known as *global site manager*, since it is always available. You can
+always get the global site manager using the API:
+
+ >>> gsm = capi.getGlobalSiteManager()
+
+ >>> from zope.component.site import globalSiteManager
+ >>> gsm is globalSiteManager
+ True
+ >>> from zope.component.interfaces import ISiteManager
+ >>> ISiteManager.providedBy(gsm)
+ True
+ >>> from zope.component.site import IGlobalSiteManager
+ >>> IGlobalSiteManager.providedBy(gsm)
+ True
+
+You can also lookup at site manager in a given context. The only requirement
+is that the context can be adapted to a site manager. So let's create a
+special site manager:
+
+ >>> from zope.component.site import SiteManager
+ >>> sm = SiteManager()
+
+Now we create a context that adapts to the site manager via the `__conform__`
+method as specied in PEP 246.
+
+ >>> class Context(object):
+ ... def __init__(self, sm):
+ ... self.sm = sm
+ ... def __conform__(self, interface):
+ ... if interface.isOrExtends(ISiteManager):
+ ... return self.sm
+
+We now instantiate the `Context` with our special site manager:
+
+ >>> context = Context(sm)
+ >>> context.sm is sm
+ True
+
+We can now ask for the site manager of this context:
+
+ >>> lsm = capi.getSiteManager(context)
+ >>> lsm is sm
+ True
+
+The site manager instance `lsm` is formally known as a *local site manager* of
+`context`.
+
+
More information about the Zope3-Checkins
mailing list