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




--
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 [EMAIL PROTECTED]
Fax: +49 241 979 00 850                               IRC: MrTopf, Tao_T

neue Show: TOPFtäglich (http://mrtopf.de/blog/category/topf-taglich/)

_______________________________________________
Click here to unsubscribe or manage your list subscription:
https://lists.secondlife.com/cgi-bin/mailman/listinfo/pyogp

Reply via email to