What's New in Zope 3.3

Date: Saturday, August 19 2006
Author: Baiju M <baiju.m.mail AT gmail.com>

1   Simplify Skinning

Proposal: SimplifySkinning?

Skins are technically interfaces defined using zope.interface. To create a custom skin it is always better to inherit from a standard skin interface. It is by convention that skins will be created in sub-package named skin in your browser package of your main package. For example if your package name is foo, then foo.browser.skin will be the skin package, but this is not mandatory. Your skin interfaces can be defined in foo.browser.skin.interfaces. Here is an example interfaces.py:

from zope.app.rotterdam import Rotterdam

class IShanghaiSkin(Rotterdam):
  """Wo zhu zai Shanghai"""

To register this we will use interface and utility directives in zope namespace. The type of the IShanghaiSkin? skin is zope.publisher.interfaces.browser.IBrowserSkinType?. Here is a sample configure.zcml:

<interface
    interface=".interfaces.IShanghaiSkin"
    type="zope.publisher.interfaces.browser.IBrowserSkinType"
    />

<utility
    component=".interfaces.IShanghaiSkin"
    provides="zope.publisher.interfaces.browser.IBrowserSkinType"
    name="ShanghaiSkin"
    />

As a shortcut, we can also just use the interface directive and pass the new name parameter. The following one directive has the same effect as the two above regarding the skin registration:

<interface
    interface=".interfaces.IShanghaiSkin"
    type="zope.publisher.interfaces.browser.IBrowserSkinType"
    name="ShanghaiSkin"
    />

Registering views and resources is not any different now, but you can simply register them on the skin directly:

<browser:resource
    name="zope3logo.gif" 
    file="shanghailogo.gif" 
    layer=".interfaces.IShanghaiSkin"
    />

As you can see, we don't have to create an extra layer just to create a custom skin. We were also able to use standard Component Architecture ZCML directives instead of custom ones whose special syntax the developer needs to remember additionally.

A typical browser:page with with layer specified is like this:

<browser:page
    for="*"
    name="dialog_macros"
    permission="zope.View"
    layer=".interfaces.IShanghaiSkin"
    template="dialog_macros.pt"
    />

2   Local component management simplification

Proposal: LocalComponentManagementSimplification?

This is the major change in this release. As part of this proposal local component management has become very easy. Now the defining, registering and looking up components can do using simple API. The components supported are utilities, adapters, subscribers and handlers.

See this example (not a good one, though), http://zopetic.googlecode.com/svn/trunk/src/browser/collectorform.py You can see how registration of an IntId? and Catalog utilities done there.

3   Reducing the amount of ZCML directives

Proposal: ReducingTheAmountOfZCMLDirectives?

There are the directives deprecated.

  • factory
  • vocabulary
  • modulealias
  • renderer:renderer
  • browser:addview (removed)
  • content and localUtility
  • browser:layer and browser:skin (see Simplify Skinning, above)
  • browser:tool (no longer needed)
  • defaultLayer (no longer needed)
  • defaultView (use browser:defaultView instead)

The following lists outlines solutions for avoiding some deprecated directives in the future.

3.1   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:

<factory
    id="alias.SydneyBristow"
    component=".alias.sydneyFactory"
    title="Agent Sydney Bristow"
    description="Sydney is an undercover agent working for the CIA"
    />

would become:

<utility
    provides="zope.component.interfaces.IFactory"
    component=".alias.sydneyFactory"
    name="alias.SydneyBristow"
    />

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.

3.2   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?:

<utility
    component="alias.SD6Vocabulary"
    provides="zope.app.schema.vocabulary.IVocabularyFactory"
    name="alias.SD6"
    />

instead of:

<vocabulary
   factory="alias.SD6Vocabulary"
   name="alias.SD6"
   />

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:

<vocabulary
    name="SD6 Agents"
    factory="zope.app.component.vocabulary.UtilityVocabulary"
    interface="alias.interfaces.ISD6Agent"
    />

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.

3.3   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.

3.4   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.

3.5   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.

3.6   content, class and localUtility

content and class 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 localUtility directive is only an alias of the class directive within Zope 3.3. Read LocalComponentManagementSimplification? to know more about it:

Local components will not required to provide any special magic interfaces (e.g. ILocalUtility?). Local components will not be required to be annotatable.

4   Make zope.app smaller

Proposal: MakeZopeAppSmaller?

zope.app is a namespace package which consists a huge number of sub-packages until 3.2. This proposal has reduced the size of zope.app considerably. From the proposal:

The long-term goal is to make zope.app smaller so that it only contains
things vital to the Zope 3 application server. Essentially, this would be 
zope.app.appsetup, zope.app.twisted, zope.app.server and zope.app.publication.

As part of this proposal, some packages are moved to zope namespace. Here is the list:

