[Grok-dev] Using martian to load schemata
Martin Aspeli
optilude at gmx.net
Wed Jun 25 08:14:13 EDT 2008
Hi all,
I have a question about using martian (I think) - I hope this is an appropriate
place to ask.
Basically, I have a library, plone.supermodel (see Plone svn - it's changing
slightly right now, but should stabilise soon) that can parse and serialise an
Interface with zope.schema fields from/to XML. It can also parse some
arbitrary metadata associated with each schema and/or each field via utility
plugins that gets annotated onto the interface using tagged values. Metadata
can be things like widget hints or security settings.
The supermodel parser creates a new InterfaceClass object (i.e. an interface)
with a fake __name__ and __module__. It is more of a vehicle for schema fields
than something usable on its own.
In many cases, I want to be able to declare a "real" interface that loads its
fields from an XML file (and adds metadata as a tagged value on the interface).
The syntax I've been experimenting with looks like this:
from plone.supermodel import xml_schema
class IPage(xml_schema("page.xml")):
"""Schema for a page type, loaded from page.xml
"""
The xml_schema() function parses page.xml and returns an InterfaceClass with a
fake module path. This then becomes the base class for the real IPage
interface, which naturally gets all the fields of the dynamically created
interface by virtue of inheritance.
This works in tests, but not very well in practice. The reason is that
xml_schema() invokes a parser that looks up utilities that know how to parse
various types of fields, as well as plug-ins from third party products that
provide the schema metadata (by parsing things in other XML namespaces). In the
example above, xml_schema() is called at module import time. If the module is
imported during ZCML parsing, then the CA won't have been properly loaded yet,
and I get ComponentLookupError's when the parser tries to load its utilities.
I therefore need some way to defer the actual parsing until the CA is loaded.
One way may be to use some kind of fake chicken as the base interface and
replace it with a populated fake interface base class at runtime (e.g. lazily -
keep trying until it works). That's a bit scary, though.
I was wondering if this could be done with Martian. I assume Grok will have the
same problem in places.
I would also consider switching the syntax a bit, to be:
from zope.interface import Interface
from plone.supermodel import xml_schema
class IPage(Interface):
xml_schema("page.xml")
This wouldn't create a dynamic base class, but instead it would scribble the
fields onto the IPage class. Again, it would need to do so after the CA was
loaded, though.
Can anyone tell me whether Martian would be worth exploring here? Or do I need
something else that's more aware of the Zope 3 CA? I don't fully understand how
to react once the CA is loaded, though I'm guessing I should be using
zope.configuration actions in some way and ensure that my actions execute late
in the process?
Any hints would be greatly appreciated. I also hope that you guys may be
interested in plone.supermodel. It should work for any application that bases
its models on zope.schema interfaces and wants to load those from XML. Use
cases in Plone land include a way to do this with a TTW-only schema, in order
to support TTW type creation, and support for using GUI tools to build the
model file.
Cheers,
Martin
More information about the Grok-dev
mailing list