Reducing the amount of ZCML directives ======================================= :Author: Philipp von Weitershausen, philikon@philikon.de :Status: IsProposal :Version: 3 :Date: $Date$ :Original: $URL$ Current status -------------- We have roughly 80 ZCML directives in Zope 3.2. Two of them, ``adapter`` and ``utility``, can be considered elementary. Another two not so elementary but still commonly used directives are ``view`` and ``browser:page``. Many of the other ZCML directives perform simple tasks revolving around those four directives. Sometimes they only save a couple of lines at the expense of having an extra directive. Problems -------- * It is hard for people to remember all of those numerous directives and remember their usage. This is a main part of criticism towards ZCML. * Another common criticism is that certain things shouldn't be done in ZCML but in Python. In general, ZCML should only be responsible for registering a component, but not for defining its behaviour. * Finally, it seems very confusing that factories and vocabularies, for example, are looked up as utilities but they have their own ZCML directive for registration. Goals ----- Apart from the "elementary" directives (``adapter`` and ``utility``), ZCML directives are good for... * registering something that isn't a utility or adapter. E.g. setting a security policy, registering a TALES namespace adapter, global principals, registering a meta type/portal type in Zope 2/CMF. * registering an adapter or utility with added policy information that belongs in configuration, not in code. E.g.: - security information (e.g. ``view``, ``browser:page`` in addition to ``adapter``, the ``content``/``class`` directive) - configuration information for the component (e.g. SMTP host for the mailer utility, locales directory for the gettext message catalogs, text file for the help topic) * additional ways of defining application policy. E.g. granting permissions on roles, etc. Other tasks, **especially automation**, is better done in Python as it is not a matter of defining or changing application policy. In short, ZCML directives should be more like on/off switches that turn behaviour defined in Python on or off. Proposal -------- The following eight ZCML directives automate certain processes associated with the registration of certain components or are not associated with registering components at all: * ``factory`` * ``vocabulary`` * ``modulealias`` * ``renderer:renderer`` * ``rdb:provideConnection`` * ``dav:provideInterface`` * ``browser:addview`` * ``browser:localUtility`` The proposal calls for deprecating these in Zope 3.3/2.10 and removing them after 12 months (which would correspond to Zope 3.5/2.12). In addition, it is proposed to deprecate and remove the alias ``content`` of the ``class`` directive and the ``factory`` and ``implements`` subdirectives of this directive. All of this will also get rid of two ZCML namespaces, ``renderer`` and ``dav``. In addition, it might be a good opportunity to get rid of ``rdb`` as well (there'll only be ``rdb:gadflyRoot`` left). Details ~~~~~~~ The following lists outlines solutions for avoiding the deprecated directives in the future. ``factory`` This is just a shortcut to the ``utility`` directive and saves one line in ZCML (for specifying the additional ``IFactory`` interface) and two lines in Python (for the title and description of the factory). For example:: would become:: whereas ``title`` and ``description`` now have to be specified as regular attributes on the factory component. Note that these values should generally be i18n messages because they might appear in browser menus. ``vocabulary`` Despite its name, this directive doesn't register vocabularies but vocabulary factories (because vocabularies are content dependent and might need to be initialized with more than just the context). It is probably one of the most magical ZCML directives of all. First, it is the only directive to take arbitrary arguments and by this it defies one of the initial aspects of ZCML (which is being restricted to a certain set of directives and parameters). Second, it creates a magical wrapper around vocabularies so that we can register them as vocabulary factories. All of this isn't needed at all, people can deal with these things much better in Python code, e.g.:: class SD6Vocabulary(object): zope.interface.implements(IVocabulary) zope.interface.classProvides(IVocabularyFactory) # the existence of this method satisfies IVocabularyFactory def __init__(self, context): ... Now, this class simply gets registered as a utility providing ``IVocabularyFactory``:: instead of:: Note that this will get rid of a (rather confusing) feature that involves the reuse of vocabulary implementations. A common type of vocabulary is the utility vocabuluary. A custom vocabulary listing a certain type of utility is easily registered with:: Note that ``interface`` is an arbitrary argument here and is passed as a unicode string to the ``UtilityVocabulary`` constructor. To achieve the same thing without the ``vocabulary`` directive, one would have to create a class in Python:: class SD6AgentsVocabulary(UtilityVocabulary): classImplements(IVocabularyFactory) interface = alias.interfaces.ISD6Agent and register this in ZCML with the ``utility`` directive as shown above. This proposal argues that this is much more straightforward and better documentable than the current behaviour. ``modulealias`` This directive can put Python modules into ``sys.modules`` under a new alias, typically to preserve backward compatability for old pickles in ZODB. It is strongly questionable whether this has anything to do with registering and/or configuring components at all, let alone the fact that people like the author of this proposal feel a bit uncomfortable with putting something like that in ZCML in the first place. If a ``sys.modules`` hack is needed, it should be done in Python with a proper BBB comment. Since the ``zope.modulealias`` package provides nothing else than this ZCML directive, the proposal calls for deprecating and removing the ``zope.modulealias`` package altogether. ``renderer:renderer`` Since the major simplification of the Component Architecture, this is probably one of the most useless directives of all times. It simply registers an adapter and does nothing else. Using the ``adapter`` directive here would not even cost you any extra lines. A search through the Zope 3 source code also indicates that it isn't used anymore. ``rdb:provideConnection`` This is just a shortcut to the ``utility`` directive and saves you exactly one line of ZCML. E.g.:: would become:: ``dav:provideInterface`` This is just a shortcut to a call to the ``interface`` directive (with an appropriate ``type`` argument) and saves only a few lines in ZCML. For example:: would become:: While this might seem like a lot of extra lines that are needed now, one should remember that ``dav:provideInterface`` is rarely used. Zope 3 itself uses it twice and it is imaginable that custom DAV apps might use it once or twice as well. That is due to the fact that the number of DAV namespaces is rather limited, at least limited enough that a custom directive is hardly justified. The WebDAVInterfacesWidgetsAndAdapters_ proposal also proposed to get rid of this directive in the context of a refactoring of the DAV support. ``browser:addview`` This is a shortcut to the ``browser:view`` directive and would save one line in ZCML. Another problem with it is that it seems broken (by looking at its code). Plus, it is also untested and untested code is broken by definition. This proposal therefore calls for its immediate removal (no deprecation period). ``browser:localUtility`` This is just a short-cut around the ``content`` directive and letting classes implement ``IAttributeAnnotatable`` and ``ILocalUtility``. So it saves two lines of Python code. Note that another proposal, `Local Component Management Simplification`_ is proposing to get rid of ``ILocalUtility``. ``content`` and ``class`` These are two names for the same directive which is confusing. Since this directive is mainly about making security assertions of any class, not only content components, it is proposed to remove the ``content`` alias and only leave ``class``. The following two items propose the removal of the two subdirectives to ``class`` that are not about security declarations. That way, the ``class`` complex-directive will be greatly simplified, only having two possible subdirectives dealing with security (``require`` and ``allow``). ``class/factory`` For the same reasons the ``factory`` directive is suggested to be removed, this subdirective of ``class`` is also proposed to be removed. It would better be served with appropriate statements in Python. E.g.:: ... would become:: ... with ``.alias.SydneyFactory`` being defined in Python:: from zope.component.factory import Factory sydneyFactory = Factory( SydneyBristow, # a callable, e.g. a class title=_(u'Agent Sydney Bristow'), description=_(u'Sydney is an undercover agent working for the CIA') ) ``class/implements`` This directive is often used to define policy by applying a marker interface to a class. The first problem with it is that you might want to use it independently from a ``class`` directive. Sometimes even (e.g. when in a 3rd party package), you would be conflicting with the ``class`` directive from the original package. Second, you might want to make factories other than a class implement something (e.g. a function). This proposal therefore calls for the ``class/implements`` subdirective to be moved to its own top-level ``implements`` directive. E.g.:: ... would become:: ... ``factory`` could be a class (in which case ``classImplements`` would be used) or any other object, e.g. a function (in which case ``implementer`` would be used). .. _WebDAVInterfacesWidgetsAndAdapters: http://www.zope.org/Members/mkerrin/WebDAVInterfacesWidgetsAndAdapters .. _Local Component Management Simplification: http://dev.zope.org/Zope3/LocalComponentManagementSimplification Risks ----- * The majority of the above mentioned directives are documented, especially in printed books. Removing them would eventually render these books outdated. This shouldn't stop us from simplifying ZCML, though. Software books will always become outdated eventually. * The usage of less specialized directives might increase the usage of dotted names referring to interfaces and such. A valid argument is that short directive names are sometimes easier to remember than long dotted names. On the other side, ZCML's usabililty benefits from the usage of more general directives (because there are less directives to work with and less to memorize), so the net effect might still be positive. In addition to that, the usage of dotted names has been reduced in the past by making the ``required`` and ``provides`` arguments of the ``utility`` and ``adapter`` directives optional, essentially turning them into on/off switches. Implementation status --------------------- This proposal has been implemented on the Zope 3 trunk, except for: * ``rdb:provideConnection`` wasn't removed. On a second thought, this directive also contains deployment information relevant to the system administrator (the DSN) that should not reside in Python code. * ``dav:provideInterface`` wasn't removed. Michael Kerrin's work on DAV is also calling for its removal so this is left for him to remove. * ``browser:localUtility`` wasn't removed. Its removal might collide with Jim's on-going work on local component simplifications. The removal is therefore postponed, if it won't be done by Jim anyways. * ``class/implements`` and ``class/factory`` weren't removed yet. Since this might be controversial, it should be brought up again on the mailinglist and then implemented on a new branch. Zope 2/Five has not been updated yet. Potential follow-ups -------------------- This proposal isn't addressing many directives that might also have a questionable status. Here are a few **unsorted** ideas that originated in a `related blog post`_ together with the rest of this proposal (these ideas are **not** up for discussion as part of this proposal, they merely set the mood for potential upcoming proposals): .. _related blog post: http://www.z3lab.org/sections/blogs/philipp-weitershausen/2005_12_14_zcml-needs-to-do-less ``xmlrpc:view`` It is probable that given the correct the usage of decent base classes, this directive could be replaced with the simple ``view`` directive. ``browser:page``, ``browser:view`` These two directives involve a lot of magic. Their feature span is great, they might be doing too much at a time. Their behaviour regarding the on-the-fly creation of classes is definitely cumbersome. Also, their distinction seems outdated nowadays. What should be done with them could fit into a separate proposal (which might also tackle ``xmlrpc:view`` at the same time as it involves similar issues.) ``browser:containerViews`` This is a shortcut to a bunch of things, so it actually saves more than just a few lines. It might perhaps not be worth removing it, though it is inflexible and questionable how many people with the need for custom containers actually use it. ``browser:addform``, ``browser:editform``, ``browser:schemadisplay`` If the form views these directives register were defined in Python (which would make the overriding of widgets much easier, too), a plain boring ``browser:page`` directive could take over. When using subclassing sensibly, a few lines of Python code should be enough for most use-cases. This is how ``zope.formlib`` does it, actually, so perhaps we should simply not worry about ``zope.app.form`` and deprecate it all together (after moving out the widgets and other useful parts). In fact, ``zope.formlib`` is a good example of how things should be done in many respects. A separate proposal should outline what happens to ``zope.app.form``. ``browser:viewlet``, ``browser:viewletManager`` The whole viewlet concept is new and needs to be field-tested before we know more about its preferred usage, etc. It is certain that the similarities between the directive handlers for these directives and others (e.g. ``browser:page``) should allow us to simplify the architecture eventually. Perhaps someone familiar with viewlets and their implementation will come up with a proposal that consolidates ZCML in this respect. Many directives of the ``browser`` namespace support the registration of menu items in addition to registering the component in question. Though this can be considered useful because it might save some typing, keeping directives as simple as possible (on/off switches!) might be weighed higher. People are certainly intimidated by the length of some of the ``browser`` directives (such as ``browser:editform``); by taking the menu functionality out, we could reduce many directives by two lines making them easier to understand by themselves (of course, we'll have to add another menu directive, but it'll only be 3 or so lines).