Difference between pages "Zope HOWTO" and "Zope Component Architecture"

(Difference between pages)
 
(Interfaces)
 
Line 1: Line 1:
This page documents how to use Zope with Funtoo Experimental, which currently has good Zope support thanks to [[Progress Overlay Python]] integration.
+
This page describes the Zope Component Architecture, or ZCA for short.
  
== About Zope ==
+
== ZCA: What is it? ==
  
Zope is an Open Source application server framework written in Python. It has an interesting history which you should familiarize yourself with before starting Zope development, as it contains several interesting twists and turns.
+
The Zope Component Architecture is a Framework that arose out of Zope 3 development, and is now integrated into Zope 2. It is used by various software projects, and doesn't even require Zope to run. You can use it natively with Python. It's part of Zope, but it's not Zope.
  
=== Zope History ===
+
=== Why Should I Use It? ===
  
{{fancynote| This HOWTO targets Zope 2.13, which includes Five. It is typically the version you should be using for new Zope projects.}}
+
You shouldn't. Well, I mean, don't use it unless you see the value in it, or if you are using a software project that uses it already. Due to the fact that it is a framework, it does impose a paradigm on how you develop Python code and you need to determine if that paradigm works for your particular situation, or if it doesn't.
  
* There are two versions of Zope: Zope 2 and Zope 3. One might assume that Zope 3 is the version that people should use for new software development projects by default, but this is not the case. Most Zope-based projects continue to use Zope 2. Zope 3 was an attempt to redesign Zope 2 from scratch, and is completely different from Zope 2, but it was not adopted by the community.
+
=== The Paradigm ===
  
