home contents changes options help subscribe edit (external edit)

Eggifying Zope's extension mechanism ("Products")

Author: Philipp von Weitershausen
Status: IsProposal?
Version: 1.2
Last changed:2007-01-25

Abstract

For their upcoming versions, Zope 2 consuming platforms such as Plone are creating standard Zope3-style Python packages while still having Zope 2 products around. This proposal aims at unifying the deployment of products and Python packages into a Zope 2 instance alike by using Python eggs and their entry point system.

Current situation

Zope 2 has an extension mechanism called "products" which allows for add-on packages to provide components. Such packages have to be a subpackage to the Products package (at least before Zope 2.10). This Products package is typically spawned over several directories, such as Zope/lib/python (where Zope denotes the Zope installation directory) and instance/Products (where instance denotes the path of a particular Zope 2 instance). This is done by extending the __path__ attribute that every Python package has.

Upon startup, Zope inspects the available products by getting a directory listing of all directories in Products.__path__, imports them and checks if their __init__.py has an initialize function. If so, it calls it. This allows products to have setup code executed, e.g. for registering their components (meta types, CMF content types, etc.).

With Zope 2.10, a Zope instance also has a lib/python directory that's on the PYTHONPATH. It allows for the installation of regular Python package which may also contain code that previously had to be in products. For example, such Python packages may also have an initialize function. However, they need to tell Zope about it explicitly with the five:registerPackage ZCML directive.

Large Zope consumer projects like Plone are moving away from products to regular Python packages, so that their code can be used better independently from Plone or even Zope 2. Many recent Plone packages can be used in a pure Zope 3 context, for example.

Problems

  1. Deploying Python packages and products is fundamentally different. First of all, the different location within an instance (Products directory vs. lib/python) makes it difficult for administrators to simply install large bundles like Plone that come as a set of both products and packages.
  2. Python packages tend to be distributed using eggs nowadays. This has many advantages: standardized packaging procedures (setup.py), dependency management from setuptools, easy_install-ability (if not through the easy_install tool itself then through frameworks that use the same mechanism such as zc.buildout) and visibility through the Cheeseshop.

Proposal

  1. The Products package becomes a namespace package in the pkg_resources-sense:

    try:
        import pkg_resources
        pkg_resources.declare_namespace('Products')
    except ImportError:
        pass
    

    That way, products can be distributed in egg form and installed into lib/python along with the rest of the Python packages and still appear as part of the Products package.

    Products distributed as eggs and deployed into lib/python (or anywhere else in the PYTHONPATH) will be indistinguishable from products deployed into Products except that they won't be picked up by Zope automatically during startup (see next bullet point).

  2. A Python package deployed as an egg (e.g. an egg containing a product) can choose to register itself as a product using the zope2.products entry point group. A setup.py entry of the egg for Products.CMFDefault would look like:

    from setuptools import setup
    setup(
        name='Products.CMFDefault',
        namespace_packages=['Products'],
        install_requires=['Products.CMFCore']
        entry_points = """
           [zope2.products]
           CMFDefault = Products.CMFDefault
           """
    )
    

    (This is only an example, of course. The CMF could also be distributed as one large egg containing CMFCore, CMFDefault, etc. packages and registering each of them as entry points at once.)

With this in place, deployment of platforms that need both products and Python packages becomes only a matter of installing them as eggs. This can be done e.g. via the workingenv.py script or via automated deployment systems such as zc.buildout. (Simply placing former products like Products.CMFDefault into lib/python/Products/CMFDefault won't work, it needs to be installed as an egg so that the entry point is registered.)

Traditional deployment mechanisms

It shall be noted that this proposal won't affect "traditional" deployment of products into the instance's Products directory at all. This proposal merely aims to provide an alternate, egg-based deployment mechanism for those who want to use eggs.

Conflicting products

If the same product is installed both as an egg and as a regular product into the Products directory, the standard Zope 2 product override rules apply. Those are based on the Products.__path__ variable which is manipulated both by Zope (e.g. to add the instance's Products directory as a location for Products subpackages) and by setuptools (to support namespace packages whose subpackages are distributed over several eggs).

Risks

  • The feature described here will depend on pkg_resources and setuptools (Zope 2 won't have to depend on them, the dependency can be optional).

Implementation status

None so far.



subject:
  ( 34 subscribers )