old package new package
zope.app.annotation zope.annotation
zope.app.content_types zope.contenttype
zope.app.copypastemove zope.copypastemove
zope.app.datetimeutils zope.datetime
zope.app.decorator zope.decorator
zope.app.dublincore zope.dublincore
zope.app.filerepresentation zope.filerepresentation
zope.app.location zope.location
zope.app.mail zope.sendmail
zope.app.rdb zope.rdb
zope.app.size zope.size
zope.app.timezones zope.datetime
zope.app.traversing zope.traversing

Some other packages are split into more packages and moved to zope subpackages.

package changes
zope.app.event functionalities are moved to zope.component and zope.lifecycleevent
zope.app.exception exceptions and interfaces are moved to zope.exceptions
zope.app.publication setDefaultSkin is moved to zope.publisher.browser
zope.app.publisher
  1. Moved BrowserView? and applySkin to zope.publisher.browser,
  2. Move menu support to zope.menu,
  3. Move ZCML directive handlers etc. to zope.browserzcml.
  4. Rename zope.formlib.page.Page to zope.publisher.browser.BrowserPage? alongside BrowserView?
zope.app.schema move IVocabularyFactory? to zope.schema.interface

5   Other Changes

FIXME: Any changes not mentioned in other sections?

6   New, Improved, and Deprecated Modules

Most Important Changes Since 3.2

  • Added two new verbs for zcml:condition, 'not-have' and 'not-installed', which are the negated counterparts to 'have' and 'installed'.
  • Added a new functional test layer, zope.app.testing.functional.FunctionalNoDevMode?, that is like Functional, except that it doesn't have the developer mode feature enabled when loading ZCML.
  • Provided a new component registry API that allows multiple component registries to be combined more flexibly than before. See zope.component.interfaces.IComponentRegistry? for more information.
  • Greatly simplified local-component registration. See zope.component.interfaces.IComponentRegistry? for more information.
  • Moved many packages out of zope.app to make them easier to use outside of Zope.
  • Change the session credentials plugin to make it configurable in which fields it looks for the credentials.
  • Added a new API for collating text. You can now adapt a locale to zope.i18n.interfaces.ILocales?.ICollator?. You can then use that to sort strings, such as menu entries, in a locale-specific fashion.
  • A new zope.annotation.factory helper function that makes it easier to create annotations. Also added a README in zope.annotation which explains how to use it.
  • Added a more complete set of widgets for fields that use iterable sources. These widgets now mirror the set provided by vocabulary-based fields.
  • Added a cleaner and more robust API to testbrowser for setting file-upload data.
  • Deprecated several ZCML directives:
    • browser:addview
    • browser:layer
    • browser:skin
    • browser:tool
    • content (as an alias to the class directive)
    • defaultLayer
    • defaultView (use the browser:defaultView directive)
    • factory
    • localUtility (use the class directive)
    • modulealias
    • renderer:renderer
    • vocabulary
  • The 'browser:layer' directive and the 'ILayer?' interface has been deprecated. Registering layers has become obsolete, layers should be created as interfaces extending 'IBrowserRequest?'.
  • The 'browser:skin' directive has been deprecated. Skins should be created as interfaces extending 'IBrowserRequest?' and can be registered using a simple 'utility' directive.
  • The 'ISkin?' interface has been renamed to 'IBrowserSkinType?'.

Additional list of deprecated packages (this list extends the tables seen previously):

package changes
zope.app.introspector merged into zope.app.apidoc.codemodule
zope.app.layers deprecated, see zope.publisher.interfaces.browser
zope.app.pluggableauth deprecated, see zope.app.authentication
zope.app.servicenames  
zope.app.site  
zope.app.skins deprecated, see zope.publisher.interfaces.browser
zope.app.tests see zope.app.testing
zope.app.utility  
zope.modulealias  

7   Porting to Zope 3.3

When porting remember these things:

  • Use from zope.i18n import MessageFactory? instead of from zope.app.i18nmessageid import MessageIDFactory?
  • Use zope.app.component.interfaces.ISite? instead of zope.app.site.interfaces.ISite? (Mostly in configure.zcml)
  • Use msg = Message(_('${chars} characters'), mapping={'chars':chars}) instead of msg = _('${chars} characters'); msg.mapping['chars']? = chars
  • In ZCML, content directives should be renamed to class.
  • Use zope.* instead of zope.app.* for many packages, including filerepresentation, size

8   FAQ

8.1   Is factory directive deprecated? What about factory sub-directive of class?

Yes, factory directive under zope namespace is deprecated. But factory sub-directive of class directive is not deprecated yet. Though you can completely avoid the usage of this.

E.g.:

<content class=".alias.Sydney">
  <factory
      id="alias.SydneyBristow"
      title="Agent Sydney Bristow"
      description="Sydney is an undercover agent working for the CIA"
      />
...
</content>

would become:

<class class=".alias.Sydney">
  ...
</class>

<utility
    component=".alias.sydneyFactory"
    provides="zope.component.interfaces.IFactory"
    name="alias.SydneyBristow"
    />

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')
    )

9   Acknowledgements

Thanks to all those who edited this page. Thanks to all who helped me through IRC and mailing list. If you have edited this page, please add your name here. Contributors: Philipp von Weitershausen, Florent Xicluna,