* There is also something called [http://codespeak.net/z3/five/ Five] (named because it is "2 + 3") that backports many of the new features of Zope 3 into the Zope 2 framework. Several projects will use Zope 2 plus Five in order to use some of the newer features in Zope. Five was merged into mainline Zope 2 in early 2010, and first appeared in Zope 2.8.
+
In one phrase, the paradigm implemented by the ZCA framework is that of a "component architecture", which in itself may not mean anything to you, but it is easy to understand.  
  
* You can learn more about the history of Zope 2, 3 and Five in the [http://svn.zope.org/Zope/trunk/src/Products/Five/README.txt?view=markup Five README].
+
If you are reading this, you have probably developed some Python software. Now imagine if you had to lead a team that is developing a complex Python software application. This type of project presents new challenges that you may not experience as an individual developer.  
  
* To make things even more interesting, work on [http://docs.zope.org/zope2/releases/4.0/ Zope 4] is underway, and it will be based on 2.13 rather than 3.x. It includes a number of [http://docs.zope.org/zope2/releases/4.0/CHANGES.html#restructuring incompatible changes] with prior versions.
+
For one, different members of the team have different skill sets. You also want the team to be able to work in parallel rather than have some members of the team sit idle waiting for parts they depend upon to be completed. Ideally, you would want to split the application into logical parts and then clearly define who is doing what part of the software and what each team is delivering. But when the software is complete, it needs to work together as an integrated unit.
=== Zope Resources ===
+
  
Now that you understand what version of Zope you should be targeting (2.13), we can point you towards the correct documentation :)
+
Clearly, you will need to think about the software architecture. But it can also be helpful to have a framework your team can use that helps them to split the application into logical parts, develop these parts in parallel, and then allow these various parts to interact once they are done. This is what a component architecture is typically used for. It supports this style of development.
  
; '''[http://docs.zope.org/zope2/zope2book/ The Zope 2 Book]'''
+
=== Interfaces ===
: This book provides a general introduction to Zope concepts and ZMI. It is a good place to start, but doesn't provide a direct introduction to Zope development. It's recommended that you skim through this book to familiarize yourself with Zope. It generally does not assume much prior knowledge about Web development or Python.
+
; '''[http://docs.zope.org/zope2/zdgbook/ Zope Developer's Guide]'''
+
: This guide will give you a better introduction to Zope development. It assumes you already know Python. Skip chapters 1 and 2 and start in [http://docs.zope.org/zope2/zdgbook/ComponentsAndInterfaces.html chapter 3], which covers components and interfaces. [http://docs.zope.org/zope2/zdgbook/Products.html Chapter 5] covers the creation of your first product.
+
; '''[http://codespeak.net/z3/five/manual.html The Five Manual]'''
+
: We're not done yet. There is a bunch of stuff in Zope 2.13 that is not in the official documentation. Namely, the stuff in Five.
+
; '''[http://docs.zope.org/ztkpackages.html ZTK Documentation]'''
+
: ZTK 
+
; '''ZCA'''
+
: [http://www.muthukadan.net/docs/zca.html A Comprehensive Guide to Zope Component Architecture] offers a good introduction to the programming concepts of ZCA. We also have a new page on [[Zope Component Architecture]] which will help you to understand the big picture of ZCA and why it is useful. ZCML ("Z-camel") is a part of ZCA and  was introduced in Zope 3, so typically you will find ZCML documented within Zope 3 documentation and book.
+
; '''Content Components'''
+
: Views and Viewlets: [http://docs.zope.org/zope.viewlet/index.html This tutorial on viewlets] also contains some viewlet-related ZCML examples near the end. The "Content Component way" of developing in Zope seems to be a Zope 3 thing and tied to ZCML. Chapter 13+ of Stephan Richter's ''Zope 3 Developer's Handbook'' (book) seems to cover this quite well. You will probably also want to check out Philipp Weitershausen's ''Web Component Development with Zope 3'' (book).
+
; '''[http://wiki.zope.org/zope2/Zope2Wiki Zope 2 Wiki]'''
+
: Main wiki page for all things related to Zope 2.
+
; '''[http://docs.zope.org docs.zope.org]'''
+
: This is the main site for Zope documentation.
+
  
== First Steps ==
+
You decide to split your team into smaller groups, and each group will tackle one part of the software. One group's code will need to interact with another group's code, but ''many parts depend upon other parts that haven't been written yet''. To solve this chicken-and-egg problem, the groups need to define ''interfaces'' for how others will be able to utilize their code, even before it is written. These interfaces are the interaction points for one group to utilize software that other groups write.
  
First, you will need to emerge {{Package|net-zope/zope}}:
+
Now we have added an additional layer of complexity to the software development project -- the effort of defining these interfaces. But we expect the investment to reap major rewards in mere days as this model of development will allow each group to actively develop their part of the application without waiting on another group to complete theirs first. While this could be done without ZCA, essentially all of ZCA is designed to help facilitate this style of development.
<console>
+
###i## emerge zope
+
</console>
+
  
Zope is now installed.
+
Now that you understand this, you will have a better appreciation for why ZCA is designed the way it is. It is trying to provide a framework for building larger projects that cannot be handled by a single developer. It does add some complexity to your project. This is intentional. You should only use ZCA if you expect that the additional complexity will be more than offset by improved productivity, maintainability and software quality that can arise from a component-based architecture. So, is it right for you? There is no one answer to this. The right answer will depend on the complexity of the project, your development team, and other factors.
  
== Project Skeleton ==
+
At least now you know what ZCA is designed for, so you will be able to evaluate it fairly :)
  
{{fancynote| Zope should be run by a regular user account, not as the root user.}}
+
Martin Aspeli has written [http://www.martinaspeli.net/articles/of-babies-and-bathwater-or-why-i-love-the-zope-component-architecture a defense of ZCA].
  
The first step in using Zope is to ensure that you are using a regular user account. As a regular user, create a new directory called <tt>zope_test</tt>:
+
== The Parts of ZCA ==
<console>
+
$##i## cd
+
$##i## mkdir zope_test
+
</console>
+
  
Now, enter the directory, and create an "instance", which is a set of files and directories that are used to contain a Zope project:
+
This section describes the "components" of ZCA itself. One of the most complicated things about ZCA is the new terminology that it uses for its various parts. Once you understand this terminology, ZCA is a lot more approachable. Also see [http://wiki.zope.org/zope3/ComponentArchitectureOverview]
<console>
+
$##i## cd zope_test
+
$##i## /usr/lib/zope-2.13/bin/mkzopeinstance
+
</console>
+
  
You will see the following output and will be prompted to answer a few questions:
+
; Components: A component is Python-based code, usually a class, that implements an ''Interface'' (defined below.)
<console>
+
;Interfaces: Interfaces are special Python classes that are declared for the sole purpose of describing functionality implemented by another class or module. They are registered with a component registry, and others can query the registry for components that implement a particular interface. Objects can advertise that they implement a particular interface. From a code perspective, interfaces are the heart of ZCA.
Please choose a directory in which you'd like to install
+
; Services: A service is generally a Python object that deals with components, and provides some functionality to your application. Think of components as "things", and services as parts of your code that take these "things" and perform useful actions with them.
Zope "instance home" files such as database files, configuration
+
;Component Registry: The Component Registry, also called a ''Site Manager'', is a directory of components, and acts as the central organizational hub of ZCA. It is a ''service'' (as defined above.) You register components with the component registry, and then others can query the registry for components that they need. There is a ''global'' component registry that is automatically created for you, and you can use the ZODB as a ''local'' component registry.
files, etc.
+
;Global Component Registry: The Global Component Registry a service that is automatically created when your ZCA-based application starts, and is populated with components based on your application's ZCML configuration files. Any part of your application can access the Global Component Registry. When your application shuts down, the Global Component Registry no longer exists. It will be re-initialized from ZCML when your application is started again.
 +
;Local Components: Local components are components that are not instantiated as part of the Global Component Registry. They are typically stored in the traditional Zope storage infrastructure, the ZODB, which also means that they are ''persistent'', in that they will survive multiple application starts/stops. Because they exist in the ZODB, Zope will use ''acquisition'' (a traditional Zope way of finding things in the ZODB) to retrieve local components when your application queries the ZODB.
 +
;ZCML: Pronounced "Z-camel", ZCML is an XML-based configuration file syntax that is used to register components with the global component registry. It is useful in that it provides a mechanism to add new components using configuration files rather than touching source code. It also provides a mechanism to easily extend ZCA code by overriding existing components with new implementations -- all without touching existing Python source code.
 +
;Adapters: Adapters are Python classes that allow new interfaces to be used with existing objects. More specifically, an Adapter class will contain an <tt>adapts()</tt> call in the class body that specifies that it ''adapts'' an interface, which means that you can pass any object that implements that interface to the adapter class' <tt>__init__()</tt> method, such as <tt>a = myAdapter(myobject)</tt>. <tt>MyAdapter</tt> now takes care of acting as a front-end for <tt>myobject</tt> so that any interfaces implemented by <tt>myAdapter</tt> will interact properly with <tt>myobject</tt>. In a sense, the adapter "eats" the adapted object.
 +
;Factories: A factory is something (technically, a ''Utility'', see below) that creates components and objects. It is like a higher-level version of a constructor. A Factory must be ''callable'' and implement the <tt>IFactory</tt> interface. Factories offer a convenient means to access component constructors via a component registry, rather than connecting to the constructor directly in Python code. It's important to note that ''ZCML creates factories for you automatically when you register components. This means that in many cases, you do not need to deal directly with Factory creation.''
 +
;Utilities: A utility is a Python object that you want to add to the component registry. It could be any type of Python object that another part of your application may need. A utility must implement at least one interface. [http://bluebream.zope.org/doc/1.0/howto/localsitemanager.html#how-does-it-usually-look-like The Zope Documentation] has some guidance on when it's appropriate to create a utility.
 +
;Multi-Adapters: Multi-adapters are adapters that have the ability to adapt one than more type of object.
 +
;Subscription Adapters: Adapters can be registered as subscription adapters, and then ''all'' adapters that adapt a particular object can be retrieved by calling the <tt>zope.component.subscribers()</tt> function. This is useful when doing things like validation, where you want to get back a bunch of adapters that apply to a specific object. Subscription adapters can return a value.
 +
;Handlers: Handlers are like subscription adapters, but they do not return a value to the caller. They go off, "handle" something, and then return.
 +
;Overrides: If you register multiple components with the same arguments, the last component registered will replace any previously-registered components. This behavior is used to replace existing components with new ones that may implement new functionality.
 +
;Invariants: Invariants are functions that you attach to your interfaces that throw exceptions when certain conditions aren't met. Invariants are invoked by calling the interface's <tt>validateInvariants</tt> method, passing the object to be validated as an argument.
  
Directory: instance
+
== ZCML ==
Please choose a username and password for the initial user.
+
These will be the credentials you use to initially manage
+
your new Zope instance.
+
  
Username: admin
+
The official specification for ZCML is viewable in the [http://apidoc.zope.org/++apidoc++/ Zope 3 APIDoc site], although this may not accurately reflect the API in Zope 2.13. In terms of explanatory, conceptual documentation, there does not seem to be anything good available, and this section will attempt to fill that gap.
Password: ****
+
Verify password: ****
+
</console>
+
  
Now, we will start our Zope instance:
+
For now, read the [http://codespeak.net/z3/five/manual.html Five Manual] and work through the <tt>[http://svn.zope.org/z3/Five/trunk/demo/?rev=46891#dirlist demos]</tt> directory.
<console>
+
$##i## cd instance
+
$##i## bin/runzope
+
</console>
+
 
+
Now that Zope is functional, you can go to the <tt>localhost:8080/manage</tt> URL in your web browser: you will be prompted to log in. Enter the username and password you specified. You are now logged in to the ZMI (Zope Management Interface.)
+
 
+
You can stop your application by pressing Control-C. In the future, you can start and stop your Zope instance using the following commands:
+
 
+
<console>
+
$##i## zopectl start
+
$##i## zopectl stop
+
</console>
+
 
+
{{fancynote| <tt>zopectl start</tt> will cause your instance to run in the background rather than consuming a shell console.}}
+
 
+
== First Project ==
+
 
+
We will create a single, very primitive Zope package, consisting of an Interface for a TODO class, and a TODO class.
+
 
+
Create the following files and directories relative to your project root:
+
 
+
* Create the directory <tt>lib/python/example</tt>.
+
* Create the file <tt>lib/python/example/__init__.py</tt> by typing <tt>touch lib/python/example/__init__.py</tt>.
+
* Create these files:
+
 
+
=== <tt>example-configure.zcml</tt> ===
+
 
+
This file registers the <tt>example</tt> directory you created in <tt>lib/python</tt> as a ''package'', so that it is seen by Zope. Edit <code>/etc/package-includes/example-configure.zcml</code>:
+
  
 
<pre>
 
<pre>
<include package="example" />
+
<?xml version="1.0" encoding="utf-8"?>
 +
<configure
 +
        xmlns="http://namespaces.zope.org/zope"
 +
        xmlns:browser="http://namespaces.zope.org/browser"
 +
        xmlns:five="http://namespaces.zope.org/five">
 +
</configure>
 
</pre>
 
</pre>
  
=== <tt>interfaces.py</tt> ===
+
Check out this excellent [http://plone.org/products/dexterity/documentation/manual/five.grok Zope Component Architecture basics with five.grok] tutorial from Martin Aspeli.
 
+
The following file defines the <tt>ITODO</tt> interface, and also uses some Zope Schema functions to define what kind of data we expect to store in objects that implement <tt>ITODO</tt>. Edit <code>/lib/python/example/interfaces.py</code> with your favorite text editor:
+
 
+
<syntaxhighlight lang="python">
+
from zope.interface import Interface
+
from zope.schema import List, Text, TextLine, Int
+
 
+
class ITODO(Interface):
+
    name = TextLine(title=u'Name', required=True)
+
    todo = List(title=u"TODO Items", required=True, value_type=TextLine(title=u'TODO'))
+
    daysleft = Int(title=u'Days left to complete', required=True)
+
    description = Text(title=u'Description', required=True)
+
</syntaxhighlight>
+
 
+
=== <tt>TODO.py</tt> ===
+
 
+
Now, we define <tt>TODO</tt> to be a ''persistent'' object, meaning it can be stored in the ZODB. We specify that it implements our previously-defined <tt>ITODO</tt> interface, and provide reasonable defaults for all values when we create a new TODO object. Edit <code>/lib/python/example/TODO.py<code> using your favorite text editor:
+
<syntaxhighlight lang="python">
+
from persistent import Persistent
+
from zope.interface import implements
+
from example.interfaces import ITODO
+
 
+
class TODO(Persistent):
+
    implements(ITODO)
+
    name = u''
+
    todo = []
+
    daysleft = 0
+
    description = u''
+
</syntaxhighlight>
+
 
+
=== <tt>configure.zcml</tt> ===
+
 
+
Create the <tt>/lib/python/example/configure.zcml</tt> configuration file:
+
<syntaxhighlight lang="xml">
+
<configure xmlns="http://namespaces.zope.org/zope"
+
    xmlns:five="http://namespaces.zope.org/five"
+
    xmlns:browser="http://namespaces.zope.org/browser">
+
</configure>
+
</syntaxhighlight>
+
 
+
== Debug Mode ==
+
 
+
We can test our first project by entering debug mode:
+
<console>
+
$##i## bin/zopectl debug
+
Starting debugger (the name "app" is bound to the top-level Zope object)
+
</console>
+
 
+
Now, let's try creating a new TODO object and writing it out to a ZODB database:
+
<console>
+
>>> from ZODB import FileStorage, DB
+
>>> storage = FileStorage.FileStorage('mydatabase.fs')
+
>>> db = DB(storage)
+
>>> connection = db.open()
+
>>> import transaction
+
>>> root = connection.root()
+
>>> from example.TODO import TODO
+
>>> a = TODO
+
>>> a.name = u'My TODOs'
+
>>> a.TODOS = [ u'Do Laundry', u'Wash Dishes' ]
+
>>> a.daysleft = 1
+
>>> a.description = u'Things I need to do today.'
+
>>> root[u'today'] = a
+
>>> transaction.commit()
+
</console>
+
  
[[Category:HOWTO]]
+
[[Category:Zope]]
 +
[[Category:Developer]]
 
[[Category:Featured]]
 
[[Category:Featured]]

Latest revision as of 15:53, 14 February 2012

This page describes the Zope Component Architecture, or ZCA for short.

ZCA: What is it?

The Zope Component Architecture is a Framework that arose out of Zope 3 development, and is now integrated into Zope 2. It is used by various software projects, and doesn't even require Zope to run. You can use it natively with Python. It's part of Zope, but it's not Zope.

Why Should I Use It?

You shouldn't. Well, I mean, don't use it unless you see the value in it, or if you are using a software project that uses it already. Due to the fact that it is a framework, it does impose a paradigm on how you develop Python code and you need to determine if that paradigm works for your particular situation, or if it doesn't.

The Paradigm

In one phrase, the paradigm implemented by the ZCA framework is that of a "component architecture", which in itself may not mean anything to you, but it is easy to understand.

If you are reading this, you have probably developed some Python software. Now imagine if you had to lead a team that is developing a complex Python software application. This type of project presents new challenges that you may not experience as an individual developer.

For one, different members of the team have different skill sets. You also want the team to be able to work in parallel rather than have some members of the team sit idle waiting for parts they depend upon to be completed. Ideally, you would want to split the application into logical parts and then clearly define who is doing what part of the software and what each team is delivering. But when the software is complete, it needs to work together as an integrated unit.

Clearly, you will need to think about the software architecture. But it can also be helpful to have a framework your team can use that helps them to split the application into logical parts, develop these parts in parallel, and then allow these various parts to interact once they are done. This is what a component architecture is typically used for. It supports this style of development.

Interfaces

You decide to split your team into smaller groups, and each group will tackle one part of the software. One group's code will need to interact with another group's code, but many parts depend upon other parts that haven't been written yet. To solve this chicken-and-egg problem, the groups need to define interfaces for how others will be able to utilize their code, even before it is written. These interfaces are the interaction points for one group to utilize software that other groups write.

Now we have added an additional layer of complexity to the software development project -- the effort of defining these interfaces. But we expect the investment to reap major rewards in mere days as this model of development will allow each group to actively develop their part of the application without waiting on another group to complete theirs first. While this could be done without ZCA, essentially all of ZCA is designed to help facilitate this style of development.

Now that you understand this, you will have a better appreciation for why ZCA is designed the way it is. It is trying to provide a framework for building larger projects that cannot be handled by a single developer. It does add some complexity to your project. This is intentional. You should only use ZCA if you expect that the additional complexity will be more than offset by improved productivity, maintainability and software quality that can arise from a component-based architecture. So, is it right for you? There is no one answer to this. The right answer will depend on the complexity of the project, your development team, and other factors.

At least now you know what ZCA is designed for, so you will be able to evaluate it fairly :)

Martin Aspeli has written a defense of ZCA.

The Parts of ZCA

This section describes the "components" of ZCA itself. One of the most complicated things about ZCA is the new terminology that it uses for its various parts. Once you understand this terminology, ZCA is a lot more approachable. Also see [1]

Components
A component is Python-based code, usually a class, that implements an Interface (defined below.)
Interfaces
Interfaces are special Python classes that are declared for the sole purpose of describing functionality implemented by another class or module. They are registered with a component registry, and others can query the registry for components that implement a particular interface. Objects can advertise that they implement a particular interface. From a code perspective, interfaces are the heart of ZCA.
Services
A service is generally a Python object that deals with components, and provides some functionality to your application. Think of components as "things", and services as parts of your code that take these "things" and perform useful actions with them.
Component Registry
The Component Registry, also called a Site Manager, is a directory of components, and acts as the central organizational hub of ZCA. It is a service (as defined above.) You register components with the component registry, and then others can query the registry for components that they need. There is a global component registry that is automatically created for you, and you can use the ZODB as a local component registry.
Global Component Registry
The Global Component Registry a service that is automatically created when your ZCA-based application starts, and is populated with components based on your application's ZCML configuration files. Any part of your application can access the Global Component Registry. When your application shuts down, the Global Component Registry no longer exists. It will be re-initialized from ZCML when your application is started again.
Local Components
Local components are components that are not instantiated as part of the Global Component Registry. They are typically stored in the traditional Zope storage infrastructure, the ZODB, which also means that they are persistent, in that they will survive multiple application starts/stops. Because they exist in the ZODB, Zope will use acquisition (a traditional Zope way of finding things in the ZODB) to retrieve local components when your application queries the ZODB.
ZCML
Pronounced "Z-camel", ZCML is an XML-based configuration file syntax that is used to register components with the global component registry. It is useful in that it provides a mechanism to add new components using configuration files rather than touching source code. It also provides a mechanism to easily extend ZCA code by overriding existing components with new implementations -- all without touching existing Python source code.
Adapters
Adapters are Python classes that allow new interfaces to be used with existing objects. More specifically, an Adapter class will contain an adapts() call in the class body that specifies that it adapts an interface, which means that you can pass any object that implements that interface to the adapter class' __init__() method, such as a = myAdapter(myobject). MyAdapter now takes care of acting as a front-end for myobject so that any interfaces implemented by myAdapter will interact properly with myobject. In a sense, the adapter "eats" the adapted object.
Factories
A factory is something (technically, a Utility, see below) that creates components and objects. It is like a higher-level version of a constructor. A Factory must be callable and implement the IFactory interface. Factories offer a convenient means to access component constructors via a component registry, rather than connecting to the constructor directly in Python code. It's important to note that ZCML creates factories for you automatically when you register components. This means that in many cases, you do not need to deal directly with Factory creation.
Utilities
A utility is a Python object that you want to add to the component registry. It could be any type of Python object that another part of your application may need. A utility must implement at least one interface. The Zope Documentation has some guidance on when it's appropriate to create a utility.
Multi-Adapters
Multi-adapters are adapters that have the ability to adapt one than more type of object.
Subscription Adapters
Adapters can be registered as subscription adapters, and then all adapters that adapt a particular object can be retrieved by calling the zope.component.subscribers() function. This is useful when doing things like validation, where you want to get back a bunch of adapters that apply to a specific object. Subscription adapters can return a value.
Handlers
Handlers are like subscription adapters, but they do not return a value to the caller. They go off, "handle" something, and then return.
Overrides
If you register multiple components with the same arguments, the last component registered will replace any previously-registered components. This behavior is used to replace existing components with new ones that may implement new functionality.
Invariants
Invariants are functions that you attach to your interfaces that throw exceptions when certain conditions aren't met. Invariants are invoked by calling the interface's validateInvariants method, passing the object to be validated as an argument.

ZCML

The official specification for ZCML is viewable in the Zope 3 APIDoc site, although this may not accurately reflect the API in Zope 2.13. In terms of explanatory, conceptual documentation, there does not seem to be anything good available, and this section will attempt to fill that gap.

For now, read the Five Manual and work through the demos directory.

<?xml version="1.0" encoding="utf-8"?>
<configure
        xmlns="http://namespaces.zope.org/zope"
        xmlns:browser="http://namespaces.zope.org/browser"
        xmlns:five="http://namespaces.zope.org/five">
</configure>

Check out this excellent Zope Component Architecture basics with five.grok tutorial from Martin Aspeli.