Christian Scholz
2008-07-14 05:46:09 UTC
So far we have seen calls like this:
gsm.registerAdapter(SomeAdapterFactory)
gsm.registerUtility(IUtility, utility, name)
These tell the component registry which components are available.
In pyogp right now I put this into the module's itself so that it's
triggered on import time. This is bad thought because on import time we
don't know if everything we need is loaded yet and it depends on import
sequence which components are registered and might override each other.
What we want is to do this on startup time in some initialization call
before we use the library.
The way Zope does it is to use a configuration file in each package
which uses an XML based configuration language called ZCML (Zope
Configuration Markup Language). It looks like this:
<adapter
factory="pyogp.lib.base.credentials.PlainPasswordLLSDSerializer" />
<adapter factory=".login.LoginHandler" />
<utility
component=".database.connection"
provides=".interfaces.IConnection"
/>
You can omit "provides" if the utility class itself has an implements()
directive (that means you can also register classes as utilities which
do not even know about the component architecture. You can also add
interfaces to normal classes via ZCML, e.g. to use these to adapt these
classes to some other interface. This makes sense if these classes are
in a package you do not own and you still want to convert them into a
common interface you need to use.)
Another advantage of using ZCML instead of the python syntax is that you
can control better which component overrides which existing one. With
Python it's simple: The last call wins. If you register a utility for
ISomeUtility twice (e.g. both with no name) then the last register call
wins. With the uncertainty of what all the other components might
override this can lead to confusing results.
With ZCML you cannot do that. The second definition of that utility will
raise a conflict error. This helps you to keep your configuration space
clean from surprises. It does this by first scanning all configuration
files before doing the actual registration. Inbetween it can notice if
something will collide.
To nevertheless be able to override existing components ZCML gives you
the includeOverrides directive:
<includeOverrides file="overrides.zcml" />
You can place overriding utilities then in the file given. This makes
much sense as really only the calling application should be able to
override the defaults.
A complete ZCML file might look like this and reside in the package base
directory as configure.zcml:
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:grok="http://namespaces.zope.org/grok">
<adapter
factory="pyogp.lib.base.credentials.PlainPasswordLLSDSerializer" />
</configure>
This registers our serializer adapter which is right now in pyogp.
The only question now is: How do convince the library to parse this file.
This is easy, we just tell it:
from zope.configuration.xmlconfig import xmlconfig
def init():
fp = open("configure.zcml","r")
xmlconfig(fp)
fp.close()
We can place this snippet in a file called registration.py and we need
to call this from the application which uses the library. We could maybe
put more configuration in here, like which serializer to use or which
network library to use. We might not need overrides for this but simply
can use named utilities (e.g. the tests could use a special network
library for testing which does not actually send data over the network
but simply returns mockup data).
Some people now were unhappy with ZCML, simply because it means a
context switch. When you are writing Python code in order to make it
work you have to go to another file and add directives there in a
different language. It would be easier if you could do this directly
inside the actual module.
Martijn Faassen has the solution for this, he invented grok (with
others). Mainly meant as a means to make whole Zope3 easier to use and
to use convention over configuration (greetings to RoR) the basic part
was then moved out into it's own package "martian". This is what we can
use to make configuration easier as we don't have to write ZCML anymore.
We simply tell the adapter or utility directly how it should register:
In the existing code we have the PlaceAvatarAdapter in agent.py which
looks like this:
class PlaceAvatarAdapter(object):
"""handles placing an avatar for an agent object"""
implements(IPlaceAvatarAdapter)
adapts(IAgent)
We need to register it via ZCML:
<adapter factory=".agent.PlaceAvatarAdapter" />
We can replace this by doing this:
import grokcore.component as grok
class PlaceAvatarAdapter(grok.Adapter):
"""handles placing an avatar for an agent object"""
grok.implements(IPlaceAvatarAdapter)
grok.context(IAgent)
This means the same but we can omit the ZCML part. Note that we now
derive from grok.Adapter, telling grok, this is an adapter.
We could add more grok.* directives to further refine configuration. We
can also use something like this for utilities:
Here is some example networking utility:
class RESTClient_wsgi(grok.GlobalUtility):
grok.implements(IRESTClient)
grok.name('test')
As we can see, it derives from GlobalUtility marking it as utility and
we can define it's interface and it's name, making it a named utility.
So these are basically the ways to configure the ZCA:
- with python calls (but does not handle conflicts)
- with ZCML (adds another language to learn)
- with grok (goes back to python but with conflict resolution)
I would propose to go with grok. Even if you don't know all the
background here it should be easy to remember those simply directives
such as grok.name, grok.implements and grok.context.
The rest is done for you.
For an overview over ZCML you can additionally read this page:
http://www.muthukadan.net/docs/zca.html#zca-usage-in-zope
There is also much more information about the ZCA and all the stuff I
didn't mention (but we maybe also don't need) on this page:
http://www.muthukadan.net/docs/zca.html
(what you can also find there are functions to introspect objects and
classes to check what interfaces they have, what they can adapt to etc.
This is mainly used for unit tests and the like, you should not use this
in your real code.
I see this as the final part but maybe some bits could be added in terms
of best practices, examples and ways of working with it (guess this is
best practices).
-- Tao
gsm.registerAdapter(SomeAdapterFactory)
gsm.registerUtility(IUtility, utility, name)
These tell the component registry which components are available.
In pyogp right now I put this into the module's itself so that it's
triggered on import time. This is bad thought because on import time we
don't know if everything we need is loaded yet and it depends on import
sequence which components are registered and might override each other.
What we want is to do this on startup time in some initialization call
before we use the library.
The way Zope does it is to use a configuration file in each package
which uses an XML based configuration language called ZCML (Zope
Configuration Markup Language). It looks like this:
<adapter
factory="pyogp.lib.base.credentials.PlainPasswordLLSDSerializer" />
<adapter factory=".login.LoginHandler" />
<utility
component=".database.connection"
provides=".interfaces.IConnection"
/>
You can omit "provides" if the utility class itself has an implements()
directive (that means you can also register classes as utilities which
do not even know about the component architecture. You can also add
interfaces to normal classes via ZCML, e.g. to use these to adapt these
classes to some other interface. This makes sense if these classes are
in a package you do not own and you still want to convert them into a
common interface you need to use.)
Another advantage of using ZCML instead of the python syntax is that you
can control better which component overrides which existing one. With
Python it's simple: The last call wins. If you register a utility for
ISomeUtility twice (e.g. both with no name) then the last register call
wins. With the uncertainty of what all the other components might
override this can lead to confusing results.
With ZCML you cannot do that. The second definition of that utility will
raise a conflict error. This helps you to keep your configuration space
clean from surprises. It does this by first scanning all configuration
files before doing the actual registration. Inbetween it can notice if
something will collide.
To nevertheless be able to override existing components ZCML gives you
the includeOverrides directive:
<includeOverrides file="overrides.zcml" />
You can place overriding utilities then in the file given. This makes
much sense as really only the calling application should be able to
override the defaults.
A complete ZCML file might look like this and reside in the package base
directory as configure.zcml:
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:grok="http://namespaces.zope.org/grok">
<adapter
factory="pyogp.lib.base.credentials.PlainPasswordLLSDSerializer" />
</configure>
This registers our serializer adapter which is right now in pyogp.
The only question now is: How do convince the library to parse this file.
This is easy, we just tell it:
from zope.configuration.xmlconfig import xmlconfig
def init():
fp = open("configure.zcml","r")
xmlconfig(fp)
fp.close()
We can place this snippet in a file called registration.py and we need
to call this from the application which uses the library. We could maybe
put more configuration in here, like which serializer to use or which
network library to use. We might not need overrides for this but simply
can use named utilities (e.g. the tests could use a special network
library for testing which does not actually send data over the network
but simply returns mockup data).
Some people now were unhappy with ZCML, simply because it means a
context switch. When you are writing Python code in order to make it
work you have to go to another file and add directives there in a
different language. It would be easier if you could do this directly
inside the actual module.
Martijn Faassen has the solution for this, he invented grok (with
others). Mainly meant as a means to make whole Zope3 easier to use and
to use convention over configuration (greetings to RoR) the basic part
was then moved out into it's own package "martian". This is what we can
use to make configuration easier as we don't have to write ZCML anymore.
We simply tell the adapter or utility directly how it should register:
In the existing code we have the PlaceAvatarAdapter in agent.py which
looks like this:
class PlaceAvatarAdapter(object):
"""handles placing an avatar for an agent object"""
implements(IPlaceAvatarAdapter)
adapts(IAgent)
We need to register it via ZCML:
<adapter factory=".agent.PlaceAvatarAdapter" />
We can replace this by doing this:
import grokcore.component as grok
class PlaceAvatarAdapter(grok.Adapter):
"""handles placing an avatar for an agent object"""
grok.implements(IPlaceAvatarAdapter)
grok.context(IAgent)
This means the same but we can omit the ZCML part. Note that we now
derive from grok.Adapter, telling grok, this is an adapter.
We could add more grok.* directives to further refine configuration. We
can also use something like this for utilities:
Here is some example networking utility:
class RESTClient_wsgi(grok.GlobalUtility):
grok.implements(IRESTClient)
grok.name('test')
As we can see, it derives from GlobalUtility marking it as utility and
we can define it's interface and it's name, making it a named utility.
So these are basically the ways to configure the ZCA:
- with python calls (but does not handle conflicts)
- with ZCML (adds another language to learn)
- with grok (goes back to python but with conflict resolution)
I would propose to go with grok. Even if you don't know all the
background here it should be easy to remember those simply directives
such as grok.name, grok.implements and grok.context.
The rest is done for you.
For an overview over ZCML you can additionally read this page:
http://www.muthukadan.net/docs/zca.html#zca-usage-in-zope
There is also much more information about the ZCA and all the stuff I
didn't mention (but we maybe also don't need) on this page:
http://www.muthukadan.net/docs/zca.html
(what you can also find there are functions to introspect objects and
classes to check what interfaces they have, what they can adapt to etc.
This is mainly used for unit tests and the like, you should not use this
in your real code.
I see this as the final part but maybe some bits could be added in terms
of best practices, examples and ways of working with it (guess this is
best practices).
-- Tao
--
Christian Scholz video blog: http://comlounge.tv
COM.lounge blog: http://mrtopf.de/blog
Luetticher Strasse 10 Skype: HerrTopf
52064 Aachen Homepage: http://comlounge.net
Tel: +49 241 400 730 0 E-Mail ***@comlounge.net
Fax: +49 241 979 00 850 IRC: MrTopf, Tao_T
neue Show: TOPFt?glich (http://mrtopf.de/blog/category/topf-taglich/)
Christian Scholz video blog: http://comlounge.tv
COM.lounge blog: http://mrtopf.de/blog
Luetticher Strasse 10 Skype: HerrTopf
52064 Aachen Homepage: http://comlounge.net
Tel: +49 241 400 730 0 E-Mail ***@comlounge.net
Fax: +49 241 979 00 850 IRC: MrTopf, Tao_T
neue Show: TOPFt?glich (http://mrtopf.de/blog/category/topf-taglich/)