Packages, products and eggs
Terminology
Before we begin, let us get a few bits of terminology straight:
- Zope installation
- What you get by downloading and compiling Zope, or using one of the binary Zope installers.
- Software home ($SOFTWARE_HOME environment variable)
- Inside the Zope installation, the lib/python directory contains all the Python code that makes up the core of the Zope application server, known as the software home. The various Zope 3 packages are distributed here.
- Zope instance
- One Zope installation can support multiple instances. The mkzopeinstance.py script from the Zope installation is used to create a new instance. Each instance has a control script for starting and stopping the server, a zope.conf configuration file, a products directory, and a Data.fs file containing the ZODB. In this tutorial, we will let buildout create and configure a zope instance for us.
- Instance home ($INSTANCEHOME environment variable)
- The path to the current Zope instance.
- Python path ($PYTHONPATH environment variable, aka sys.path)
- The Python interpreter will look for modules in one or more folders, known as the Python path. When Zope is running, this typically includes the global Python modules making up the standard library, the interpreter's site-packages directory, where third party "global" modules and eggs are installed, the Zope software home, and the lib/python directory inside the instance home. In this tutorial, we will see how buildout adds a number of specific eggs to the pythonpath at run time as well.
- Python package
- A general term describing a redistributable Python module. At the most basic level, a package is a directory with an __init__.py file and some Python code.
- Zope product
- A special kind of Python package used to extend Zope. In old versions of Zope, all products were directories inside the special Products directory of an zope instance, and would have a Python module name beginning with "Products". For example, the core of Plone is a product called CMFPlone, known in Python as Products.CMFPlone.
- Python egg
- A way to package and distribute Python packages. Eggs contains a setup.py file with metadata such as the author's name and email address and licensing information, as well as information about dependencies. setuptools, the Python library that powers the egg mechanism, is able to automatically find and download dependencies for eggs that you install. It is even possible for two different eggs to concurrently use different versions of the same dependency. Eggs also support a feature called entry points, a kind of generic plug-in mechanism. We will not cover extension points in great detail here, but you can read more about them (and other features of eggs) at the PEAK website.
- The Cheese Shop (aka PyPI, the Python Package Index)
- The Cheese Shop is an index that hosts thousands of Python packages. You can browse this if you are looking for a particular package. More importantly, setuptools (and buildout, and the easy_install script) can query this index to download and install eggs automatically.
- easy_install
- A console script which can be used to search the Cheese Shop and install packages into the global Python environment. We will only use this to install a few global packages, since buildout manages eggs for us in a way that is local to each buildout project, avoiding global version ambiguities.
- Namespace package
- A feature of setuptools which makes it possible to distribute multiple, separate packages sharing a single top-level namespace. For example, the packages plone.theme and plone.portlets both share the top-level "plone" namespace, but they are distributed as separate eggs. When installed, each egg lives in its own directory (or possibly a compressed archive of that directory). Without namespace packages, we would have had to distribute one giant plone package, with a top-level plone directory containing all possible children, e.g. plone/theme and plone/portlets.
The magic Products namespace
When Zope finds a "product", it will create an entry in Control_Panel/Products in the root of the ZMI, and run the initialize() method, found in the product's root __init__.py file, each time Zope starts up. Not every package used in a Plone context needs to be a product, but "productness" is required for:
- GenericSetup profiles
- Skin directories being installed as layers in the portal_skins tool (but not for Zope 3-style browser views)
The easiest way to create a product is to use Paster/ZopeSkel to create an egg-ready package in the Products.* namespace using the basic_namespace template:
$ paster create -t basic_namespace Products.myproduct Selected and implied templates: ZopeSkel#basic_namespace A project with a namespace package Variables: egg: Products.myproduct package: productsmyproduct project: Products.myproduct Enter namespace_package (Namespace package (like plone)) ['plone']: Products Enter package (The package contained namespace package (like example)) ['example']: myproduct ... accept defaults to end
If you're using buildout, create your package in the src directory, and add references to it in the develop and instance/eggs sections of buildout.cfg:
develop =
src/Products.myproduct
...
[instance]
...
eggs =
${buildout:eggs}
${plone:eggs}
Products.myproduct
Run bin/buildout and you'll be set up to develop your egg-ready product in the src directory. Turn it into a distribution egg when complete.
It is possible to use packages (including egg-distributed ones) outside the Products namespace/directory as Zope 2 products. Many developers prefer this approach, feeling it unnatural to keep everything in a single, "flat" namespace.
Extra steps are required for this. Prior to Zope 2.10.4, this is also required for products in the Products namespace
We must add a line like the following to the package's configure.zcml:
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:five="http://namespaces.zope.org/five">
<five:registerPackage package="." initialize=".initialize" />
</configure>
Secondly, it is important to realize that packages outside the Products namespace are not automatically detected when Zope starts up. If they contain configure.zcml files (as most packages will do), this must be explicitly included from somewhere. This may be:
- Another package's configure.zcml file.
- Zope's site.zcml, the root of all ZCML files, which is found in the etc directory in the instance home.
- A ZCML slug, a one-liner created in the zope instance's etc/package-includes directory, with a name like my.package-configure.zcml.
In all cases, the syntax is the same:
<include package="my.package" file="configure.zcml" />
If you have meta.zcml or overrides.zcml files, you can add <include /> directives for these as well. If you are using slugs, it must be named accordingly, e.g. my.package-meta.zcml or my.package-overrides.zcml. A slug can not contain more than one line.
Later in this tutorial, we will show how buildout can manage slugs for us automatically.