|
|
/ Hathaway Weblog / Zope 3 Frustration |
I need to confess my frustration with Zope 3. It really won't do any good to keep this to myself. Maybe by talking about it, we (fellow Zope contributors and enthusiasts) can make some headway.
Two months ago, I migrated a project to Zope 3. The first week was quite arduous. Little goofs I made in ZCML led to disasters that I wasn't ready to solve. I failed to find any way to debug ZCML other than repeated trial and error. I longed for pdb: I wanted to step into the code executed by the configuration directives and see just what the directives were configuring. Even now, I'm still relying on trial and error.
If I hadn't been so committed, I would have silently given up on Zope 3 on the first day. I already believed strongly in certain Zope 3 technologies: page templates, interfaces, schemas, adapters, the object database, the publisher, generated forms, and a basic UI for managing a tree of objects through the web. It turns out that the new security proxies also work splendidly and predictably.
However, the Zope 3 code base is hard to read. In particular, it's hard to follow code paths. I have to weave between Python and ZCML, grepping at every indirection. Whenever I write new Python code designed to run in Zope 3, I also have to write ZCML before that code has any effect. I fear that because so much is in ZCML, my own code is becoming as hard to follow as the Zope 3 code base. Even worse, my ZCML is full of duplication. It's hard to apply the "don't repeat yourself" principle when the language's expressiveness is intentionally limited.
How I wish I could just write something like this:
from zope.configure import permission, adapts
from zope.security import public
class FileView:
adapts(file)
@permission(public)
def read_lowercase(self):
return self.context.read().lower()
Or perhaps I could take a hint from Phillip Eby and drop adaptation:
from zope.configure import view
from zope.security import public
@view(type=file, permission=public)
def read_lowercase(f):
return f.read().lower()
Maybe I'm leaning toward PEAK. I never considered PEAK before, seeing it as excessively verbose and Java-like, until Phillip started exploring generic functions. From what I read on Phillip's weblog, generic functions are dramatically simplifying PEAK.
Now, I understand the purpose of ZCML. ZCML is intended to promote reuse: you can use another developer's code, unchanged, with your own configuration. What I'm seeing, though, is that configuration is just as complex as code. Since my tools for working with complex code are already excellent and mature, I want to use those same tools to read, write, and refactor configuration.
Comments
Hey Shane. :-)
I'm sympathetic with your concerns. FWIW, I think putting the permission information in the same code file is a step back. I'm much more tempted by the Twisted configuration model of having configuration in a separate file, but the same language: Python.
One argument against that solution, in comparison to ZCML, might be that it will be harder to maintain the division. Twisted seems to have done a good job of that though, afaict.
Another argument against both types of Python configuration (in-line, as you and Phillip show, and in a separate file) might be that people who configure a site should not have to know Python. While I would agree that someone with the site configuration hat should not have to know Python as well as a component developer, I think the idea of someone running a non-toy Zope site who does not know at least minimal Python is pretty questionable.
I centered on the permission blurbs in your examples. The adapter information is component specific, and I would agree that the declaration of use does not need to be in a separate file. However, registering the adapter does still belong in a separate configuration file, I'd argue. Jim's mentioned that he wants zcml to grow the ability to sniff classes for the adapter-specific information, and reduce that duplication of information: that would be a step forward, at least.
Another reduction of duplicated information would be to have nested zcml tags that allow specifying shared information in upper tags; I think improved configuration patterns that reduce duplication will have to evolve as we understand the problem.
I think there are two separate issues:
- indirections
- zcml
Indirections would be there no matter what mechanism is used to express configuration. This is a challenge any time information is spread around. It occurs with inheritence and with technologies like CMF skins and pages, etc.
I think Stephan's API documentation tool, or something like it has tremendous potential to help people discover how things are connected. It needs a lot more work.
wrt ZCML, first, it is becoming simpler. I recently added zope.component.adapts which allows you to declare what a class adapts in a class statement. This allows you to omit this information from ZCML. Similarly, if a class implements a single interface, you can omit the provides attribute from adapter or utility directives. Now, often an adapter is registered with only a factory. I plan to extend this to things like subscribers, views and so on. I'm also trying to get to a place where you can generally avoid specifying permissions for adapters and utilities, since, generally, these just provide application logic that doesn't need to be protected.
Second, I'd like to make it easier to use Zope 3 technologies without the full application server. I've started a new Bobo project that will make it easier for people to build small applications without ZCML. ZCML is really only appropriate for application servers, where you want to compine software in large pieces.
Thanks for your comments, Gary and Jim. I'm definitely learning something.
In my current project, I'm using zapi, zope.app.container and zope.app.publisher, but few other parts of zope.app. I can't decide whether it's a Zope application or not. It feels like I shouldn't be using zope.app, but some of the core parts of zope.app are quite useful and reusable for many things.
Personally, although I'm a big fan of the Twisted core, I dislike most of the rest, particularly the mktap application configuration software. Fortunately, the core is easy to use by itself.
I think what I'm really after is a Bobo-like framework that gives me a simple web interface for managing custom objects. Wouldn't it be cool if newbies only had to write:
class IHello(Interface):
greeting = TextLine(default=u"Hello, world!")
class Hello(Persistent):
implements(IHello)
if __name__ == '__main__':
bobo(class_=Hello, security=None, listen=('', 8080))
This would present a web interface for editing the greeting field of the single Hello object in an object database. From there, newbies desire a simple way to:
- enable and manage security
- refine or replace the look and feel
- create other objects
- manage collections of objects
- make associations (such as containment relationships) between objects
- index and search objects
- store objects in a relational database
- switch HTTP servers (perhaps using WSGI)
- enable downloads and uploads via FTP or WebDAV
- enable XML-RPC or SOAP
- launch a client-side GUI for managing objects :-)
That's my vision right now. Zope would be oh-so-close to meeting it if only the first few steps were simpler.
