[Zope3-dev] Re: ZCML

Shane Hathaway shane at zope.com
Tue Aug 19 12:13:48 EDT 2003


Philipp von Weitershausen wrote:
> Shane Hathaway wrote:
>> In the Ape project, I found a way to achieve minimal overrides that 
>> wasn't too burdensome.  I'd like to discuss how we can improve upon 
>> the approach Ape takes, without losing the schema-based configuration 
>> we have now.  I have some ideas.
> 
> Let's bring it on!

Ok, thanks for listening.  I'll try to keep this short.  You might want 
to refer to apeconf.py.  By no means is it perfect, but it demonstrates 
a lot of what I'm saying:

http://cvs.zope.org/Products/Ape/lib/apelib/config/apeconf.py?rev=1.4&content-type=text/vnd.viewcvs-markup

I love the idea you guys came up with: parsing XML using Zope schemas is 
excellent and useful well beyond configuration files.  I didn't do that 
and it would enhance Ape.  Instead of one schema per directive, though, 
I think the parsing should be oriented toward one schema per *element*. 
  It would make a tighter fit with Python in general.  An implementation 
suggestion: the parser should maintain a registry of element name to 
(schema, handler), where the parser converts the tag to an object that 
fits the required schema, then passes that object to the handler.

Next, ZCML tries to allow developers to override configurations in 
chunks, but the chunks aren't as small as they could be.  To reduce the 
size of the chunks in Ape, I borrowed some ideas from relational theory.

By representing the configuration using relational tables, I realized 
that the problem of combining configurations from independent sources 
would become manageable.  If each row represents a minimal configuration 
directive, and the primary keys along with the table names form the 
discriminators, all I have to do is try to insert directives into the 
tables.  If two directives have the same primary key but the other 
fields have different values, the configuration conflicts somehow (and 
the insert will fail).  Two directives with different primary keys can 
live in harmony.

The process of storing the directives in relational tables also made it 
simpler to think about how to divide large directives into smaller 
directives.  Each row in a table should specify only one property.  If 
you need two properties, store them in two rows or two tables.

I realized that I was approaching the general idea of triples and RDF 
principles, but those notions are still a little too abstract for me to 
grasp intuitively.  Relational tables were a more practical approach.

To represent multiple directives in a single tag, I created a layer that 
translates XML into directives.  The tag handlers receive information 
about a tag from the XML processor and add corresponding directives to a 
list containing all directives.  (This layer would be simpler and report 
better errors if it used Zope schemas.)

After reading the entire configuration, the configuration machinery 
searches for conflicting directives and reports errors.  If it finds no 
conflicts, it places all of the directives in relational tables.

(In the process, I wrote a little relational table class that might be 
useful elsewhere.  It's in minitables.py.  Interestingly, I didn't need 
static types.  I also didn't need a "delete" method, so I didn't write 
one.  It was a YAGNI. ;-) )

Note that the configuration is purely declarative.  The order of the 
directives does not matter.  I think that is an important feature.

With all of the directives in relational tables, now comes the part I'm 
not quite happy with yet.  You need to assemble objects based on the 
tabulated directives.  You can't just iterate over all of the tables and 
apply each directive in sequence, since some directives require other 
directives to be applied first.  There might even be circular dependencies.

In Ape, I decided to create "assembler" objects.  They are responsible 
for reading the tables and applying the configuration.  The assemblers 
don't have any trouble with dependencies, but the MapperAssembler class 
turned out more complex than I hoped.  It also had to do a lot of querying.

So, if you're still following along ;-), that's where I'm stuck.  Ape's 
assemblers work, but they should be simpler.  In fact, it's tempting to 
say that the directive objects should also be assemblers, but that 
didn't work out in Ape.  Maybe that strategy would work in Zope 3.

> I totally agree. I recently refactored the browser:*form and *wizard 
> stuff, so I would be willing to take this at least one step further to::
> 
>      <browser:editform
>          schema="zope.app.content.image.IImage"
>          name="upload.html"
>          label="Upload an image"
>          permission="zope.ManageContent"
>          class=".image.ImageUpload."
>          template="imageedit.pt"
>          />
>        <browser:menu
>            id="zmi_views"
>            title="Upload"
>            />
>      </browser:editform>
> 
> That the 'title' property was actually for the menu and not for the form 
> had always bothered me... Any further suggestions before I get started 
> with this?

I'm guessing that the discriminator for this directive includes the 
"name" and "class" attributes, but not the other attributes.  To avoid 
conflicts, configuration authors need to be aware of exactly which 
attributes are included in the discriminator.  I wonder if it would make 
sense to move all attributes that are not part of the discriminator into 
child elements.  That could be too heavy, though.  Alternatively, we 
could just say that as a matter of style, discriminator attributes 
should come first.

Shane




More information about the Zope3-dev mailing list