Copyright (C) 2007,2008 Baiju M <baiju.m.mail AT gmail.com>.
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or (at your option) any later version published by the Free Software Foundation.
The source code in this document is subject to the provisions of the Zope Public License, Version 2.1 (ZPL).
THE SOURCE CODE IN THIS DOCUMENT AND THE DOCUMENT ITSELF IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE.
Acknowledgements
Many people have helped me to write this book. The initial draft was reviewed by my colleague Brad Allen. When I announced this book through my blog, I got many encouraging comments to proceed with this work. Kent Tenney edited most parts of the book, he also rewrote the example application. Many others sent me fixes and comments including, Lorenzo Gil Sanchez, Michael Haubenwallner, Nando Quintana, Stephane Klein, Tim Cook, Kamal Gill and Thomas Herve. Lorenzo translated this work to Spanish and Stephane translated it to French. Thanks to all !
Contents
Developing a large software system is always very complicated. An object oriented approach to analysis, design and programming has been shown to be well suited for dealing with large systems. Component based design, and programming using components are becoming very popular these days. There are many frameworks for supporting component based design in different languages, some are even language neutral. Examples of these are Microsoft's COM and Mozilla's XPCOM.
Zope Component Architecture (ZCA) is a Python framework for supporting component based design and programming. It is very well suited to developing large Python software systems. The ZCA is not specific to the Zope web application server: it can be used for developing any Python application. Maybe it should be called the Python Component Architecture.
The ZCA is all about using Python objects effectively. Components are reusable objects with introspectable interfaces. A component provides an interface implemented in a class, or any other callable object. It doesn't matter how the component is implemented, the important part is that it comply with its interface contracts. Using ZCA, you can spread the complexity of systems over multiple cooperating components. It helps you to create two basic kinds of components: adapter and utility.
There are two core packages related to the ZCA:
zope.interface is used to define the interface of a component.
zope.component deals with registration and retrieval of components.
Remember, the ZCA is not about the components themselves, rather it is about creating, registering, and retrieving components. Remember also, an adapter is a normal Python class (or a factory in general) and utility is a normal Python callable object.
The ZCA framework is developed as part of the Zope 3 project. As noted earlier, it is a pure Python framework, so it can be used in any kind of Python application. Currently both Zope 3 and Zope 2 projects use this framework extensively. There are many other projects including non-web applications using it [1].
原版地址:http://www.muthukadan.net/docs/zca.html
润普:http://docs.everydo.com/zope3
Zope 组件架构全面指导
作者:Baiju M
版本:0.5.6
打印版本:http://www.lulu.com/content/1561045
在线PDF:http://www.muthukadan.net/docs/zca.pdf
版权所有 (C) 2007,2008 Baiju M <baiju.m.mail AT gmail.com>.
内容目录
1.1 介绍
开发一个大型软件系统通常是非常复杂的。 用于分析、设计和编程的面向对象方法,已显示出非常适合于处理大型系统。 近来,基于组件的设计、和使用组件编程正在变成非常流行。 在不同的编程语言中,有很多支持基于组件设计的框架。 有些甚至是和语言中立的,比如Microsoft的COM和Mozilla的XPCOM.
(译者:组件架构适合大型的复杂系统,所以很多简单应用,可能并不需要这个。)
Zope组件架构(ZCA) 是一个支持基于组件设计和编程的Python框架。 它非常适合于开发大型Python软件系统。 ZCA并非为Zope的网站应用服务器所专用:它能够被用来开发任何Python应用程序。 或许它应该被称为是 Python组件架构 。
ZCA其实就是讲如何更有效地使用Python对象。组件是提供可自省接口的可重用对象。 每个组件提供的接口,可以是在类Class中实现,或者其他任何可调用的对象中实现。 它不管组件是怎样实现的,重要的是它遵守它的接口的约定。 使用ZCA,你能把系统的复杂性分布到多个相互协作的组件上(译者:每个相对简单了)。 他帮助你创建2中基本的组件类型: 适配器adapter 和 工具utility .
ZCA相关的核心包有2个:
zope.interface 用来定义组件的接口。
zope.component 用来处理组件的注册和检索。
记住:ZCA不仅仅是关于组件自身,它更包括组件的创建、注册和检索。 再次记住: 适配器adapter 是一个普通的Python类Class(或一个广义的工厂),而utility是一个普通的Python可调用的对象。
ZCA框架是作为Zope3项目的一部分进行开发的。 如前面提及的,这是一个纯Python的框架,它能够被应用于任何类型的Python应用程序中。 目前Zope3和Zope2项目都广泛地使用这个框架,还有很多其他的项目在使用,包括非网站应用 [1].
[1] http://wiki.zope.org/zope3/ComponentArchitecture
1.2 历史概要
ZCA框架项目作为Zope3项目的一部分,开始于2001年。 ZCA是总结在开发Zope2这个大型软件系统的教训的基础上逐渐成长起来的。 Jim Fulton是这个项目的项目领导者。很多其他人参与了系统设计和实现, 包括Stephan Richter, Philipp von Weitershausen, Guido van Rossum (亦称为:Python BDFL), Tres Seaver, Phillip J Eby and Martijn Faassen等。
最初,ZCA定义了几个附加组件: 服务(services) 和 视图(views) , 但开发者们最终认识到 工具utility 可代替 服务(services) , 多适配multi-adapter 可代替 视图(views) 。 现在,ZCA只有一个非常小量的核心组件类型: 工具utilities、适配器adapters, 订阅器subscribers和处理器handlers. 而事实上,订阅器subscribers和处理器handlers是两种特殊的adapters类型。
在Zope3.2发布周期期间,Jim Fulton提出了一个大的ZCA简化提议 [2]. 由于这个简化,一个新的简单接口(IComponentRegistry),被用来注册全局的和局部的组件。
[2] http://wiki.zope.org/zope3/LocalComponentManagementSimplification
zope.component 包中有很一个长的依赖关系列表,其中很多对于非Zope3的应用程序来说并不需要。 于是在PyCon 2007期间,Jim Fulton添加了setuptools的 extras_require 特性 , 将核心ZCA功能和附加特性(add-on features)分离 [#extras]_。
[3] http://peak.telecommunity.com/DevCenter/setuptools#declaring-dependencies
现在,ZCA项目是一个独立的项目,具有自己的发布周期和Subversion版本仓库。 然而,问题(issues)和故障(bugs)仍然是作为Zope3项目的一部分在跟踪处理 [4], 而且使用主要的zope-dev邮件列表作为开发讨论 [5]. 另外Zope3还有一个通用的用户邮件列表(zope3-users),可以用来讨论任何ZCA疑问 [6] 。
[4] https://bugs.launchpad.net/zope3
[5] http://mail.zope.org/mailman/listinfo/zope-dev
[6] http://mail.zope.org/mailman/listinfo/zope3-users
1.3 安装
zope.component 包和 zope.interface 包是Zope组件架构的核心。 他们提供定义组件、注册组件和查找组件的工具。 zope.component 包及其依赖包都可在Python包索引(PyPI)中找到egg格式包 [7].
[7] Repository of Python packages: http://pypi.python.org/pypi
你能够使用easy_install安装zope.component及其依赖包 [8]
$ easy_install zope.component
[8] http://peak.telecommunity.com/DevCenter/EasyInstall
这个命令将从PyPI下载 zope.component 及其依赖包,并且在你的Python路径中安装它们。
或者,你也能从PyPI下载 zope.component 及其依赖包然后安装它们。安装包的顺序在下面。 在Windows上,你可能需要 zope.interface``和 ``zope.proxy 的二进制包。
zope.interface
zope.proxy
zope.deferredimport
zope.event
zope.deprecation
zope.component
在下载完这些包后,要安装它们,你可以使用带有参数的 easy_install 命令将其当作eggs安装 (你也可以让所有的eggs放在同一条命令里。):
$ easy_install /path/to/zope.interface-3.4.x.tar.gz
$ easy_install /path/to/zope.proxy-3.4.x.tar.gz
...
你也能够逐个解压这些包后安装。比如:
$ tar zxvf /path/to/zope.interface-3.4.x.tar.gz
$ cd zope.interface-3.4.x
$ python setup.py build
$ python setup.py install
这些方法将安装ZCA到系统Python路径的site-packages目录中,这可能会引发问题。 在Zope3邮件列表中,Jim Fulton建议不要使用系统Python路径 [9]. 你可以使用virtualenv和(或)zc.buildout来处理任何Python包,这对部署也很好。
[9] http://article.gmane.org/gmane.comp.web.zope.zope3/21045
1.4 代码体验
在Python中有两个不错的包,可用来建立开发Python应用程序的工作环境。 它们是Ian Biking创建的 virtualenv ,和Jim Fulton创建的 zc.buildout. 你也可以结合2个一起使用。 你能够使用这两种包将 zope.component 及其其他依赖包安装在一个独立的工作环境中。 即便是做python代码实验,这也是一个好习惯,并且精通这些工具将有助于开发和部署应用程序。
可以使用easy_install来安装virtualenv:
$ easy_install virtualenv
然后创建一个类似下面的新环境
$ virtualenv myve
这将在myve目录下创建一个新的虚拟环境。现在,在myve目录中,你能够使用easy_install,将zope.component及其其他依赖包安装进myve/bin目录中:
$ cd myve
$ ./bin/easy_install zope.component
现在你可以从myve/bin目录下的新python解释器(interpreter)导入zope.interface和zope.component:
$ ./bin/python
这个命令将给你一个Python提示符,在这里你可以用来运行这本书上的程序。
使用带zc.recipe.egg的zc.buildout的方法,你可以创建带指定的Python eggs的Python解释器(interpreter)。首先,使用easy_install命令安装zc.buildout。(你也可以在虚拟环境中这样做。) 如要创建用Python eggs的新buildout进行实验,首先要创建一个目录并且使用buildout初始命令初始化它:
$ mkdir mybuildout
$ cd mybuildout
$ buildout init
现在,新建的mybuildout目录就是一个buildout. 缺省的buildout配置文件是buildout.cfg. 在初始化之后,将包括这些内容:
[buildout]
parts =
你可改变成:
[buildout]
parts = py
[py]
recipe = zc.recipe.egg
interpreter = python
eggs = zope.component
现在,在mybuildout/bin目录中,不带参数运行buildout命令。 这将在mybuildout/bin下创建一个新的Python解释器(interpreter):
$ ./bin/buildout
$ ./bin/python
这个命令将给你一个Python提示符,在这里你可以用来运行这本书上的程序。
让我们来考虑为住在酒店中的顾客进行注册的业务应用。用Python语言可以有很多种方法来实现这一需求。我们先来简单看看过程式的开发过程,然后是最基本的面向对象方式。当我们研究后一种开发方法时,我们将会看到如何从“适配器(adapter)”、“接口(interface)”这两种经典设计模式中获益。这将把我们带到Zope组件架构(Zope Component Architecture)的世界里。
2.2 过程式开发过程
在任何业务应用中,数据存储都是十分关键的。为了简单,这里使用一个Python字典来作为存储引擎。我们给这个字典生成唯一的id作为键,与之关联的值则是一个关于预订详细信息的字典。
>>> bookings_db = {} #键:唯一的Id,值:存储细节信息的字典
这个例子的最小实现需要一个用来传递预订详情的函数,以及一个提供唯一id用作存储引擎字典键值的辅助函数。
我们可以像这样得到这个唯一的id:
>>> def get_next_id():
... db_keys = bookings_db.keys()
... if db_keys == []:
... next_id = 1
... else:
... next_id = max(db_keys) + 1
... return next_id
就像你看到的那样,“get_next_id”函数的实现是十分简单的。这个函数取得键值的列表并且检查是否是一个空列表:如果列表为空,那么这就是我们的第一个预订,因此返回“1”;如果列表不为空,我们就给列表中的最大元素加1来作为函数的返回值。
现在我们将使用下面的函数来创建bookings_db字典的条目:
>>> def book_room(name, place):
... next_id = get_next_id()
... bookings_db[next_id] = {
... 'name': name,
... 'room': place
... }
酒店预订管理的应用需求可能会要考虑额外的数据:
电话号码
房间选项
支付方式
……
以及管理这些数据的代码:
取消一个预订
更新一个预订
为房间付款
数据持久化
保证数据安全
……
如果我们继续进行这个过程式编程的例子,我们将创建很多函数,并且在这些函数之间把数据传来传去。当需求改变或者增加时,代码将更难维护、Bug会更难发现和修正。
我们将在此结束我们关于过程式开发的讨论。使用对象能更容易地提供数据持久化、设计上的灵活性、以及代码的可测试性。
2.3 面向对象开发过程
我们关于面向对象设计的讨论将引入“类”来封装数据及管理数据的代码。
我们的主类是“FrontDesk”。FrontDesk或者其他它委派(delegate)的类将知道如何管理酒店的数据。我们将创建FrontDesk类的实例来把这些知识应用到运营酒店业务中去。
经验证明可以用对象来统一处理代码和数据两方面的需求,我们最后将给出一个更容易理解、测试和修改的设计。
让我们看看FrontDesk类的实现细节:
>>> class FrontDesk(object):
...
... def book_room(self, name, place):
... next_id = get_next_id()
... bookings_db[next_id] = {
... 'name': name,
... 'place': place
... }
在这一实现中,FrontDesk对象(也就是FrontDesk类的一个实例)是能够处理预订的。我们可以像这样来使用它:
>>> frontdesk = FrontDesk()
>>> frontdesk.book_room("Jack", "Bangalore")
任何实际项目都可能包括需求变更。在这个案例中,管理部门决定每个顾客都必须提供电话号码,因此我们必须修改代码。
我们可以给book_room方法增加一个会被添加到取值字典中的参数来实现这个需求:
>>> class FrontDesk(object):
...
... def book_room(self, name, place, phone):
... next_id = get_next_id()
... bookings_db[next_id] = {
... 'name': name,
... 'place': place,
... 'phone': phone
... }
除了要向新的模型迁移数据外,我们还必须修改所有对FrontDesk的调用。如果我们把顾客的细节信息抽象成一个guest对象,并且用这个对象来完成注册,那么对代码的改动可以更小。也就是说我们只要修改guest对象的细节就可以了,而对FrontDesk的调用则不必修改。
现在我们有:
>>> class FrontDesk(object):
...
... def book_room(self, guest):
... next_id = get_next_id()
... bookings_db[next_id] = {
... 'name': guest.name,
... 'place': guest.place,
... 'phone': guest.phone
... }
如果出现需求变更的话,我们还是得修改代码的。这是不可避免的,但是我们的目标是将这样的改动最小化,因而也就提高了可维护性。
注解
在写代码的时候,能随意修改而不需要担心破坏了整个应用程序是很重要的。获得所需的即时反馈可以通过自动化测试来实现。有了高质量的测试(以及合理的版本控制),你就能自由自在地进行或大或小的修改。Kent Beck写的《Extreme Programming Explained》是关于这一编程法则的优秀资料。
通过引入guest对象,你省去了不少打字的功夫。更重要的是,guest对象所提供的这种抽象使得系统更简单并且更容易理解。因此,代码也就更容易重构和维护了。
2.4 适配器模式
现实应用中frontdesk对象需要处理诸如取消或是更新预订这类琐事。在现在的设计方案中,我们每次调用cancel_booking和update_booking这些方法时都得把guest对象传递给frontdesk。
我们把guest对象传递给FrontDesk.__init__()方法,使之成为实例的一个属性,就能避免这种情况:
>>> class FrontDeskNG(object):
...
... def __init__(self, guest):
... self.guest = guest
...
... def book_room(self):
... guest = self.guest
... next_id = get_next_id()
... bookings_db[next_id] = {
... 'name': guest.name,
... 'place': guest.place,
... 'phone': guest.phone
... }
我们获得的这个解决方案是一种著名的设计模式——适配器(adapter)。通常,适配器包含适配源:
>>> class Adapter(object):
...
... def __init__(self, adaptee):
... self.adaptee = adaptee
这种模式在处理依赖以下因素的实现细节时会很有用:
修改客户需求
存储需求 (ZODB, RDBM, XML ...)
输出需求 (HTML, PDF, plain text ...)
标记渲染 (ReST, Markdown, Textile ...)
ZCA使用适配器和组件注册表来实现通过配置修改代码实现细节的能力。
就像我们将在ZCA适配器章节看到的那样,配置实现细节的可能性提供了十分有用的能力:
在不同具体实现间切换的能力
在需要时添加新实现的能力
提高遗留代码和ZCA代码的重用性
这些能力使代码变得灵活、可扩展、可重用。当然这也有一定代价,维护组件注册表会把应用程序的复杂性增加一些。如果应用永远不需要这样的特性,那么是不需要使用ZCA的。
我们现在准备从接口(interfaces)开始学习Zope Component Architecture了。
path/to/zope/interface 目录下的 README.txt [1] 中对接口定义如下:
Interfaces are objects that specify (document) the external behavior
of objects that "provide" them. An interface specifies behavior
through:
- Informal documentation in a doc string
- Attribute definitions
- Invariants, which are conditions that must hold for objects that
provide the interface
翻译如下:
接口是一类特殊的对象,它规范(并文档化)了所有“提供”这个接口的对象的外部行为。
一个接口通过如下方式来规范行为:
- 在文档字符串里提供详细的文档
- 定义属性
- 不变量,也就是每个提供这个接口的对象所必须满足的条件
在Gang of Four所编写的经典软件工程书籍《设计模式》 [2] 中推荐“要面向接口而不要面向实现编程”。规范的进行接口定义有助于更好的理解系统,更重要的是,使用接口可以让你充分利用ZCA。
[1] 在Zope的代码树中有大量的README.txt,这些文件提供了大量的高质量文档。
[2] http://en.wikipedia.org/wiki/Design_Patterns
一个接口定义了一个对象的特性,包括对象的行为和对象的能力。接口定义了对象能做些 什么 ,但是要想知道是 怎么 做的就需要去查看对象的实现。
在其他领域中常见的和接口类似的词语是 契约 和 蓝图 ,他们分别是法律和建筑中用来描述一组规范的术语。
在一些现代编程语言(比如Java、C#、VB.NET等)中,接口是语言本身直接提供的一个特性。由于Python不提供接口,ZCA把接口实现为一个用来作为基类继承的<<元类>><<meta-class>>。
这是一个传统的Hello world类型的例子:
>>> class Host(object):
...
... def goodmorning(self, name):
... """Say good morning to guests"""
...
... return "Good morning, %s!" % name
在上面的类中定义了一个 goodmorning 方法,如果调用一个用这个类创建的对象中的 goodmorning 方法,那么这个方法将会返回 Good morning, ...!
>>> host = Host()
>>> host.goodmorning('Jack')
'Good morning, Jack!'
这里 host 是你的代码中真正使用的对象。如果想要了解实现细节,那么就需要通过查看 Host 的源码或者利用API [3] 文档工具检查 Host 。
[3] http://en.wikipedia.org/wiki/Application_programming_interface
现在我们开始使用ZCA的接口,按照上面给出的类定义,你可以用如下方式来定义接口:
>>> from zope.interface import Interface
>>> class IHost(Interface):
...
... def goodmorning(guest):
... """Say good morning to guest"""
如上所见,接口从 zope.interface.Interface 继承而来。这个使用(也许是不恰当的?)Python的 class 语句的方法就是ZCA定义接口的方式,在接口名字前面添加一个 I 前缀是一个很常见的习惯做法。
3.2 声明接口
在上一节中你已经了解了如何使用 zope.interface.Interface 来声明接口,在这一节中将会详细介绍这一部分。
考虑如下的接口例子:
>>> from zope.interface import Interface
>>> from zope.interface import Attribute
>>> class IHost(Interface):
... """A host object"""
...
... name = Attribute("""Name of host""")
...
... def goodmorning(guest):
... """Say good morning to guest"""
接口 IHost 有两个属性, name 和 goodmoring 。回想一下,至少在Python中,方法也是属性。属性 name 是采用 zope.interface.Attribute 类来定义的。当你给接口 IHost 添加一个属性 name 的时候,你不需要设置任何的初始值,在这里定义属性 name 只是为了声明所有的这个接口的实现都有一个名为 name 的属性,在这种情况下,你甚至不需要指定这个属性的类型。 Attribute 可以接收一个文档字符串作为第一个参数。
另一个属性 goodmorning 是一个采用函数声明来定义的方法。注意一下,在接口中不需要 self 参数,这是因为 self 是类的实现细节。举例来说,我们可以用一个模块来实现这个接口。如果有一个模块实现了这个接口,那么这个模块中必须定义一个属性 name 和一个函数 goodmorning ,并且这个 goodmorning 函数接收一个参数。
现在将要介绍接口、类、和对象之间的关联。对象是真实存在的实体,对象是类的实现,而接口提供了对象的规范定义,类只是实现细节。这就是为什么你要面向接口而不是面向实现编程的原因。
为了了解后面的概念,你必须熟悉两个术语,一个是 提供 ,另一个是 实现 。对象提供接口而类实现接口。换句话说就是对象提供了定义它们的类所实现的接口。在上面的例子中, host (对象)提供了 IHost (接口), Host (类) 实现了 IHost (接口)。一个对象可以提供多个接口,一个类也可以显示多个接口。对于对象来说,除了定义它们的类所实现的接口之外,还可以直接提供其他接口。
注意
类提供了对象的实现细节。在Python中,类是一个可调用对象。有人可能会问,难道其他可调用对象就不能实现一个接口吗?这是可以的。对于任意一个可调用对象,如果你希望声明这个可调用对象生成的对象提供了某些接口,那么你可以通过声称这个可调用对象实现了这些接口的方式来完成,这类的可调用对象一般被称作工厂。由于函数也是可调用对象,所以一个函数也可以是一个接口的实现者。
3.3 实现接口
要声明一个类实现了某个特定的接口,可以在 class 语句中使用zope.interface.implements函数。
在下面的例子, Host 实现了 IHost
>>> from zope.interface import implements
>>> class Host(object):
...
... implements(IHost)
...
... name = u''
...
... def goodmorning(self, guest):
... """Say good morning to guest"""
...
... return "Good morning, %s!" % guest
注意
如果你想知道 implements 函数是怎么工作的,可以参考James Henstridge撰写的博客文章( http://blogs.gnome.org/jamesh/2005/09/08/python-class-advisors/ )。在adapter一节,有一个工作原理类似的 adapts 函数。
由于 Host 实现了 IHost , Host 的实例也就提供了 IHost 。有一些辅助方法可以<<内省>><<introspect>>这些声明。这个声明也可以写在类的外面,如果你没有在前面的例子中编写 interface.implements(IHost) 这条语句,那么在类的定义后面,你可以输入:
>>> from zope.interface import classImplements
>>> classImplements(Host, IHost)
3.4 范例重览
现在,回到之前的范例应用上来,下面是前台对象的接口定义:
>>> from zope.interface import Interface
>>> class IDesk(Interface):
... """A frontdesk will register object's details"""
...
... def register():
... """Register object's details"""
...
首先,从 zope.interface 模块中导入了 Interface 类。如果你定义了一个这个接口类的子类,那么从Zope component architecture的角度来看,这个类就是一个接口。就像前面已经提示过的,一个接口可以被类或者任意的其他可调用对象所实现。
这里定义的前台接口是 IDesk 。这个接口的文档字符串提供了对象的概述。通过在接口中定义一个方法,你保证了所有的组件都提供了同样名字的方法。在接口的方法的定义中,第一个参数绝对不能是 self ,这是由于一个接口绝对不会被实例化,而且接口的方法也不会被调用,接口类只是用来为那些声称实现它的普通类提供必须要实现的属性和方法的参考文档的,而 self 是一个实现上的细节,不需要被文档化。
前面已经说过,接口也可以被用来规定普通的属性:
>>> from zope.interface import Interface
>>> from zope.interface import Attribute
>>> class IGuest(Interface):
...
... name = Attribute("Name of guest")
... place = Attribute("Place of guest")
在这个接口中,房客对象有两个被文档所规定的属性。一个接口中可以同时规定属性和方法,接口可以被类、模块或者其他任何对象所实现。比如一个函数可以动态创建一个组件并返回,在这种情况下,这个函数就是某个接口的实现者。
现在你已经知道什么是接口和如何定义并使用接口,在下面一章中将你将了解到如何使用接口去定义一个<<适配器>><<adapter>>对象。
3.5 <<标识>><<marker>>接口
一个接口可以被用来声明某个特定对象属于一个特殊的类型。一个没有任何属性和方法的接口被称作 <<标识>><<marker>>接口 。
下面是一个 <<标识>><<marker>>接口
>>> from zope.interface import Interface
>>> class ISpecialGuest(Interface):
... """A special guest"""
这个接口可以用来声明一个对象是一个特殊房客。
3.6 不变量
在某些情况下,你可能需要给你的组件定义某些规则,这些规则会涉及到组件的一个或多个普通属性。这样的规则被叫做 不变量 。你可以在接口中使用 zope.interface.invariant 来给你的对象设定不变量。
考虑一个简单的例子,比如人物对象,每个人物都有 name 、 email 、和 phone 属性。那么要如何实现一个保证 email 和 phone 中至少有一个存在的校验规则呢?
首先,你必须完成一个可调用对象,可以是一个简单的函数或者是一个可调用的类实例,比如:
>>> def contacts_invariant(obj):
...
... if not (obj.email or obj.phone):
... raise Exception(
... "At least one contact info is required")
然后按下面的做法定义一个人物对象的接口。使用 zope.interface.invariant 函数来设定不变量:
>>> from zope.interface import Interface
>>> from zope.interface import Attribute
>>> from zope.interface import invariant
>>> class IPerson(Interface):
...
... name = Attribute("Name")
... email = Attribute("Email Address")
... phone = Attribute("Phone Number")
...
... invariant(contacts_invariant)
现在使用接口的 validateInvariants 方法来进行校验:
>>> from zope.interface import implements
>>> class Person(object):
... implements(IPerson)
...
... name = None
... email = None
... phone = None
>>> jack = Person()
>>> jack.email = u"jack@some.address.com"
>>> IPerson.validateInvariants(jack)
>>> jill = Person()
>>> IPerson.validateInvariants(jill)
Traceback (most recent call last):
...
Exception: At least one contact info is required
从上面的执行结果可以看出, jack 对象没有抛出任何异常就通过了校验,而 jill 对象就无法通过不变量约束的校验,并因此抛出了异常。
4.1 实现
这一节将会详细介绍适配器。就像前面说过的那样,Zope组件架构可以帮助程序员更有效的使用Python对象,适配器是这一架构中的一个基本组件。适配器组件是拥有完善接口定义的Python对象。
使用 zope.component 包中的 adapts 函数来声明一个类是适配器。下面是一个显式声明了接口的新的 FrontDeskNG 适配器:
>>> from zope.interface import implements
>>> from zope.component import adapts
>>> class FrontDeskNG(object):
...
... implements(IDesk)
... adapts(IGuest)
...
... def __init__(self, guest):
... self.guest = guest
...
... def register(self):
... guest = self.guest
... next_id = get_next_id()
... bookings_db[next_id] = {
... 'name': guest.name,
... 'place': guest.place,
... 'phone': guest.phone
... }
上面定义的是对应 IDesk 的适配器,这个适配器适用于 IGuest 对象 [1] 。 IDesk 接口由 FrontDeskNG 类所实现,因此,这个类的实例提供了 IDesk 接口。
[1] 译者注:这里的 IGuest 就是适配者 adpatee
>>> class Guest(object):
...
... implements(IGuest)
...
... def __init__(self, name, place):
... self.name = name
... self.place = place
>>> jack = Guest("Jack", "Bangalore")
>>> jack_frontdesk = FrontDeskNG(jack)
>>> IDesk.providedBy(jack_frontdesk)
True
刚才我们已经创建了一个名为 FrontDeskNG 的适配器,同样的,我们也可以创建用于处理不同房客登记流程的其他适配器。
4.2 注册
在使用一个适配器组件之前,你必须在一个也被称作站点管理器的组件注册表中注册该适配器。通常情况下,站点管理器是属于一个站点的。站点和站点管理器在开发Zope 3应用的时候会更重要,现在你只需要了解全局站点和全局站点管理器(或者叫组件注册表)就可以了。全局站点管理器是保存在内存之中,而局部站点管理器是持久化的。
在注册组件之前,首先需要获得全局站点管理器:
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> gsm.registerAdapter(FrontDeskNG,
... (IGuest,), IDesk, 'ng')
为了获得全局站点管理器,需要调用 zope.component 包中的 getGlobalSiteManager 函数。事实上,全局站点管理器是作为 zope.component 包的一个属性( globalSiteManager )出现的,因此,我们也可以直接使用 zope.component.globalSiteManager 属性。像前面的例子所写的那样,把适配器注册成组件,需要调用组件注册表的 registerAdapter 方法。第一个参数是适配器的类或者工厂。第二个参数是一个由多个适配者(adaptee 也就是适配器所适配的对象)所组成的tuple;在这个例子中,只适配了 IGuest 对象。第三个参数是由该适配器组件所实现的接口。第四个参数是个可选参数,提供了该适配器的名称;如果给出了适配器名称,那么该适配器就是一个已命名适配器,如果没有给出适配器名称,那么该参数默认为一个空字符串( '' )。
在上面的组件注册流程中,我们给出了适配者(adaptee)的接口和适配器所提供的接口,不过由于在适配器的实现中已经给定了这些细节,注册的时候完全没有必要再次指定这些参数,事实上,我们完全可以像下面这样注册这个适配器:
>>> gsm.registerAdapter(FrontDeskNG, name='ng')
包里面提供了一些陈旧的注册API,我们要尽量避免使用这些API,这些陈旧的API函数都以 provide 开头,比如: provideAdapter 、 provideUtility 之类。在开发Zope 3应用的时候,可以使用Zope设置标记语言(ZCML)来注册组件,在Zope 3里,局部组件(持久化组件)可以通过Zope管理界面(ZMI)或者编程来注册。
前面我们使用 ng 来注册 FrontDeskNG ,同样的,我们也可以用不同的名字来注册其他组件,如果注册组件的时候没有提供名字,那么名字默认为空字符串。
注意 :
局部组件是持久化组件,而全局组件是保存在内存之中。全局组件是根据应用的设置进行注册的,而局部组件是在应用启动的时候从数据库中加载到内存的。
4.3 查询适配器
从组件注册表中获取已注册的组件是通过 zope.component 包中的两个函数来完成的。一个是 getAdapter 另一个是 queryAdapter 。两个函数都接受相同的参数,不过如果无法找到组件,那么 getAdapter 会抛出 ComponentLookupError ,而 queryAdapter 会返回 None 。
可以像下面这样导入这两个方法:
>>> from zope.component import getAdapter
>>> from zope.component import queryAdapter
在上一节中,我们以'ng'为名字注册了一个组件,这个组件提供了 IDesk 接口,并且适配于房客对象(适配者 adaptee)。在本章的第一节中,我们创建了一个名为 jack 的房客对象。
下面就是如何获取一个既适配于 jack 对象的接口( IGuest ),又提供了 IDesk 接口,同时被命名为 ng 的组件的例子。在这里, getAdapter 和 queryAdapter 的作用是一致的:
>>> getAdapter(jack, IDesk, 'ng') #doctest: +ELLIPSIS
<FrontDeskNG object at ...>
>>> queryAdapter(jack, IDesk, 'ng') #doctest: +ELLIPSIS
<FrontDeskNG object at ...>
就像你所看到的这样,第一个参数是适配者(adaptee),然后是组件应该提供的接口,最后是适配器组件的名字。
如果我们试图查找一个有相同的接口和适配者(adaptee)但是使用一个没有被注册过的名字的组件,那么查询会失败,下面是这两种方法的在这种情况下的不同结果:
>>> getAdapter(jack, IDesk, 'not-exists') #doctest: +ELLIPSIS
Traceback (most recent call last):
...
ComponentLookupError: ...
>>> reg = queryAdapter(jack,
... IDesk, 'not-exists') #doctest: +ELLIPSIS
>>> reg is None
True
就像上面所写的那样,在查找失败的时候, getAdapter 抛出一个 ComponentLookupError 异常,而 queryAdapter 返回 None 。
第三个参数,也就是组件的注册名,是可选项。如果没有提供第三个参数,那么这个参数默认为空字符串。由于之前我们没有用空字符串注册组件, getAdapter 会抛出 ComponentLookupError ,类似的, queryAdapter 会返回 None ,下面请自己尝试一下:
>>> getAdapter(jack, IDesk) #doctest: +ELLIPSIS
Traceback (most recent call last):
...
ComponentLookupError: ...
>>> reg = queryAdapter(jack, IDesk) #doctest: +ELLIPSIS
>>> reg is None
True
在这一节中,我们学习了如何注册一个简单适配器和如何从组件注册表中获取这个适配器。这类适配器被称为简单适配器是因为它只适配于一个适配者(adaptee)。如果一个适配器适配于多个适配者(adaptee),那么这个适配器被称为多重适配器。
4.4 利用接口来获取适配器
我们可以利用接口来直接获取适配器,不过这种方式只能获取未命名简单适配器。第一个参数是适配者(adaptee),第二个参数是一个关键字参数,如果适配器查找失败,那么就会返回第二个参数:
>>> IDesk(jack, alternate='default-output')
'default-output'
调用时可以忽略关键字名:
>>> IDesk(jack, 'default-output')
'default-output'
如果没有给出第二个参数,那么会抛出 TypeError
>>> IDesk(jack) #doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
Traceback (most recent call last):
...
TypeError: ('Could not adapt',
<Guest object at ...>,
<InterfaceClass __builtin__.IDesk>)
下面我们采用无命名方式来注册 FrontDeskNG:
>>> gsm.registerAdapter(FrontDeskNG)
在简单的情况下,可以使用接口来获得适配器对象。
4.5 适配器模式
在Zope组件架构中的适配器概念和《设计模式》一书中的经典 适配器模式 是非常类似的;但是,ZCA中的适配器的设计应用目标要比 适配器模式 广泛的多。 适配器模式 的目的是把一个类的接口转换为客户需要的另一个接口,这可以使得本来不兼容的几个类在一起工作。但是在《设计模式》一书中的“设计动机”一节中,GoF提到:“通常情况下,适配器需要负责提供适配类没有提供的功能”。ZCA的适配器更多的着眼于为适配对象(适配者 adaptee)增加功能而不是创建新的接口。ZCA的适配器允许适配器类对象通过增加方法的方式来扩展功能(值得一提的是,在ZCA的早期设计阶段, 适配器 被称为 特性 ——Feature) [2]
[2] 讨论把 特性 改名为 适配器 的帖子: http://mail.zope.org/pipermail/zope3-dev/2001-December/000008.html
在上面一段文字中,引用了Gang of Four的书中的一段文字,那段话中写到:“……适配类没有提供的……”。但是在下面一句话中,我使用了“适配对象”来替代“适配类”。这是因为GoF在书中描述了根据实现不同区分的两种适配器,一种是 类适配器 另一种则是 对象适配器 。类适配器采用多重继承来把一个接口适配为另外一个,而对象适配器依赖于对象组合来完成适配。ZCA的适配器采用了对象适配器模式,并且采用委托(delegation)模式作为组合机制。GoF的面向对象设计第二原则就是:“倾向于对象组合而不是类继承”,关于这一话题的更详细介绍请参考《设计模式》一书。
ZCA适配器的主要优势就是在于明确组件的接口和组件注册表。ZCA的适配器组件都在组件注册表中进行过注册,客户对象可以在需要的时候根据名字和接口来查找这些适配器。
现在我们已经清楚接口,适配器和组件注册表的概念。有时注册一个并未适配任何对象的对象是有用的。数据库连接, XML解析器,对象返回的独特识别码等就是这种对象的例子。这类由ZCA提供的组件被称为工具组件(utility components)。
工具组件(Utilities)正是那种提供了一个接口的对象,并且由一个接口和一个名字进行查询。这种做法创建了一个全局的注册表,通过这个注册表中,实例可以进行注册,并且可以被您的应用程序的不同部分访问,而无需使用周围的实例作为参数。
你无须注册所有的组件实例,只需要注册你想替换的组件。
A utility can be registered with a name or without a name. A utility registered with a name is called named utility, which you will see in the next section. Before implementing the utility, as usual, define its interface. Here is a greeter interface:
The ZCA framework project began in 2001 as part of Zope 3 project. It grew out of lessons learned while developing large software systems using Zope 2. Jim Fulton was the project leader of this project. Many people contributed to the design and implementation, including but not limited to, Stephan Richter, Philipp von Weitershausen, Guido van Rossum (aka. Python BDFL), Tres Seaver, Phillip J Eby and Martijn Faassen.
Initially ZCA defined additional components; services and views, but the developers came to realize that utility can replace service and multi-adapter can replace view. Now ZCA has a very small number of core component types: utilities, adapters, subscribers and handlers. In fact, subscribers and handlers are two special types of adapters.
During the Zope 3.2 release cycle, Jim Fulton proposed a major simplification of ZCA [2]. With this simplification, a new single interface (IComponentRegistry) for registration of both global and local component was created.
The zope.component package had a long list of dependencies, many of which were not required for a non Zope 3 application. During PyCon 2007, Jim Fulton added setuptools' extras_require feature to allow separating out core ZCA functionality from add-on features [3].
Now, The ZCA project is an independent project with it's own release cycle and Subversion repository. However, issues and bugs are still tracked as part of Zope 3 project [4], and the main zope-dev list is used for development discussions [5]. There is also another general user list for Zope 3 (zope3-users) which can be used for any queries about the ZCA [6].
The zope.component, package together with the zope.interface package are the core of Zope component architecture. They provide facilities for defining, registering and looking up components. The zope.component package and its dependencies are available in egg format from the Python Package Index (PyPI) [7].
You can install zope.component and it's dependencies using easy_install [8]
$ easy_install zope.component
This command will download zope.component and its dependencies from PyPI and install it in your Python path.
Alternately, you can download zope.component and its dependencies from PyPI and then install them. Install packages in the order given below. On Windows, you may need binary packages of zope.interface and zope.proxy.
zope.interface
zope.proxy
zope.deferredimport
zope.event
zope.deprecation
zope.component
To install these packages, after downloading them, you can use easy_install command with argument as the eggs. (You may also give all these eggs in the same line.):
$ easy_install /path/to/zope.interface-3.4.x.tar.gz
$ easy_install /path/to/zope.proxy-3.4.x.tar.gz
...
You can also install these packages after extracting each one separately. For example:
$ tar zxvf /path/to/zope.interface-3.4.x.tar.gz
$ cd zope.interface-3.4.x
$ python setup.py build
$ python setup.py install
These methods will install the ZCA to the system Python, in the site-packages directory, which can cause problems. In a Zope 3 mailing list post, Jim Fulton recommends against using the system Python [9]. You can use virtualenv and/or zc.buildout for playing with any Python packages, also good for deployments.
There are two good packages in Python for setting up isolated working environments for developing Python applications. virtualenv created by Ian Biking and zc.buildout created by Jim Fulton are these two packages. You can also use these packages together. Using these packages you can install zope.component and other dependencies into an isolated working environment. This is a good practice for experimenting with any Python code, and familiarity with these tools will be beneficial when developing and deploying applications.
You can install virtualenv using easy_install:
$ easy_install virtualenv
Then create a new environment like this:
$ virtualenv myve
This will create a new virtual environment in the myve directory. Now, from inside the myve directory, you can install zope.component and dependencies using easy_install inside myve/bin directory:
$ cd myve
$ ./bin/easy_install zope.component
Now you can import zope.interface and zope.component from the new python interpreter inside myve/bin directory:
$ ./bin/python
This command will give you a Python prompt which you can use to run the code in this book.
Using zc.buildout with zc.recipe.egg recipe you can create Python interpreter with specified Python eggs. First, install zc.buildout using easy_install command. (You may also do it inside virtual environment). To create new buildout to experiment with Python eggs, first create a directory and initialize it using buildout init command:
$ mkdir mybuildout
$ cd mybuildout
$ buildout init
Now the new mybuildout directory is a buildout. The default configuration file for buildout is buildout.cfg . After initializing, it will be having this content:
[buildout]
parts =
You can change it like this:
[buildout]
parts = py
[py]
recipe = zc.recipe.egg
interpreter = python
eggs = zope.component
Now run buildout command available inside mybuildout/bin directory without any argument. This will create a new Python interpreter inside mybuildout/bin directory:
$ ./bin/buildout
$ ./bin/python
This command will give you a Python prompt which you can use to run the code in this book.
Consider a business application for registering guests staying in a hotel. Python can implement this in a number of ways. We will start with a brief look at a procedural implementation, and then move to a basic object oriented approach. As we examine the object oriented approach, we will see how we can benefit from the classic design patterns, adapter and interface. This will bring us into the world of the Zope Component Architecture.
In any business application, data storage is very critical. For simplicity, this example use a Python dictionary as the storage. We will generate unique id's for the dictionary, the associated value will be a dictionary of details about the booking.
>>> bookings_db = {} #key: unique Id, value: details in a dictionary
A minimal implementation requires a function which we pass the details of the booking, and a supporting function which provides the the unique id for the storage dictionary key.
We can get the unique id like this:
>>> def get_next_id():
... db_keys = bookings_db.keys()
... if db_keys == []:
... next_id = 1
... else:
... next_id = max(db_keys) + 1
... return next_id
As you can see, the get_next_id function implementation is very simple. The function gets a list of keys and checks for an empty list. If the list is empty this is our first booking, so we return 1. If the list is not empty, we add 1 to the maximum value in the list and return it.
Now we will use the above function to create entries in the bookings_db dictionary:
>>> def book_room(name, place):
... next_id = get_next_id()
... bookings_db[next_id] = {
... 'name': name,
... 'room': place
... }
The requirements of a hotel booking management application require considering additional data:
phone numbers
room options
payment methods
...
And code to manage the data:
cancel a reservation
update a reservation
pay for a room
persist the data
insure security of the data
...
Were we to continue with the procedural example, we would create many functions, passing data back and forth between them. As requirements change and are added, the code becomes harder to maintain and bugs become harder to find and fix.
We will end our discussion of the procedural approach here. It will be much easier to provide data persistence, design flexibility and code testability using objects.
Our discussion of object oriented design will introduce the class which serves to encapsulate the data, and the code to manage it.
Our main class will be the FrontDesk. FrontDesk, or other classes it delegates to, will know how to manage the data for the hotel. We will create instances of FrontDesk to apply this knowledge to the business of running a hotel.
Experience has shown that by consolidating the code and data requirements via objects, we will end up with a design which is easier to understand, test, and change.
Lets look at the implementation details of a FrontDesk class:
>>> class FrontDesk(object):
...
... def book_room(self, name, place):
... next_id = get_next_id()
... bookings_db[next_id] = {
... 'name': name,
... 'place': place
... }
In this implementation, the frontdesk object (an instance of FrontDesk class) is able to handle the bookings. We can use it like this:
>>> frontdesk = FrontDesk()
>>> frontdesk.book_room("Jack", "Bangalore")
Any real project will involve changing requirements. In this case management has decided that each guest must provide a phone number, so we must change the code.
We can achieve this requirement by adding one argument to the book_room method which will be added to the dictionary of values:
>>> class FrontDesk(object):
...
... def book_room(self, name, place, phone):
... next_id = get_next_id()
... bookings_db[next_id] = {
... 'name': name,
... 'place': place,
... 'phone': phone
... }
In addition to migrating the data to new schema, we now have to change all calls to FrontDesk. If we abstract the details of guest into an object and use it for registration, the code changes can be minimized. We now can make changes to the details of the guest object and the calls to FrontDesk won't need to change.
Now we have:
>>> class FrontDesk(object):
...
... def book_room(self, guest):
... next_id = get_next_id()
... bookings_db[next_id] = {
... 'name': guest.name,
... 'place': guest.place,
... 'phone': guest.phone
... }
We still will have to change code to respond to changing requirements. This is unavoidable, however, our goal is to minimize those changes, thereby increasing maintainability.
Note
When coding, it is important to feel free to make changes without fear of breaking the application. The way to get the immediate feedback required is via automated testing. With well written tests (and good version control) you can make changes large or small with impunity. A good source of information about this programming philosophy is the book Extreme Programming Explained by Kent Beck.
By introducing the guest object, you saved some typing. More importantly, the abstraction provided by the guest object made the system simpler and more understandable. As a result, the code is easier to restructure and maintain.
In a real application, the frontdesk object will need to handle chores such as cancellations and updates. In the current design, we will need to pass the guest object to frontdesk every time we call methods such as cancel_booking and update_booking.
We can avoid this requirement if we pass the guest object to FrontDesk.__init__(), making it an attribute of the instance.
>>> class FrontDeskNG(object):
...
... def __init__(self, guest):
... self.guest = guest
...
... def book_room(self):
... guest = self.guest
... next_id = get_next_id()
... bookings_db[next_id] = {
... 'name': guest.name,
... 'place': guest.place,
... 'phone': guest.phone
... }
The solution we have reached is a well known pattern, the adapter. In general, an adapter contains an adaptee:
>>> class Adapter(object):
...
... def __init__(self, adaptee):
... self.adaptee = adaptee
This pattern will be useful in dealing with implementation details which depend on considerations such as:
changing customer requirements
storage requirements (ZODB, RDBM, XML ...)
output requirements (HTML, PDF, plain text ...)
markup rendering (ReST, Markdown, Textile ...)
ZCA uses adapters and a component registry to provide the capability to change implementation details of code via configuration.
As we will see in the section on ZCA adapters, the ability to configure implementation details provides useful capability:
the ability to switch between implementations
the ability to add implementations as needed
increased re-use of both legacy and ZCA code
These capabilities lead to code that is flexible, scalable and re-usable. There is a cost however, maintaining the component registry adds a level of complexity to the application. If an application will never require these features, ZCA is unnecessary.
We are now ready to begin our study of the Zope Component Architecture, beginning with interfaces.
The README.txt [10] in path/to/zope/interface defines interfaces like this:
Interfaces are objects that specify (document) the external behavior
of objects that "provide" them. An interface specifies behavior
through:
- Informal documentation in a doc string
- Attribute definitions
- Invariants, which are conditions that must hold for objects that
provide the interface
The classic software engineering book Design Patterns [11] by the Gang of Four recommends that you "Program to an interface, not an implementation". Defining a formal interface is helpful in understanding a system. Moreover, interfaces bring to you all the benefits of ZCA.
An interface specifies the characteristics of an object, it's behaviour, it's capabilities. The interface describes what an object can do, to learn how, you must look at the implementation.
Commonly used metaphors for interfaces are contract or blueprint, the legal and architectural terms for a set of specifications.
In some modern programming languages: Java, C#, VB.NET etc, interfaces are an explicit aspect of the language. Since Python lacks interfaces, ZCA implements them as a meta-class to inherit from.
Here is a classic hello world style example:
>>> class Host(object):
...
... def goodmorning(self, name):
... """Say good morning to guests"""
...
... return "Good morning, %s!" % name
In the above class, you defined a goodmorning method. If you call the goodmorning method from an object created using this class, it will return Good morning, ...!
>>> host = Host()
>>> host.goodmorning('Jack')
'Good morning, Jack!'
Here host is the actual object your code uses. If you want to examine implementation details you need to access the class Host, either via the source code or an API [12] documentation tool.
Now we will begin to use the ZCA interfaces. For the class given above you can specify the interface like this:
>>> from zope.interface import Interface
>>> class IHost(Interface):
...
... def goodmorning(guest):
... """Say good morning to guest"""
As you can see, the interface inherits from zope.interface.Interface. This use (abuse?) of Python's class statement is how ZCA defines an interface. The I prefix for the interface name is a useful convention.
You have already seen how to declare an interface using zope.interface in previous section. This section will explain the concepts in detail.
Consider this example interface:
>>> from zope.interface import Interface
>>> from zope.interface import Attribute
>>> class IHost(Interface):
... """A host object"""
...
... name = Attribute("""Name of host""")
...
... def goodmorning(guest):
... """Say good morning to guest"""
The interface, IHost has two attributes, name and goodmorning. Recall that, at least in Python, methods are also attributes of classes. The name attribute is defined using zope.interface.Attribute class. When you add the attribute name to the IHost interface, you don't set an initial value. The purpose of defining the attribute name here is merely to indicate that any implementation of this interface will feature an attribute named name. In this case, you don't even say what type of attribute it has to be!. You can pass a documentation string as a first argument to Attribute.
The other attribute, goodmorning is a method defined using a function definition. Note that self is not required in interfaces, because self is an implementation detail of class. For example, a module can implement this interface. If a module implement this interface, there will be a name attribute and goodmorning function defined. And the goodmorning function will accept one argument.
Now you will see how to connect interface-class-object. So object is the real living thing, objects are instances of classes. And interface is the actual definition of the object, so classes are just the implementation details. This is why you should program to an interface and not to an implementation.
Now you should familiarize two more terms to understand other concepts. First one is provide and the other one is implement. Object provides interfaces and classes implement interfaces. In other words, objects provide interfaces that their classes implement. In the above example host (object) provides IHost (interface) and Host (class) implement IHost (interface). One object can provide more than one interface also one class can implement more than one interface. Objects can also provide interfaces directly, in addition to what their classes implement.
Note
Classes are the implementation details of objects. In Python, classes are callable objects, so why other callable objects can't implement an interface. Yes, it is possible. For any callable object you can declare that it produces objects that provide some interfaces by saying that the callable object implements the interfaces. The callable objects are generally called as factories. Since functions are callable objects, a function can be an implementer of an interface.
To declare a class implements a particular interface, use the function zope.interface.implements in the class statement.
Consider this example, here Host implements IHost:
>>> from zope.interface import implements
>>> class Host(object):
...
... implements(IHost)
...
... name = u''
...
... def goodmorning(self, guest):
... """Say good morning to guest"""
...
... return "Good morning, %s!" % guest
Note
If you wonder how implements function works, refer the blog post by James Henstridge (http://blogs.gnome.org/jamesh/2005/09/08/python-class-advisors/) . In the adapter section, you will see an adapts function, it is also working similarly.
Since Host implements IHost, instances of Host provides IHost. There are some utility methods to introspect the declarations. The declaration can write outside the class also. If you don't write interface.implements(IHost) in the above example, then after defining the class statement, you can write like this:
>>> from zope.interface import classImplements
>>> classImplements(Host, IHost)
Now, return to the example application. Here you will see how to define the interface of the frontdesk object:
>>> from zope.interface import Interface
>>> class IDesk(Interface):
... """A frontdesk will register object's details"""
...
... def register():
... """Register object's details"""
...
Here, first you imported Interface class from zope.interface module. If you define a subclass of this Interface class, it will be an interface from Zope component architecture point of view. An interface can be implemented, as you already noted, in a class or any other callable object.
The frontdesk interface defined here is IDesk. The documentation string for interface gives an idea about the object. By defining a method in the interface, you made a contract for the component, that there will be a method with same name available. For the method definition interface, the first argument should not be self, because an interface will never be instantiated nor will its methods ever be called. Instead, the interface class merely documents what methods and attributes should appear in any normal class that claims to implement it, and the self parameter is an implementation detail which doesn't need to be documented.
As you know, an interface can also specify normal attributes:
>>> from zope.interface import Interface
>>> from zope.interface import Attribute
>>> class IGuest(Interface):
...
... name = Attribute("Name of guest")
... place = Attribute("Place of guest")
In this interface, guest object has two attributes specified with documentation. An interface can also specify both attributes and methods together. An interface can be implemented in a class, module or any other objects. For example a function can dynamically create the component and return, in this case the function is an implementer for the interface.
Now you know what is an interface and how to define and use it. In the next chapter you can see how an interface is used to define an adapter component.
An interface can be used to declare that a particular object belongs to a special type. An interface without any attribute or method is called marker interface.
Here is a marker interface:
>>> from zope.interface import Interface
>>> class ISpecialGuest(Interface):
... """A special guest"""
This interface can be used to declare an object is a special guest.
Sometimes you will be required to use some rule for your component which involve one or more normal attributes. These kind of rule is called invariants. You can use zope.interface.invariant for setting invariants for your objects in their interface.
Consider a simple example, there is a person object. A person object has name, email and phone attributes. How do you implement a validation rule that says either email or phone have to exist, but not necessarily both.
First you have to make a callable object, either a simple function or callable instance of a class like this:
>>> def contacts_invariant(obj):
...
... if not (obj.email or obj.phone):
... raise Exception(
... "At least one contact info is required")
Then define the person object's interface like this. Use the zope.interface.invariant function to set the invariant:
>>> from zope.interface import Interface
>>> from zope.interface import Attribute
>>> from zope.interface import invariant
>>> class IPerson(Interface):
...
... name = Attribute("Name")
... email = Attribute("Email Address")
... phone = Attribute("Phone Number")
...
... invariant(contacts_invariant)
Now use validateInvariants method of the interface to validate:
>>> from zope.interface import implements
>>> class Person(object):
... implements(IPerson)
...
... name = None
... email = None
... phone = None
>>> jack = Person()
>>> jack.email = u"jack@some.address.com"
>>> IPerson.validateInvariants(jack)
>>> jill = Person()
>>> IPerson.validateInvariants(jill)
Traceback (most recent call last):
...
Exception: At least one contact info is required
As you can see jack object validated without raising any exception. But jill object didn't validated the invariant constraint, so it raised exception.
This section will describe adapters in detail. Zope component architecture, as you noted, helps to effectively use Python objects. Adapter components are one of the basic components used by Zope component architecture for effectively using Python objects. Adapter components are Python objects, but with well defined interface.
To declare a class is an adapter use adapts function defined in zope.component package. Here is a new FrontDeskNG adapter with explicit interface declaration:
>>> from zope.interface import implements
>>> from zope.component import adapts
>>> class FrontDeskNG(object):
...
... implements(IDesk)
... adapts(IGuest)
...
... def __init__(self, guest):
... self.guest = guest
...
... def register(self):
... guest = self.guest
... next_id = get_next_id()
... bookings_db[next_id] = {
... 'name': guest.name,
... 'place': guest.place,
... 'phone': guest.phone
... }
What you defined here is an adapter for IDesk, which adapts IGuest object. The IDesk interface is implemented by FrontDeskNG class. So, an instance of this class will provide IDesk interface.
>>> class Guest(object):
...
... implements(IGuest)
...
... def __init__(self, name, place):
... self.name = name
... self.place = place
>>> jack = Guest("Jack", "Bangalore")
>>> jack_frontdesk = FrontDeskNG(jack)
>>> IDesk.providedBy(jack_frontdesk)
True
The FrontDeskNG is just one adapter you created, you can also create other adapters which handles guest registration differently.
To use this adapter component, you have to register this in a component registry also known as site manager. A site manager normally resides in a site. A site and site manager will be more important when developing a Zope 3 application. For now you only required to bother about global site and global site manager ( or component registry). A global site manager will be in memory, but a local site manager is persistent.
To register your component, first get the global site manager:
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> gsm.registerAdapter(FrontDeskNG,
... (IGuest,), IDesk, 'ng')
To get the global site manager, you have to call getGlobalSiteManager function available in zope.component package. In fact, the global site manager is available as an attribute (globalSiteManager) of zope.component package. So, you can directly use zope.component.globalSiteManager attribute. To register the adapter in component, as you can see above, use registerAdapter method of component registry. The first argument should be your adapter class/factory. The second argument is a tuple of adaptee objects, i.e, the object which you are adapting. In this example, you are adapting only IGuest object. The third argument is the interface implemented by the adapter component. The fourth argument is optional, that is the name of the particular adapter. Since you gave a name for this adapter, this is a named adapter. If name is not given, it will default to an empty string ('').
In the above registration, you have given the adaptee interface and interface to be provided by the adapter. Since you have already given these details in adapter implementation, it is not required to specify again. In fact, you could have done the registration like this:
>>> gsm.registerAdapter(FrontDeskNG, name='ng')
There are some old API to do the registration, which you should avoid. The old API functions starts with provide, eg: provideAdapter, provideUtility etc. While developing a Zope 3 application you can use Zope configuration markup language (ZCML) for registration of components. In Zope 3, local components (persistent components) can be registered from Zope Management Interface (ZMI) or you can do it programmatically also.
You registered FrontDeskNG with a name ng. Similarly you can register other adapters with different names. If a component is registered without name, it will default to an empty string.
Note
Local components are persistent components but global components are in memory. Global components will be registered based on the configuration of application. Local components are taken to memory from database while starting the application.
Retrieving registered components from component registry is achieved through two functions available in zope.component package. One of them is getAdapter and the other is queryAdapter . Both functions accepts same arguments. The getAdapter will raise ComponentLookupError if component lookup fails on the other hand queryAdapter will return None.
You can import the methods like this:
>>> from zope.component import getAdapter
>>> from zope.component import queryAdapter
In the previous section you have registered a component for guest object (adaptee) which provides IDesk interface with name as 'ng'. In the first section of this chapter, you have created a guest object named jack .
This is how you can retrieve a component which adapts the interface of jack object (IGuest) and provides IDesk interface also with name as 'ng'. Here both getAdapter and queryAdapter works similarly:
>>> getAdapter(jack, IDesk, 'ng') #doctest: +ELLIPSIS
<FrontDeskNG object at ...>
>>> queryAdapter(jack, IDesk, 'ng') #doctest: +ELLIPSIS
<FrontDeskNG object at ...>
As you can see, the first argument should be adaptee then, the interface which should be provided by component and last the name of adapter component.
If you try to lookup the component with an name not used for registration but for same adaptee and interface, the lookup will fail. Here is how the two methods works in such a case:
>>> getAdapter(jack, IDesk, 'not-exists') #doctest: +ELLIPSIS
Traceback (most recent call last):
...
ComponentLookupError: ...
>>> reg = queryAdapter(jack,
... IDesk, 'not-exists') #doctest: +ELLIPSIS
>>> reg is None
True
As you can see above, getAdapter raised a ComponentLookupError exception, but queryAdapter returned None when lookup failed.
The third argument, the name of registration, is optional. If the third argument is not given it will default to empty string (''). Since there is no component registered with an empty string, getAdapter will raise ComponentLookupError . Similarly queryAdapter will return None, see yourself how it works:
>>> getAdapter(jack, IDesk) #doctest: +ELLIPSIS
Traceback (most recent call last):
...
ComponentLookupError: ...
>>> reg = queryAdapter(jack, IDesk) #doctest: +ELLIPSIS
>>> reg is None
True
In this section you have learned how to register a simple adapter and how to retrieve it from component registry. These kind of adapters is called single adapter, because it adapts only one adaptee. If an adapter adapts more that one adaptee, then it is called multi adapter.
Adapters can be directly retrieved using interfaces, but it will only work for non-named single adapters. The first argument is the adaptee and the second argument is a keyword argument. If adapter lookup fails, second argument will be returned.
>>> IDesk(jack, alternate='default-output')
'default-output'
Keyword name can be omitted:
>>> IDesk(jack, 'default-output')
'default-output'
If second argument is not given, it will raise TypeError:
>>> IDesk(jack) #doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
Traceback (most recent call last):
...
TypeError: ('Could not adapt',
<Guest object at ...>,
<InterfaceClass __builtin__.IDesk>)
Here FrontDeskNG is registered without name:
>>> gsm.registerAdapter(FrontDeskNG)
Now the adapter lookup should succeed:
>>> IDesk(jack, 'default-output') #doctest: +ELLIPSIS
<FrontDeskNG object at ...>
For simple cases, you may use interface to get adapter components.
The adapter concept in Zope Component Architecture and the classic adapter pattern as described in Design Patterns book are very similar. But the intent of ZCA adapter usage is more wider than the adapter pattern itself. The intent of adapter pattern is to convert the interface of a class into another interface clients expect. This allows classes work together that couldn't otherwise because of incompatible interfaces. But in the motivation section of Design Patterns book, GoF says: "Often the adapter is responsible for functionality the adapted class doesn't provide". ZCA adapter has more focus on adding functionalities than creating a new interface for an adapted object (adaptee). ZCA adapter lets adapter classes extend functionality by adding methods. (It would be interesting to note that Adapter was known as Feature in earlier stage of ZCA design. ) [13]
Thread discussing renaming of Feature to Adapter: http://mail.zope.org/pipermail/zope3-dev/2001-December/000008.html
The above paragraph has a quote from Gang of Four book, it ends like this: " ...adapted class doesn't provide". But in the next sentence I used "adapted object" instead of "adapted class", because GoF describes about two variants of adapters based on implementations. The first one is called class adapter and the other one is called object adapter. A class adapter uses multiple inheritance to adapt one interface to another, on the other hand an object adapter relies on object composition. ZCA adapter is following object adapter pattern, which use delegation as a mechanism for composition. GoF's second principle of object-oriented design goes like this: "Favor object composition over class inheritance". For more details about this subject please read Design Patterns book.
The major attraction of ZCA adapter are the explicit interface for components and the component registry. ZCA adapter components are registered in component registry and looked up by client objects using interface and name when required.
Now you know the concept of interface, adapter and component registry. Sometimes it would be useful to register an object which is not adapting anything. Database connection, XML parser, object returning unique Ids etc. are examples of these kinds of objects. These kind of components provided by the ZCA are called utility components.
Utilities are just objects that provide an interface and that are looked up by an interface and a name. This approach creates a global registry by which instances can be registered and accessed by different parts of your application, with no need to pass the instances around as parameters.
You need not to register all component instances like this. Only register components which you want to make replaceable.
A utility can be registered with a name or without a name. A utility registered with a name is called named utility, which you will see in the next section. Before implementing the utility, as usual, define its interface. Here is a greeter interface:
>>> from zope.interface import Interface
>>> from zope.interface import implements
>>> class IGreeter(Interface):
...
... def greet(name):
... """Say hello"""
Like an adapter a utility may have more than one implementation. Here is a possible implementation of the above interface:
>>> class Greeter(object):
...
... implements(IGreeter)
...
... def greet(self, name):
... return "Hello " + name
The actual utility will be an instance of this class. To use this utility, you have to register it, later you can query it using the ZCA API. You can register an instance of this class (utility) using registerUtility:
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> greet = Greeter()
>>> gsm.registerUtility(greet, IGreeter)
In this example you registered the utility as providing the IGreeter interface. You can look the interface up with either queryUtility or getUtility:
>>> from zope.component import queryUtility
>>> from zope.component import getUtility
>>> queryUtility(IGreeter).greet('Jack')
'Hello Jack'
>>> getUtility(IGreeter).greet('Jack')
'Hello Jack'
As you can see, adapters are normally classes, but utilities are normally instances of classes. Only once you are creating the instance of a utility class, but adapter instances are dynamically created whenever you query for it.
When registering a utility component, like adapter, you can use a name. As mentioned in the previous section, a utility registered with a particular name is called named utility.
This is how you can register the greeter utility with a name:
>>> greet = Greeter()
>>> gsm.registerUtility(greet, IGreeter, 'new')
In this example you registered the utility with a name as providing the IGreeter interface. You can look up the interface with either queryUtility or getUtility:
>>> from zope.component import queryUtility
>>> from zope.component import getUtility
>>> queryUtility(IGreeter, 'new').greet('Jill')
'Hello Jill'
>>> getUtility(IGreeter, 'new').greet('Jill')
'Hello Jill'
As you can see here, while querying you have to use the name as second argument.
Calling getUtility function without a name (second argument) is equivalent to calling with an empty string as the name. Because, the default value for second (keyword) argument is an empty string. Then, component lookup mechanism will try to find the component with name as empty string, and it will fail. When component lookup fails it will raise ComponentLookupError exception. Remember, it will not return some random component registered with some other name. The adapter look up fuctions, getAdapter and queryAdapter also works similarly.
A Factory is a utility component which provides IFactory interface.
To create a factory, first define the interface of the object:
>>> from zope.interface import Attribute
>>> from zope.interface import Interface
>>> from zope.interface import implements
>>> class IDatabase(Interface):
...
... def getConnection():
... """Return connection object"""
Here is fake implementation of IDatabase interface:
>>> class FakeDb(object):
...
... implements(IDatabase)
...
... def getConnection(self):
... return "connection"
You can create a factory using zope.component.factory.Factory:
>>> from zope.component.factory import Factory
>>> factory = Factory(FakeDb, 'FakeDb')
Now you can register it like this:
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> from zope.component.interfaces import IFactory
>>> gsm.registerUtility(factory, IFactory, 'fakedb')
To use the factory, you may do it like this:
>>> from zope.component import queryUtility
>>> queryUtility(IFactory, 'fakedb')() #doctest: +ELLIPSIS
<FakeDb object at ...>
There is a shortcut to use factory:
>>> from zope.component import createObject
>>> createObject('fakedb') #doctest: +ELLIPSIS
<FakeDb object at ...>
This chapter discuss some advanced adapters like multi adapter, subscription adapter and handler.
A simple adapter normally adapts only one object, but an adapter may adapt more than one object. If an adapter adapts more than one objects, it is called multi-adapter.
>>> from zope.interface import Interface
>>> from zope.interface import implements
>>> from zope.component import adapts
>>> class IAdapteeOne(Interface):
... pass
>>> class IAdapteeTwo(Interface):
... pass
>>> class IFunctionality(Interface):
... pass
>>> class MyFunctionality(object):
... implements(IFunctionality)
... adapts(IAdapteeOne, IAdapteeTwo)
...
... def __init__(self, one, two):
... self.one = one
... self.two = two
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> gsm.registerAdapter(MyFunctionality)
>>> class One(object):
... implements(IAdapteeOne)
>>> class Two(object):
... implements(IAdapteeTwo)
>>> one = One()
>>> two = Two()
>>> from zope.component import getMultiAdapter
>>> getMultiAdapter((one,two), IFunctionality) #doctest: +ELLIPSIS
<MyFunctionality object at ...>
>>> myfunctionality = getMultiAdapter((one,two), IFunctionality)
>>> myfunctionality.one #doctest: +ELLIPSIS
<One object at ...>
>>> myfunctionality.two #doctest: +ELLIPSIS
<Two object at ...>
Unlike regular adapters, subscription adapters are used when we want all of the adapters that adapt an object to a particular interface. Subscription adapter is also known as subscriber.
Consider a validation problem. We have objects and we want to assess whether they meet some sort of standards. We define a validation interface:
>>> from zope.interface import Interface
>>> from zope.interface import Attribute
>>> from zope.interface import implements
>>> class IValidate(Interface):
...
... def validate(ob):
... """Determine whether the object is valid
...
... Return a string describing a validation problem.
... An empty string is returned to indicate that the
... object is valid.
... """
Perhaps we have documents:
>>> class IDocument(Interface):
...
... summary = Attribute("Document summary")
... body = Attribute("Document text")
>>> class Document(object):
...
... implements(IDocument)
...
... def __init__(self, summary, body):
... self.summary, self.body = summary, body
Now, we may want to specify various validation rules for documents. For example, we might require that the summary be a single line:
>>> from zope.component import adapts
>>> class SingleLineSummary:
...
... adapts(IDocument)
... implements(IValidate)
...
... def __init__(self, doc):
... self.doc = doc
...
... def validate(self):
... if '\n' in self.doc.summary:
... return 'Summary should only have one line'
... else:
... return ''
Or we might require the body to be at least 1000 characters in length:
>>> class AdequateLength(object):
...
... adapts(IDocument)
... implements(IValidate)
...
... def __init__(self, doc):
... self.doc = doc
...
... def validate(self):
... if len(self.doc.body) < 1000:
... return 'too short'
... else:
... return ''
We can register these as subscription adapters:
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> gsm.registerSubscriptionAdapter(SingleLineSummary)
>>> gsm.registerSubscriptionAdapter(AdequateLength)
We can then use the subscribers to validate objects:
>>> from zope.component import subscribers
>>> doc = Document("A\nDocument", "blah")
>>> [adapter.validate()
... for adapter in subscribers([doc], IValidate)
... if adapter.validate()]
['Summary should only have one line', 'too short']
>>> doc = Document("A\nDocument", "blah" * 1000)
>>> [adapter.validate()
... for adapter in subscribers([doc], IValidate)
... if adapter.validate()]
['Summary should only have one line']
>>> doc = Document("A Document", "blah")
>>> [adapter.validate()
... for adapter in subscribers([doc], IValidate)
... if adapter.validate()]
['too short']
Handlers are subscription adapter factories that don't produce anything. They do all of their work when called. Handlers are typically used to handle events. Handlers are also known as event subscribers or event subscription adapters.
Event subscribers are different from other subscription adapters in that the caller of event subscribers doesn't expect to interact with them in any direct way. For example, an event publisher doesn't expect to get any return value. Because subscribers don't need to provide an API to their callers, it is more natural to define them with functions, rather than classes. For example, in a document-management system, we might want to record creation times for documents:
>>> import datetime
>>> def documentCreated(event):
... event.doc.created = datetime.datetime.utcnow()
In this example, we have a function that takes an event and performs some processing. It doesn't actually return anything. This is a special case of a subscription adapter that adapts an event to nothing. All of the work is done when the adapter "factory" is called. We call subscribers that don't actually create anything "handlers". There are special APIs for registering and calling them.
To register the subscriber above, we define a document-created event:
>>> from zope.interface import Interface
>>> from zope.interface import Attribute
>>> from zope.interface import implements
>>> class IDocumentCreated(Interface):
...
... doc = Attribute("The document that was created")
>>> class DocumentCreated(object):
...
... implements(IDocumentCreated)
...
... def __init__(self, doc):
... self.doc = doc
We'll also change our handler definition to:
>>> def documentCreated(event):
... event.doc.created = datetime.datetime.utcnow()
>>> from zope.component import adapter
>>> @adapter(IDocumentCreated)
... def documentCreated(event):
... event.doc.created = datetime.datetime.utcnow()
This marks the handler as an adapter of IDocumentCreated events.
Now we'll register the handler:
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> gsm.registerHandler(documentCreated)
Now, we can create an event and use the handle function to call handlers registered for the event:
>>> from zope.component import handle
>>> handle(DocumentCreated(doc))
>>> doc.created.__class__.__name__
'datetime'
Zope Component Architecture is used in both Zope 3 and Zope 2. This chapter will go through usage of the ZCA in Zope.
The Zope Configuration Markup Language (ZCML) is an XML based configuration system for registration of components. So, instead of using Python API for registration, you can use ZCML. But to use ZCML, unfortunately, you will be required to install more dependency packages.
To install these packages:
$ easy_install "zope.component [zcml]"
To register an adapter:
<configure xmlns="http://namespaces.zope.org/zope">
<adapter
factory=".company.EmployeeSalary"
provides=".interfaces.ISalary"
for=".interfaces.IEmployee"
/>
The provides and for attributes are optional, provided you have declared it in the implementation:
<configure xmlns="http://namespaces.zope.org/zope">
<adapter
factory=".company.EmployeeSalary"
/>
If you want to register the component as named adapter, you can give a name attribute:
<configure xmlns="http://namespaces.zope.org/zope">
<adapter
factory=".company.EmployeeSalary"
name="salary"
/>
Utilities are also registered similarly.
To register an utility:
<configure xmlns="http://namespaces.zope.org/zope">
<utility
component=".database.connection"
provides=".interfaces.IConnection"
/>
The provides attribute is optional, provided you have declared it in the implementation:
<configure xmlns="http://namespaces.zope.org/zope">
<utility
component=".database.connection"
/>
If you want to register the component as named utility, you can give a name attribute:
<configure xmlns="http://namespaces.zope.org/zope">
<utility
component=".database.connection"
name="Database Connection"
/>
Instead of directly using the component, you can also give a factory:
<configure xmlns="http://namespaces.zope.org/zope">
<utility
factory=".database.Connection"
/>
When you register components using Python API (register* methods), the last registered component will replace previously registered component, if both are registered with same type of arguments. For example, consider this example:
>>> from zope.interface import Attribute
>>> from zope.interface import Interface
>>> class IA(Interface):
... pass
>>> class IP(Interface):
... pass
>>> from zope.interface import implements
>>> from zope.component import adapts
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> class AP(object):
...
... implements(IP)
... adapts(IA)
...
... def __init__(self, context):
... self.context = context
>>> class AP2(object):
...
... implements(IP)
... adapts(IA)
...
... def __init__(self, context):
... self.context = context
>>> class A(object):
...
... implements(IA)
>>> a = A()
>>> ap = AP(a)
>>> gsm.registerAdapter(AP)
>>> getAdapter(a, IP) #doctest: +ELLIPSIS
<AP object at ...>
If you register another adapter, the existing one will be replaced:
>>> gsm.registerAdapter(AP2)
>>> getAdapter(a, IP) #doctest: +ELLIPSIS
<AP2 object at ...>
But when registering components using ZCML, the second registration will raise a conflict error. This is a hint for you, otherwise there is a chance for overriding registration by mistake. This may lead to hard to track bugs in your system. So, using ZCML is a win for the application.
Sometimes you will be required to override existing registration. ZCML provides includeOverrides directive for this. Using this, you can write your overrides in a separate file:
<includeOverrides file="overrides.zcml" />
Location: zope.app.container.contained.NameChooser
This is an adapter for choosing a unique name for an object inside a container.
The registration of adapter is like this:
<adapter
provides=".interfaces.INameChooser"
for="zope.app.container.interfaces.IWriteContainer"
factory=".contained.NameChooser"
/>
From the registration, you can see that the adaptee is a IWriteContainer and the adapter provides INameChooser.
This adapter provides a very convenient functionality for Zope programmers. The main implementations of IWriteContainer in Zope 3 are zope.app.container.BTreeContainer and zope.app.folder.Folder. Normally you will be inheriting from these implementations for creating your own container classes. Suppose there is no interface called INameChooser and adapter, then you will be required to implement this functionality for every implementations separately.
Location: zope.location.traversing.LocationPhysicallyLocatable
This adapter is frequently used in Zope 3 applications, but normally it is called through an API in zope.traversing.api. (Some old code even use zope.app.zapi functions, which is again one more indirection)
The registration of adapter is like this:
<adapter
factory="zope.location.traversing.LocationPhysicallyLocatable"
/>
The interface provided and adaptee interface is given in the implementation.
Here is the beginning of implementation:
class LocationPhysicallyLocatable(object):
"""Provide location information for location objects
"""
zope.component.adapts(ILocation)
zope.interface.implements(IPhysicallyLocatable)
...
Normally, almost all persistent objects in Zope 3 application will be providing the ILocation interface. This interface has only two attribute, __parent__ and __name__. The __parent__ is the parent in the location hierarchy. And __name__ is the name within the parent.
The IPhysicallyLocatable interface has four methods: getRoot, getPath, getName, and getNearestSite.
getRoot function will return the physical root object.
getPath return the physical path to the object as a string.
getName return the last segment of the physical path.
getNearestSite return the site the object is contained in. If the object is a site, the object itself is returned.
If you learn Zope 3, you can see that these are the important things which you required very often. To understand the beauty of this system, you must see how Zope 2 actually get the physical root object and how it is implemented. There is a method called getPhysicalRoot virtually for all container objects.
Location: zope.size.DefaultSized
This adapter is just a default implementation of ISized interface. This adapter is registered for all kind of objects. If you want to register this adapter for a particular interface, then you have to override this registration for your implementation.
The registration of adapter is like this:
<adapter
for="*"
factory="zope.size.DefaultSized"
provides="zope.size.interfaces.ISized"
permission="zope.View"
/>
As you can see, the adaptee interface is *, so it can adapt any kind of objects.
The ISized is a simple interface with two method contracts:
class ISized(Interface):
def sizeForSorting():
"""Returns a tuple (basic_unit, amount)
Used for sorting among different kinds of sized objects.
'amount' need only be sortable among things that share the
same basic unit."""
def sizeForDisplay():
"""Returns a string giving the size.
"""
You can see another ISized adapter registered for IZPTPage in zope.app.zptpage package.
Location: zope.app.applicationcontrol.ZopeVersionUtility
This utility gives version of the running Zope.
The registration goes like this:
<utility
component=".zopeversion.ZopeVersionUtility"
provides=".interfaces.IZopeVersion" />
The interface provided, IZopeVersion, has only one method named getZopeVersion. This method return a string containing the Zope version (possibly including SVN information).
The default implementation, ZopeVersionUtility, get version info from a file version.txt in zope/app directory. If Zope is running from subversion checkout, it will show the latest revision number. If none of the above works it will set it to: Development/Unknown.
Note
This chapter is not yet completed. Please send your suggestions !
This chapter demonstrates creating a desktop application using PyGTK GUI library and the ZCA. This application also use two different kinds of data persistence mechanisms, one object database (ZODB) & and another relational database (SQLite). However, practically, only one storage can be used for a particular installation. The reason for using two different persistence mechanisms is to demonstrate how to use the ZCA to glue components. Majority of the code in this application is related to PyGTK.
As the application grows you may use the ZCA components wherever you want pluggability or exensibility. Use plain Python objects directly where you do not required pluggability or exensibility.
There is no differnce in using ZCA for web or desktop or any other kind of applicatiion or framework. It is better to follow a convention for the location from where you are going to register components. This application use a convention, which can be extended by putting registration of similar components in separate modules and later import them from main registration module. In this application the main component registration module is register.py.
Source code of this application can be downloaded from: http://www.muthukadan.net/downloads/zcalib.tar.bz2
The application we are going to discuss here is a library management system with minimal features. The requirements can be summarized like this:
Add members with a unique number and name.
Add books with barcode, author & title
Issue books
Return books
The application can be designed in such a way that major features can be accessed from a single window. The main window for accessing all these features can be designed like this:
From member adding window, user should be able to manage members. So, the member adding window should have add, update and delete buttons:
From the catalog window, user can add, edit and delete books:
The circulation window should have the facility for issuing and returning books:
As you can see in the code, most of the code are related to PyGTK. The code structure is very similar for different windows. The windows of this application are designed using Glade GUI builder. You should give proper names for widgets you are going to use from code. In the above window, all menu entries has names like: circulation, catalog, member, quit & about.
The gtk.glade.XML object is used to parse glade file, this will create GUI widget objects. This is how to parse and access objects:
import gtk.glade
xmlobj = gtk.glade.XML('/path/to/file.glade')
widget = xmlobj.get_widget('widget_name')
In the mainwindow.py, you can see code like this:
curdir = os.path.abspath(os.path.dirname(__file__))
xml = os.path.join(curdir, 'glade', 'mainwindow.glade')
xmlobj = gtk.glade.XML(xml)
self.mainwindow = xmlobj.get_widget('mainwindow')
The name of main window widget is mainwindow. Similarly, other widgets are retrived below that:
circulation = xmlobj.get_widget('circulation')
member = xmlobj.get_widget('member')
quit = xmlobj.get_widget('quit')
catalog = xmlobj.get_widget('catalog')
about = xmlobj.get_widget('about')
Then, these widgets are connected for some events:
self.mainwindow.connect('delete_event', self.delete_event)
quit.connect('activate', self.delete_event)
circulation.connect('activate', self.on_circulation_activate)
member.connect('activate', self.on_member_activate)
catalog.connect('activate', self.on_catalog_activate)
about.connect('activate', self.on_about_activate)
The delete_event is the event when the window is closing using the window close button. The activate event is emitted when the menu is selected. The widgets are connected to some callback functions for some eventes.
You can see from the above code that, main window is connected to on_delete_event method for delete_event. The quit widget is also connected to same method for activate event:
def on_delete_event(self, *args):
gtk.main_quit()
The callback function just call main_quit function
This is the zcalib.py:
import registry
import mainwindow
if __name__ == '__main__':
registry.initialize()
try:
mainwindow.main()
except KeyboardInterrupt:
import sys
sys.exit(1)
Here, two modules are imported registry and mainwindow. Then, registry is initialized and mainwindow's main function is called. If user is trying to exit application using Ctrl+C, system will exit normally, that's why we catched KeyboardInterrupt exception.
This is the registry.py:
import sys
from zope.component import getGlobalSiteManager
from interfaces import IMember
from interfaces import IBook
from interfaces import ICirculation
from interfaces import IDbOperation
def initialize_rdb():
from interfaces import IRelationalDatabase
from relationaldatabase import RelationalDatabase
from member import MemberRDbOperation
from catalog import BookRDbOperation
from circulation import CirculationRDbOperation
gsm = getGlobalSiteManager()
db = RelationalDatabase()
gsm.registerUtility(db, IRelationalDatabase)
gsm.registerAdapter(MemberRDbOperation,
(IMember,),
IDbOperation)
gsm.registerAdapter(BookRDbOperation,
(IBook,),
IDbOperation)
gsm.registerAdapter(CirculationRDbOperation,
(ICirculation,),
IDbOperation)
def initialize_odb():
from interfaces import IObjectDatabase
from objectdatabase import ObjectDatabase
from member import MemberODbOperation
from catalog import BookODbOperation
from circulation import CirculationODbOperation
gsm = getGlobalSiteManager()
db = ObjectDatabase()
gsm.registerUtility(db, IObjectDatabase)
gsm.registerAdapter(MemberODbOperation,
(IMember,),
IDbOperation)
gsm.registerAdapter(BookODbOperation,
(IBook,),
IDbOperation)
gsm.registerAdapter(CirculationODbOperation,
(ICirculation,),
IDbOperation)
def check_use_relational_db():
use_rdb = False
try:
arg = sys.argv[1]
if arg == '-r':
return True
except IndexError:
pass
return use_rdb
def initialize():
use_rdb = check_use_relational_db()
if use_rdb:
initialize_rdb()
else:
initialize_odb()
Look at the initialize function which we are calling from the main module, zcalib.py. The initialize function first check which db to use, relational database (RDB) or object database (ODB) and this checking is done at check_use_relational_db function. If -r option is given at command line, it will call initialize_rdb otherwise, initialize_odb. If the RDB function is called, it will setup all components related to RDB. On the other hand, if the ODB function is called, it will setup all components related to ODB.
Here is the mainwindow.py:
import os
import gtk
import gtk.glade
from circulationwindow import circulationwindow
from catalogwindow import catalogwindow
from memberwindow import memberwindow
class MainWindow(object):
def __init__(self):
curdir = os.path.abspath(os.path.dirname(__file__))
xml = os.path.join(curdir, 'glade', 'mainwindow.glade')
xmlobj = gtk.glade.XML(xml)
self.mainwindow = xmlobj.get_widget('mainwindow')
circulation = xmlobj.get_widget('circulation')
member = xmlobj.get_widget('member')
quit = xmlobj.get_widget('quit')
catalog = xmlobj.get_widget('catalog')
about = xmlobj.get_widget('about')
self.mainwindow.connect('delete_event', self.delete_event)
quit.connect('activate', self.delete_event)
circulation.connect('activate', self.on_circulation_activate)
member.connect('activate', self.on_member_activate)
catalog.connect('activate', self.on_catalog_activate)
about.connect('activate', self.on_about_activate)
def delete_event(self, *args):
gtk.main_quit()
def on_circulation_activate(self, *args):
circulationwindow.show_all()
def on_member_activate(self, *args):
memberwindow.show_all()
def on_catalog_activate(self, *args):
catalogwindow.show_all()
def on_about_activate(self, *args):
pass
def run(self):
self.mainwindow.show_all()
def main():
mainwindow = MainWindow()
mainwindow.run()
gtk.main()
The main function here creates an instance of MainWindow class, which will initialize all widgets.
Here is the memberwindow.py:
import os
import gtk
import gtk.glade
from zope.component import getAdapter
from components import Member
from interfaces import IDbOperation
class MemberWindow(object):
def __init__(self):
curdir = os.path.abspath(os.path.dirname(__file__))
xml = os.path.join(curdir, 'glade', 'memberwindow.glade')
xmlobj = gtk.glade.XML(xml)
self.memberwindow = xmlobj.get_widget('memberwindow')
self.number = xmlobj.get_widget('number')
self.name = xmlobj.get_widget('name')
add = xmlobj.get_widget('add')
update = xmlobj.get_widget('update')
delete = xmlobj.get_widget('delete')
close = xmlobj.get_widget('close')
self.treeview = xmlobj.get_widget('treeview')
self.memberwindow.connect('delete_event', self.on_delete_event)
add.connect('clicked', self.on_add_clicked)
update.connect('clicked', self.on_update_clicked)
delete.connect('clicked', self.on_delete_clicked)
close.connect('clicked', self.on_delete_event)
self.initialize_list()
def show_all(self):
self.populate_list_store()
self.memberwindow.show_all()
def populate_list_store(self):
self.list_store.clear()
member = Member()
memberdboperation = getAdapter(member, IDbOperation)
members = memberdboperation.get()
for member in members:
number = member.number
name = member.name
self.list_store.append((member, number, name,))
def on_delete_event(self, *args):
self.memberwindow.hide()
return True
def initialize_list(self):
self.list_store = gtk.ListStore(object, str, str)
self.treeview.set_model(self.list_store)
tvcolumn = gtk.TreeViewColumn('Member Number')
self.treeview.append_column(tvcolumn)
cell = gtk.CellRendererText()
tvcolumn.pack_start(cell, True)
tvcolumn.add_attribute(cell, 'text', 1)
tvcolumn = gtk.TreeViewColumn('Member Name')
self.treeview.append_column(tvcolumn)
cell = gtk.CellRendererText()
tvcolumn.pack_start(cell, True)
tvcolumn.add_attribute(cell, 'text', 2)
def on_add_clicked(self, *args):
number = self.number.get_text()
name = self.name.get_text()
member = Member()
member.number = number
member.name = name
self.add(member)
self.list_store.append((member, number, name,))
def add(self, member):
memberdboperation = getAdapter(member, IDbOperation)
memberdboperation.add()
def on_update_clicked(self, *args):
number = self.number.get_text()
name = self.name.get_text()
treeselection = self.treeview.get_selection()
model, iter = treeselection.get_selected()
if not iter:
return
member = self.list_store.get_value(iter, 0)
member.number = number
member.name = name
self.update(member)
self.list_store.set(iter, 1, number, 2, name)
def update(self, member):
memberdboperation = getAdapter(member, IDbOperation)
memberdboperation.update()
def on_delete_clicked(self, *args):
treeselection = self.treeview.get_selection()
model, iter = treeselection.get_selected()
if not iter:
return
member = self.list_store.get_value(iter, 0)
self.delete(member)
self.list_store.remove(iter)
def delete(self, member):
memberdboperation = getAdapter(member, IDbOperation)
memberdboperation.delete()
memberwindow = MemberWindow()
Here is the components.py:
from zope.interface import implements
from interfaces import IBook
from interfaces import IMember
from interfaces import ICirculation
class Book(object):
implements(IBook)
barcode = ""
title = ""
author = ""
class Member(object):
implements(IMember)
number = ""
name = ""
class Circulation(object):
implements(ICirculation)
book = Book()
member = Member()
Here is the interfaces.py:
from zope.interface import Interface
from zope.interface import Attribute
class IBook(Interface):
barcode = Attribute("Barcode")
author = Attribute("Author of book")
title = Attribute("Title of book")
class IMember(Interface):
number = Attribute("ID number")
name = Attribute("Name of member")
class ICirculation(Interface):
book = Attribute("A book")
member = Attribute("A member")
class IRelationalDatabase(Interface):
def commit():
pass
def rollback():
pass
def cursor():
pass
def get_next_id():
pass
class IObjectDatabase(Interface):
def commit():
pass
def rollback():
pass
def container():
pass
def get_next_id():
pass
class IDbOperation(Interface):
def get():
pass
def add():
pass
def update():
pass
def delete():
pass
Here is the member.py:
from zope.interface import implements
from zope.component import getUtility
from zope.component import adapts
from components import Member
from interfaces import IRelationalDatabase
from interfaces import IObjectDatabase
from interfaces import IMember
from interfaces import IDbOperation
class MemberRDbOperation(object):
implements(IDbOperation)
adapts(IMember)
def __init__(self, member):
self.member = member
def get(self):
db = getUtility(IRelationalDatabase)
cr = db.cursor()
number = self.member.number
if number:
cr.execute("""SELECT
id,
number,
name
FROM members
WHERE number = ?""",
(number,))
else:
cr.execute("""SELECT
id,
number,
name
FROM members""")
rst = cr.fetchall()
cr.close()
members = []
for record in rst:
id = record['id']
number = record['number']
name = record['name']
member = Member()
member.id = id
member.number = number
member.name = name
members.append(member)
return members
def add(self):
db = getUtility(IRelationalDatabase)
cr = db.cursor()
next_id = db.get_next_id("members")
number = self.member.number
name = self.member.name
cr.execute("""INSERT INTO members
(id, number, name)
VALUES (?, ?, ?)""",
(next_id, number, name))
cr.close()
db.commit()
self.member.id = next_id
def update(self):
db = getUtility(IRelationalDatabase)
cr = db.cursor()
number = self.member.number
name = self.member.name
id = self.member.id
cr.execute("""UPDATE members
SET
number = ?,
name = ?
WHERE id = ?""",
(number, name, id))
cr.close()
db.commit()
def delete(self):
db = getUtility(IRelationalDatabase)
cr = db.cursor()
id = self.member.id
cr.execute("""DELETE FROM members
WHERE id = ?""",
(id,))
cr.close()
db.commit()
class MemberODbOperation(object):
implements(IDbOperation)
adapts(IMember)
def __init__(self, member):
self.member = member
def get(self):
db = getUtility(IObjectDatabase)
zcalibdb = db.container()
members = zcalibdb['members']
return members.values()
def add(self):
db = getUtility(IObjectDatabase)
zcalibdb = db.container()
members = zcalibdb['members']
number = self.member.number
if number in [x.number for x in members.values()]:
db.rollback()
raise Exception("Duplicate key")
next_id = db.get_next_id('members')
self.member.id = next_id
members[next_id] = self.member
db.commit()
def update(self):
db = getUtility(IObjectDatabase)
zcalibdb = db.container()
members = zcalibdb['members']
id = self.member.id
members[id] = self.member
db.commit()
def delete(self):
db = getUtility(IObjectDatabase)
zcalibdb = db.container()
members = zcalibdb['members']
id = self.member.id
del members[id]
db.commit()
This function helps to find the adapted interfaces.
Location: zope.component
Signature: adaptedBy(object)
Example:
>>> from zope.interface import implements
>>> from zope.component import adapts
>>> from zope.component import adaptedBy
>>> class FrontDeskNG(object):
...
... implements(IDesk)
... adapts(IGuest)
...
... def __init__(self, guest):
... self.guest = guest
>>> adaptedBy(FrontDeskNG)
(<InterfaceClass __builtin__.IGuest>,)
Adapters can be any callable object, you can use the adapter decorator to declare that a callable object adapts some interfaces (or classes)
Location: zope.component
Signature: adapter(*interfaces)
Example:
>>> from zope.interface import Attribute
>>> from zope.interface import Interface
>>> from zope.interface import implementer
>>> from zope.component import adapter
>>> from zope.interface import implements
>>> class IJob(Interface):
... """A job"""
>>> class Job(object):
... implements(IJob)
>>> class IPerson(Interface):
...
... name = Attribute("Name")
... job = Attribute("Job")
>>> class Person(object):
... implements(IPerson)
...
... name = None
... job = None
>>> @implementer(IJob)
... @adapter(IPerson)
... def personJob(person):
... return person.job
>>> jack = Person()
>>> jack.name = "Jack"
>>> jack.job = Job()
>>> personJob(jack) #doctest: +ELLIPSIS
<Job object at ...>
This function helps to declare adapter classes.
Location: zope.component
Signature: adapts(*interfaces)
Example:
>>> from zope.interface import implements
>>> from zope.component import adapts
>>> class FrontDeskNG(object):
...
... implements(IDesk)
... adapts(IGuest)
...
... def __init__(self, guest):
... self.guest = guest
...
... def register(self):
... next_id = get_next_id()
... bookings_db[next_id] = {
... 'name': guest.name,
... 'place': guest.place,
... 'phone': guest.phone
... }
Declare interfaces declared directly for an object. The arguments after the object are one or more interfaces. The interfaces given are added to the interfaces previously declared for the object.
Location: zope.interface
Signature: alsoProvides(object, *interfaces)
Example:
>>> from zope.interface import Attribute
>>> from zope.interface import Interface
>>> from zope.interface import implements
>>> from zope.interface import alsoProvides
>>> class IPerson(Interface):
...
... name = Attribute("Name of person")
>>> class IStudent(Interface):
...
... college = Attribute("Name of college")
>>> class Person(object):
...
... implements(IDesk)
... name = u""
>>> jack = Person()
>>> jack.name = "Jack"
>>> jack.college = "New College"
>>> alsoProvides(jack, IStudent)
You can test it like this:
>>> from zope.interface import providedBy
>>> IStudent in providedBy(jack)
True
Using this class, you can define normal attributes in an interface.
Location: zope.interface
Signature: Attribute(name, doc='')
See also: Interface
Example:
>>> from zope.interface import Attribute
>>> from zope.interface import Interface
>>> class IPerson(Interface):
...
... name = Attribute("Name of person")
... email = Attribute("Email Address")
Declare additional interfaces implemented for instances of a class. The arguments after the class are one or more interfaces. The interfaces given are added to any interfaces previously declared.
Location: zope.interface
Signature: classImplements(cls, *interfaces)
Example:
>>> from zope.interface import Attribute
>>> from zope.interface import Interface
>>> from zope.interface import implements
>>> from zope.interface import classImplements
>>> class IPerson(Interface):
...
... name = Attribute("Name of person")
>>> class IStudent(Interface):
...
... college = Attribute("Name of college")
>>> class Person(object):
...
... implements(IDesk)
... name = u""
... college = u""
>>> classImplements(Person, IStudent)
>>> jack = Person()
>>> jack.name = "Jack"
>>> jack.college = "New College"
You can test it like this:
>>> from zope.interface import providedBy
>>> IStudent in providedBy(jack)
True
Declare the only interfaces implemented by instances of a class. The arguments after the class are one or more interfaces. The interfaces given replace any previous declarations.
Location: zope.interface
Signature: classImplementsOnly(cls, *interfaces)
Example:
>>> from zope.interface import Attribute
>>> from zope.interface import Interface
>>> from zope.interface import implements
>>> from zope.interface import classImplementsOnly
>>> class IPerson(Interface):
...
... name = Attribute("Name of person")
>>> class IStudent(Interface):
...
... college = Attribute("Name of college")
>>> class Person(object):
...
... implements(IPerson)
... college = u""
>>> classImplementsOnly(Person, IStudent)
>>> jack = Person()
>>> jack.college = "New College"
You can test it like this:
>>> from zope.interface import providedBy
>>> IPerson in providedBy(jack)
False
>>> IStudent in providedBy(jack)
True
Normally if a class implements a particular interface, the instance of that class will provide the interface implemented by that class. But if you want a class to be provided by an interface, you can declare it using classProvides function.
Location: zope.interface
Signature: classProvides(*interfaces)
Example:
>>> from zope.interface import Attribute
>>> from zope.interface import Interface
>>> from zope.interface import classProvides
>>> class IPerson(Interface):
...
... name = Attribute("Name of person")
>>> class Person(object):
...
... classProvides(IPerson)
... name = u"Jack"
You can test it like this:
>>> from zope.interface import providedBy
>>> IPerson in providedBy(Person)
True
This is the exception raised when a component lookup fails.
Example:
>>> class IPerson(Interface):
...
... name = Attribute("Name of person")
>>> person = object()
>>> getAdapter(person, IPerson, 'not-exists') #doctest: +ELLIPSIS
Traceback (most recent call last):
...
ComponentLookupError: ...
Create an object using a factory.
Finds the named factory in the current site and calls it with the given arguments. If a matching factory cannot be found raises ComponentLookupError. Returns the created object.
A context keyword argument can be provided to cause the factory to be looked up in a location other than the current site. (Of course, this means that it is impossible to pass a keyword argument named "context" to the factory.
Location: zope.component
Signature: createObject(factory_name, *args, **kwargs)
Example:
>>> from zope.interface import Attribute
>>> from zope.interface import Interface
>>> from zope.interface import implements
>>> class IDatabase(Interface):
...
... def getConnection():
... """Return connection object"""
>>> class FakeDb(object):
...
... implements(IDatabase)
...
... def getConnection(self):
... return "connection"
>>> from zope.component.factory import Factory
>>> factory = Factory(FakeDb, 'FakeDb')
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> from zope.component.interfaces import IFactory
>>> gsm.registerUtility(factory, IFactory, 'fakedb')
>>> from zope.component import createObject
>>> createObject('fakedb') #doctest: +ELLIPSIS
<FakeDb object at ...>
Need not to use directly.
This function will return the interfaces directly provided by the given object.
Location: zope.interface
Signature: directlyProvidedBy(object)
Example:
>>> from zope.interface import Attribute
>>> from zope.interface import Interface
>>> class IPerson(Interface):
...
... name = Attribute("Name of person")
>>> class IStudent(Interface):
...
... college = Attribute("Name of college")
>>> class ISmartPerson(Interface):
... pass
>>> class Person(object):
...
... implements(IPerson)
... name = u""
>>> jack = Person()
>>> jack.name = u"Jack"
>>> jack.college = "New College"
>>> alsoProvides(jack, ISmartPerson, IStudent)
>>> from zope.interface import directlyProvidedBy
>>> jack_dp = directlyProvidedBy(jack)
>>> IPerson in jack_dp.interfaces()
False
>>> IStudent in jack_dp.interfaces()
True
>>> ISmartPerson in jack_dp.interfaces()
True
Declare interfaces declared directly for an object. The arguments after the object are one or more interfaces. The interfaces given replace interfaces previously declared for the object.
Location: zope.interface
Signature: directlyProvides(object, *interfaces)
Example:
>>> from zope.interface import Attribute
>>> from zope.interface import Interface
>>> class IPerson(Interface):
...
... name = Attribute("Name of person")
>>> class IStudent(Interface):
...
... college = Attribute("Name of college")
>>> class ISmartPerson(Interface):
... pass
>>> class Person(object):
...
... implements(IPerson)
... name = u""
>>> jack = Person()
>>> jack.name = u"Jack"
>>> jack.college = "New College"
>>> alsoProvides(jack, ISmartPerson, IStudent)
>>> from zope.interface import directlyProvidedBy
>>> jack_dp = directlyProvidedBy(jack)
>>> ISmartPerson in jack_dp.interfaces()
True
>>> IPerson in jack_dp.interfaces()
False
>>> IStudent in jack_dp.interfaces()
True
>>> from zope.interface import providedBy
>>> ISmartPerson in providedBy(jack)
True
>>> from zope.interface import directlyProvides
>>> directlyProvides(jack, IStudent)
>>> jack_dp = directlyProvidedBy(jack)
>>> ISmartPerson in jack_dp.interfaces()
False
>>> IPerson in jack_dp.interfaces()
False
>>> IStudent in jack_dp.interfaces()
True
>>> ISmartPerson in providedBy(jack)
False
Get a named adapter to an interface for an object. Returns an adapter that can adapt object to interface. If a matching adapter cannot be found, raises ComponentLookupError .
Location: zope.interface
Signature: getAdapter(object, interface=Interface, name=u'', context=None)
Example:
>>> from zope.interface import Attribute
>>> from zope.interface import Interface
>>> class IDesk(Interface):
... """A frontdesk will register object's details"""
...
... def register():
... """Register object's details"""
...
>>> from zope.interface import implements
>>> from zope.component import adapts
>>> class FrontDeskNG(object):
...
... implements(IDesk)
... adapts(IGuest)
...
... def __init__(self, guest):
... self.guest = guest
...
... def register(self):
... next_id = get_next_id()
... bookings_db[next_id] = {
... 'name': guest.name,
... 'place': guest.place,
... 'phone': guest.phone
... }
>>> class Guest(object):
...
... implements(IGuest)
...
... def __init__(self, name, place):
... self.name = name
... self.place = place
>>> jack = Guest("Jack", "Bangalore")
>>> jack_frontdesk = FrontDeskNG(jack)
>>> IDesk.providedBy(jack_frontdesk)
True
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> gsm.registerAdapter(FrontDeskNG,
... (IGuest,), IDesk, 'ng')
>>> getAdapter(jack, IDesk, 'ng') #doctest: +ELLIPSIS
<FrontDeskNG object at ...>
Instead of this function, use context argument of getAdapter function.
Location: zope.component
Signature: getAdapterInContext(object, interface, context)
See also: queryAdapterInContext
Example:
>>> from zope.component.globalregistry import BaseGlobalComponents
>>> from zope.component import IComponentLookup
>>> sm = BaseGlobalComponents()
>>> class Context(object):
... def __init__(self, sm):
... self.sm = sm
... def __conform__(self, interface):
... if interface.isOrExtends(IComponentLookup):
... return self.sm
>>> context = Context(sm)
>>> from zope.interface import Attribute
>>> from zope.interface import Interface
>>> class IDesk(Interface):
... """A frontdesk will register object's details"""
...
... def register():
... """Register object's details"""
...
>>> from zope.interface import implements
>>> from zope.component import adapts
>>> class FrontDeskNG(object):
...
... implements(IDesk)
... adapts(IGuest)
...
... def __init__(self, guest):
... self.guest = guest
...
... def register(self):
... next_id = get_next_id()
... bookings_db[next_id] = {
... 'name': guest.name,
... 'place': guest.place,
... 'phone': guest.phone
... }
>>> class Guest(object):
...
... implements(IGuest)
...
... def __init__(self, name, place):
... self.name = name
... self.place = place
>>> jack = Guest("Jack", "Bangalore")
>>> jack_frontdesk = FrontDeskNG(jack)
>>> IDesk.providedBy(jack_frontdesk)
True
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> sm.registerAdapter(FrontDeskNG,
... (IGuest,), IDesk)
>>> from zope.component import getAdapterInContext
>>> getAdapterInContext(jack, IDesk, sm) #doctest: +ELLIPSIS
<FrontDeskNG object at ...>
Look for all matching adapters to a provided interface for objects. Return a list of adapters that match. If an adapter is named, only the most specific adapter of a given name is returned.
Location: zope.component
Signature: getAdapters(objects, provided, context=None)
Example:
>>> from zope.interface import implements
>>> from zope.component import adapts
>>> class FrontDeskNG(object):
...
... implements(IDesk)
... adapts(IGuest)
...
... def __init__(self, guest):
... self.guest = guest
...
... def register(self):
... next_id = get_next_id()
... bookings_db[next_id] = {
... 'name': guest.name,
... 'place': guest.place,
... 'phone': guest.phone
... }
>>> jack = Guest("Jack", "Bangalore")
>>> jack_frontdesk = FrontDeskNG(jack)
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> gsm.registerAdapter(FrontDeskNG, name='ng')
>>> from zope.component import getAdapters
>>> list(getAdapters((jack,), IDesk)) #doctest: +ELLIPSIS
[(u'ng', <FrontDeskNG object at ...>)]
Return all registered utilities for an interface. This includes overridden utilities. The returned value is an iterable of utility instances.
Location: zope.component
Signature: getAllUtilitiesRegisteredFor(interface)
Example:
>>> from zope.interface import Interface
>>> from zope.interface import implements
>>> class IGreeter(Interface):
... def greet(name):
... "say hello"
>>> class Greeter(object):
...
... implements(IGreeter)
...
... def greet(self, name):
... print "Hello", name
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> greet = Greeter()
>>> gsm.registerUtility(greet, IGreeter)
>>> from zope.component import getAllUtilitiesRegisteredFor
>>> getAllUtilitiesRegisteredFor(IGreeter) #doctest: +ELLIPSIS
[<Greeter object at ...>]
Return a tuple (name, factory) of registered factories that create objects which implement the given interface.
Location: zope.component
Signature: getFactoriesFor(interface, context=None)
Example:
>>> from zope.interface import Attribute
>>> from zope.interface import Interface
>>> from zope.interface import implements
>>> class IDatabase(Interface):
...
... def getConnection():
... """Return connection object"""
>>> class FakeDb(object):
...
... implements(IDatabase)
...
... def getConnection(self):
... return "connection"
>>> from zope.component.factory import Factory
>>> factory = Factory(FakeDb, 'FakeDb')
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> from zope.component.interfaces import IFactory
>>> gsm.registerUtility(factory, IFactory, 'fakedb')
>>> from zope.component import getFactoriesFor
>>> list(getFactoriesFor(IDatabase))
[(u'fakedb', <Factory for <class 'FakeDb'>>)]
Get interfaces implemented by a factory. Finds the factory of the given name that is nearest to the context, and returns the interface or interface tuple that object instances created by the named factory will implement.
Location: zope.component
Signature: getFactoryInterfaces(name, context=None)
Example:
>>> from zope.interface import Attribute
>>> from zope.interface import Interface
>>> from zope.interface import implements
>>> class IDatabase(Interface):
...
... def getConnection():
... """Return connection object"""
>>> class FakeDb(object):
...
... implements(IDatabase)
...
... def getConnection(self):
... return "connection"
>>> from zope.component.factory import Factory
>>> factory = Factory(FakeDb, 'FakeDb')
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> from zope.component.interfaces import IFactory
>>> gsm.registerUtility(factory, IFactory, 'fakedb')
>>> from zope.component import getFactoryInterfaces
>>> getFactoryInterfaces('fakedb')
<implementedBy __builtin__.FakeDb>
Return the global site manager. This function should never fail and always return an object that provides IGlobalSiteManager
Location: zope.component
Signature: getGlobalSiteManager()
Example:
>>> from zope.component import getGlobalSiteManager
>>> from zope.component import globalSiteManager
>>> gsm = getGlobalSiteManager()
>>> gsm is globalSiteManager
True
Look for a multi-adapter to an interface for an objects. Returns a multi-adapter that can adapt objects to interface. If a matching adapter cannot be found, raises ComponentLookupError. The name consisting of an empty string is reserved for unnamed adapters. The unnamed adapter methods will often call the named adapter methods with an empty string for a name.
Location: zope.component
Signature: getMultiAdapter(objects, interface=Interface, name='', context=None)
See also: queryMultiAdapter
Example:
>>> from zope.interface import Interface
>>> from zope.interface import implements
>>> from zope.component import adapts
>>> class IAdapteeOne(Interface):
... pass
>>> class IAdapteeTwo(Interface):
... pass
>>> class IFunctionality(Interface):
... pass
>>> class MyFunctionality(object):
... implements(IFunctionality)
... adapts(IAdapteeOne, IAdapteeTwo)
...
... def __init__(self, one, two):
... self.one = one
... self.two = two
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> gsm.registerAdapter(MyFunctionality)
>>> class One(object):
... implements(IAdapteeOne)
>>> class Two(object):
... implements(IAdapteeTwo)
>>> one = One()
>>> two = Two()
>>> from zope.component import getMultiAdapter
>>> getMultiAdapter((one,two), IFunctionality) #doctest: +ELLIPSIS
<MyFunctionality object at ...>
>>> myfunctionality = getMultiAdapter((one,two), IFunctionality)
>>> myfunctionality.one #doctest: +ELLIPSIS
<One object at ...>
>>> myfunctionality.two #doctest: +ELLIPSIS
<Two object at ...>
Get the nearest site manager in the given context. If context is None, return the global site manager. If the context is not None, it is expected that an adapter from the context to IComponentLookup can be found. If no adapter is found, a ComponentLookupError is raised.
Location: zope.component
Signature: getSiteManager(context=None)
Example 1:
>>> from zope.component.globalregistry import BaseGlobalComponents
>>> from zope.component import IComponentLookup
>>> sm = BaseGlobalComponents()
>>> class Context(object):
... def __init__(self, sm):
... self.sm = sm
... def __conform__(self, interface):
... if interface.isOrExtends(IComponentLookup):
... return self.sm
>>> context = Context(sm)
>>> from zope.component import getSiteManager
>>> lsm = getSiteManager(context)
>>> lsm is sm
True
Example 2:
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> sm = getSiteManager()
>>> gsm is sm
True
Look up the registered utilities that provide an interface. Returns an iterable of name-utility pairs.
Location: zope.component
Signature: getUtilitiesFor(interface)
Example:
>>> from zope.interface import Interface
>>> from zope.interface import implements
>>> class IGreeter(Interface):
... def greet(name):
... "say hello"
>>> class Greeter(object):
...
... implements(IGreeter)
...
... def greet(self, name):
... print "Hello", name
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> greet = Greeter()
>>> gsm.registerUtility(greet, IGreeter)
>>> from zope.component import getUtilitiesFor
>>> list(getUtilitiesFor(IGreeter)) #doctest: +ELLIPSIS
[(u'', <Greeter object at ...>)]
Get the utility that provides interface. Returns the nearest utility to the context that implements the specified interface. If one is not found, raises ComponentLookupError.
Location: zope.component
Signature: getUtility(interface, name='', context=None)
Example:
>>> from zope.interface import Interface
>>> from zope.interface import implements
>>> class IGreeter(Interface):
... def greet(name):
... "say hello"
>>> class Greeter(object):
...
... implements(IGreeter)
...
... def greet(self, name):
... return "Hello " + name
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> greet = Greeter()
>>> gsm.registerUtility(greet, IGreeter)
>>> from zope.component import getUtility
>>> getUtility(IGreeter).greet('Jack')
'Hello Jack'
Call all of the handlers for the given objects. Handlers are subscription adapter factories that don't produce anything. They do all of their work when called. Handlers are typically used to handle events.
Location: zope.component
Signature: handle(*objects)
Example:
>>> import datetime
>>> def documentCreated(event):
... event.doc.created = datetime.datetime.utcnow()
>>> from zope.interface import Interface
>>> from zope.interface import Attribute
>>> from zope.interface import implements
>>> class IDocumentCreated(Interface):
... doc = Attribute("The document that was created")
>>> class DocumentCreated(object):
... implements(IDocumentCreated)
...
... def __init__(self, doc):
... self.doc = doc
>>> def documentCreated(event):
... event.doc.created = datetime.datetime.utcnow()
>>> from zope.component import adapter
>>> @adapter(IDocumentCreated)
... def documentCreated(event):
... event.doc.created = datetime.datetime.utcnow()
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> gsm.registerHandler(documentCreated)
>>> from zope.component import handle
>>> handle(DocumentCreated(doc))
>>> doc.created.__class__.__name__
'datetime'
Return the interfaces implemented for a class' instances.
Location: zope.interface
Signature: implementedBy(class_)
Example 1:
>>> from zope.interface import Interface
>>> from zope.interface import implements
>>> class IGreeter(Interface):
... def greet(name):
... "say hello"
>>> class Greeter(object):
...
... implements(IGreeter)
...
... def greet(self, name):
... print "Hello", name
>>> from zope.interface import implementedBy
>>> implementedBy(Greeter)
<implementedBy __builtin__.Greeter>
Example 2:
>>> from zope.interface import Attribute
>>> from zope.interface import Interface
>>> from zope.interface import implements
>>> class IPerson(Interface):
... name = Attribute("Name of person")
>>> class ISpecial(Interface):
... pass
>>> class Person(object):
... implements(IPerson)
... name = u""
>>> from zope.interface import classImplements
>>> classImplements(Person, ISpecial)
>>> from zope.interface import implementedBy
To get a list of all interfaces implemented by that class::
>>> [x.__name__ for x in implementedBy(Person)]
['IPerson', 'ISpecial']
Create a decorator for declaring interfaces implemented by a factory. A callable is returned that makes an implements declaration on objects passed to it.
Location: zope.interface
Signature: implementer(*interfaces)
Example:
>>> from zope.interface import implementer
>>> class IFoo(Interface):
... pass
>>> class Foo(object):
... implements(IFoo)
>>> @implementer(IFoo)
... def foocreator():
... foo = Foo()
... return foo
>>> list(implementedBy(foocreator))
[<InterfaceClass __builtin__.IFoo>]
Declare interfaces implemented by instances of a class This function is called in a class definition. The arguments are one or more interfaces. The interfaces given are added to any interfaces previously declared. Previous declarations include declarations for base classes unless implementsOnly was used.
Location: zope.interface
Signature: implements(*interfaces)
Example:
>>> from zope.interface import Attribute
>>> from zope.interface import Interface
>>> from zope.interface import implements
>>> class IPerson(Interface):
...
... name = Attribute("Name of person")
>>> class Person(object):
...
... implements(IPerson)
... name = u""
>>> jack = Person()
>>> jack.name = "Jack"
You can test it like this:
>>> from zope.interface import providedBy
>>> IPerson in providedBy(jack)
True
Declare the only interfaces implemented by instances of a class. This function is called in a class definition. The arguments are one or more interfaces. Previous declarations including declarations for base classes are overridden.
Location: zope.interface
Signature: implementsOnly(*interfaces)
Example:
>>> from zope.interface import Attribute
>>> from zope.interface import Interface
>>> from zope.interface import implements
>>> from zope.interface import implementsOnly
>>> class IPerson(Interface):
...
... name = Attribute("Name of person")
>>> class IStudent(Interface):
...
... college = Attribute("Name of college")
>>> class Person(object):
...
... implements(IPerson)
... name = u""
>>> class NewPerson(Person):
... implementsOnly(IStudent)
... college = u""
>>> jack = NewPerson()
>>> jack.college = "New College"
You can test it like this:
>>> from zope.interface import providedBy
>>> IPerson in providedBy(jack)
False
>>> IStudent in providedBy(jack)
True
Using this class, you can define an interface. To define an interface, just inherit from Interface class.
Location: zope.interface
Signature: Interface(name, doc='')
Example 1:
>>> from zope.interface import Attribute
>>> from zope.interface import Interface
>>> class IPerson(Interface):
...
... name = Attribute("Name of person")
... email = Attribute("Email Address")
Example 2:
>>> from zope.interface import Interface
>>> class IHost(Interface):
...
... def goodmorning(guest):
... """Say good morning to guest"""
Declare interfaces provided by a module. This function is used in a module definition. The arguments are one or more interfaces. The given interfaces are used to create the module's direct-object interface specification. An error will be raised if the module already has an interface specification. In other words, it is an error to call this function more than once in a module definition.
This function is provided for convenience. It provides a more convenient way to call directlyProvides for a module.
Location: zope.interface
Signature: moduleProvides(*interfaces)
See also: directlyProvides
You can see an example usage in zope.component source itself. The __init__.py file has a statement like this:
moduleProvides(IComponentArchitecture,
IComponentRegistrationConvenience)
So, the zope.component provides two interfaces: IComponentArchitecture and IComponentRegistrationConvenience.
Remove an interface from the list of an object's directly provided interfaces.
Location: zope.interface
Signature: noLongerProvides(object, interface)
Example:
>>> from zope.interface import Attribute
>>> from zope.interface import Interface
>>> from zope.interface import implements
>>> from zope.interface import classImplements
>>> class IPerson(Interface):
...
... name = Attribute("Name of person")
>>> class IStudent(Interface):
...
... college = Attribute("Name of college")
>>> class Person(object):
...
... implements(IPerson)
... name = u""
>>> jack = Person()
>>> jack.name = "Jack"
>>> jack.college = "New College"
>>> directlyProvides(jack, IStudent)
You can test it like this:
>>> from zope.interface import providedBy
>>> IPerson in providedBy(jack)
True
>>> IStudent in providedBy(jack)
True
>>> from zope.interface import noLongerProvides
>>> noLongerProvides(jack, IStudent)
>>> IPerson in providedBy(jack)
True
>>> IStudent in providedBy(jack)
False
It is recommended to use registerAdapter .
It is recommended to use registerHandler .
It is recommended to use registerSubscriptionAdapter .
It is recommended to use registerUtility .
Test whether the interface is implemented by the object. Return true if the object asserts that it implements the interface, including asserting that it implements an extended interface.
Location: zope.interface
Signature: providedBy(object)
Example 1:
>>> from zope.interface import Attribute
>>> from zope.interface import Interface
>>> from zope.interface import implements
>>> class IPerson(Interface):
...
... name = Attribute("Name of person")
>>> class Person(object):
...
... implements(IPerson)
... name = u""
>>> jack = Person()
>>> jack.name = "Jack"
You can test it like this:
>>> from zope.interface import providedBy
>>> IPerson in providedBy(jack)
True
Example 2:
>>> from zope.interface import Attribute
>>> from zope.interface import Interface
>>> from zope.interface import implements
>>> class IPerson(Interface):
... name = Attribute("Name of person")
>>> class ISpecial(Interface):
... pass
>>> class Person(object):
... implements(IPerson)
... name = u""
>>> from zope.interface import classImplements
>>> classImplements(Person, ISpecial)
>>> from zope.interface import providedBy
>>> jack = Person()
>>> jack.name = "Jack"
To get a list of all interfaces provided by that object::
>>> [x.__name__ for x in providedBy(jack)]
['IPerson', 'ISpecial']
Look for a named adapter to an interface for an object. Returns an adapter that can adapt object to interface. If a matching adapter cannot be found, returns the default.
Location: zope.component
Signature: queryAdapter(object, interface=Interface, name=u'', default=None, context=None)
Example:
>>> from zope.interface import Attribute
>>> from zope.interface import Interface
>>> class IDesk(Interface):
... """A frontdesk will register object's details"""
...
... def register():
... """Register object's details"""
...
>>> from zope.interface import implements
>>> from zope.component import adapts
>>> class FrontDeskNG(object):
...
... implements(IDesk)
... adapts(IGuest)
...
... def __init__(self, guest):
... self.guest = guest
...
... def register(self):
... next_id = get_next_id()
... bookings_db[next_id] = {
... 'name': guest.name,
... 'place': guest.place,
... 'phone': guest.phone
... }
>>> class Guest(object):
...
... implements(IGuest)
...
... def __init__(self, name, place):
... self.name = name
... self.place = place
>>> jack = Guest("Jack", "Bangalore")
>>> jack_frontdesk = FrontDeskNG(jack)
>>> IDesk.providedBy(jack_frontdesk)
True
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> gsm.registerAdapter(FrontDeskNG,
... (IGuest,), IDesk, 'ng')
>>> queryAdapter(jack, IDesk, 'ng') #doctest: +ELLIPSIS
<FrontDeskNG object at ...>
Look for a special adapter to an interface for an object.
NOTE: This method should only be used if a custom context needs to be provided to provide custom component lookup. Otherwise, call the interface, as in:
interface(object, default)
Returns an adapter that can adapt object to interface. If a matching adapter cannot be found, returns the default.
Context is adapted to IServiceService, and this adapter's 'Adapters' service is used.
If the object has a __conform__ method, this method will be called with the requested interface. If the method returns a non-None value, that value will be returned. Otherwise, if the object already implements the interface, the object will be returned.
Location: zope.component
Signature: queryAdapterInContext(object, interface, context, default=None)
See also: getAdapterInContext
Example:
>>> from zope.component.globalregistry import BaseGlobalComponents
>>> from zope.component import IComponentLookup
>>> sm = BaseGlobalComponents()
>>> class Context(object):
... def __init__(self, sm):
... self.sm = sm
... def __conform__(self, interface):
... if interface.isOrExtends(IComponentLookup):
... return self.sm
>>> context = Context(sm)
>>> from zope.interface import Attribute
>>> from zope.interface import Interface
>>> class IDesk(Interface):
... """A frontdesk will register object's details"""
...
... def register():
... """Register object's details"""
...
>>> from zope.interface import implements
>>> from zope.component import adapts
>>> class FrontDeskNG(object):
...
... implements(IDesk)
... adapts(IGuest)
...
... def __init__(self, guest):
... self.guest = guest
...
... def register(self):
... next_id = get_next_id()
... bookings_db[next_id] = {
... 'name': guest.name,
... 'place': guest.place,
... 'phone': guest.phone
... }
>>> class Guest(object):
...
... implements(IGuest)
...
... def __init__(self, name, place):
... self.name = name
... self.place = place
>>> jack = Guest("Jack", "Bangalore")
>>> jack_frontdesk = FrontDeskNG(jack)
>>> IDesk.providedBy(jack_frontdesk)
True
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> sm.registerAdapter(FrontDeskNG,
... (IGuest,), IDesk)
>>> from zope.component import queryAdapterInContext
>>> queryAdapterInContext(jack, IDesk, sm) #doctest: +ELLIPSIS
<FrontDeskNG object at ...>
Look for a multi-adapter to an interface for objects. Returns a multi-adapter that can adapt objects to interface. If a matching adapter cannot be found, returns the default. The name consisting of an empty string is reserved for unnamed adapters. The unnamed adapter methods will often call the named adapter methods with an empty string for a name.
Location: zope.component
Signature: queryMultiAdapter(objects, interface=Interface, name=u'', default=None, context=None)
See also: getMultiAdapter
Example:
>>> from zope.interface import Interface
>>> from zope.interface import implements
>>> from zope.component import adapts
>>> class IAdapteeOne(Interface):
... pass
>>> class IAdapteeTwo(Interface):
... pass
>>> class IFunctionality(Interface):
... pass
>>> class MyFunctionality(object):
... implements(IFunctionality)
... adapts(IAdapteeOne, IAdapteeTwo)
...
... def __init__(self, one, two):
... self.one = one
... self.two = two
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> gsm.registerAdapter(MyFunctionality)
>>> class One(object):
... implements(IAdapteeOne)
>>> class Two(object):
... implements(IAdapteeTwo)
>>> one = One()
>>> two = Two()
>>> from zope.component import queryMultiAdapter
>>> getMultiAdapter((one,two), IFunctionality) #doctest: +ELLIPSIS
<MyFunctionality object at ...>
>>> myfunctionality = queryMultiAdapter((one,two), IFunctionality)
>>> myfunctionality.one #doctest: +ELLIPSIS
<One object at ...>
>>> myfunctionality.two #doctest: +ELLIPSIS
<Two object at ...>
This function is used to look up a utility that provides an interface. If one is not found, returns default.
Location: zope.component
Signature: queryUtility(interface, name='', default=None)
Example:
>>> from zope.interface import Interface
>>> from zope.interface import implements
>>> class IGreeter(Interface):
... def greet(name):
... "say hello"
>>> class Greeter(object):
...
... implements(IGreeter)
...
... def greet(self, name):
... return "Hello " + name
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> greet = Greeter()
>>> gsm.registerUtility(greet, IGreeter)
>>> from zope.component import queryUtility
>>> queryUtility(IGreeter).greet('Jack')
'Hello Jack'
This function is used to register an adapter factory.
Location: zope.component - IComponentRegistry
Signature: registerAdapter(factory, required=None, provided=None, name=u'', info=u'')
See also: unregisterAdapter
Example:
>>> from zope.interface import Attribute
>>> from zope.interface import Interface
>>> class IDesk(Interface):
... """A frontdesk will register object's details"""
...
... def register():
... """Register object's details"""
...
>>> from zope.interface import implements
>>> from zope.component import adapts
>>> class FrontDeskNG(object):
...
... implements(IDesk)
... adapts(IGuest)
...
... def __init__(self, guest):
... self.guest = guest
...
... def register(self):
... next_id = get_next_id()
... bookings_db[next_id] = {
... 'name': guest.name,
... 'place': guest.place,
... 'phone': guest.phone
... }
>>> class Guest(object):
...
... implements(IGuest)
...
... def __init__(self, name, place):
... self.name = name
... self.place = place
>>> jack = Guest("Jack", "Bangalore")
>>> jack_frontdesk = FrontDeskNG(jack)
>>> IDesk.providedBy(jack_frontdesk)
True
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> gsm.registerAdapter(FrontDeskNG,
... (IGuest,), IDesk, 'ng')
You can test it like this:
>>> queryAdapter(jack, IDesk, 'ng') #doctest: +ELLIPSIS
<FrontDeskNG object at ...>
Return an iterable of IAdapterRegistrations. These registrations describe the current adapter registrations in the object.
Location: zope.component - IComponentRegistry
Signature: registeredAdapters()
Example:
>>> from zope.interface import Attribute
>>> from zope.interface import Interface
>>> class IDesk(Interface):
... """A frontdesk will register object's details"""
...
... def register():
... """Register object's details"""
...
>>> from zope.interface import implements
>>> from zope.component import adapts
>>> class FrontDeskNG(object):
...
... implements(IDesk)
... adapts(IGuest)
...
... def __init__(self, guest):
... self.guest = guest
...
... def register(self):
... next_id = get_next_id()
... bookings_db[next_id] = {
... 'name': guest.name,
... 'place': guest.place,
... 'phone': guest.phone
... }
>>> class Guest(object):
...
... implements(IGuest)
...
... def __init__(self, name, place):
... self.name = name
... self.place = place
>>> jack = Guest("Jack", "Bangalore")
>>> jack_frontdesk = FrontDeskNG(jack)
>>> IDesk.providedBy(jack_frontdesk)
True
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> gsm.registerAdapter(FrontDeskNG,
... (IGuest,), IDesk, 'ng2')
>>> reg_adapter = list(gsm.registeredAdapters())
>>> 'ng2' in [x.name for x in reg_adapter]
True
Return an iterable of IHandlerRegistrations. These registrations describe the current handler registrations in the object.
Location: zope.component - IComponentRegistry
Signature: registeredHandlers()
Example:
>>> import datetime
>>> def documentCreated(event):
... event.doc.created = datetime.datetime.utcnow()
>>> from zope.interface import Interface
>>> from zope.interface import Attribute
>>> from zope.interface import implements
>>> class IDocumentCreated(Interface):
... doc = Attribute("The document that was created")
>>> class DocumentCreated(object):
... implements(IDocumentCreated)
...
... def __init__(self, doc):
... self.doc = doc
>>> def documentCreated(event):
... event.doc.created = datetime.datetime.utcnow()
>>> from zope.component import adapter
>>> @adapter(IDocumentCreated)
... def documentCreated(event):
... event.doc.created = datetime.datetime.utcnow()
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> gsm.registerHandler(documentCreated, info='ng3')
>>> reg_adapter = list(gsm.registeredHandlers())
>>> 'ng3' in [x.info for x in reg_adapter]
True
>>> gsm.registerHandler(documentCreated, name='ng4')
Traceback (most recent call last):
...
TypeError: Named handlers are not yet supported
Return an iterable of ISubscriptionAdapterRegistrations. These registrations describe the current subscription adapter registrations in the object.
Location: zope.component - IComponentRegistry
Signature: registeredSubscriptionAdapters()
Example:
>>> from zope.interface import Interface
>>> from zope.interface import Attribute
>>> from zope.interface import implements
>>> class IValidate(Interface):
... def validate(ob):
... """Determine whether the object is valid
...
... Return a string describing a validation problem.
... An empty string is returned to indicate that the
... object is valid.
... """
>>> class IDocument(Interface):
... summary = Attribute("Document summary")
... body = Attribute("Document text")
>>> class Document(object):
... implements(IDocument)
... def __init__(self, summary, body):
... self.summary, self.body = summary, body
>>> from zope.component import adapts
>>> class AdequateLength(object):
...
... adapts(IDocument)
... implements(IValidate)
...
... def __init__(self, doc):
... self.doc = doc
...
... def validate(self):
... if len(self.doc.body) < 1000:
... return 'too short'
... else:
... return ''
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> gsm.registerSubscriptionAdapter(AdequateLength, info='ng4')
>>> reg_adapter = list(gsm.registeredSubscriptionAdapters())
>>> 'ng4' in [x.info for x in reg_adapter]
True
This function return an iterable of IUtilityRegistrations. These registrations describe the current utility registrations in the object.
Location: zope.component - IComponentRegistry
Signature: registeredUtilities()
Example:
>>> from zope.interface import Interface
>>> from zope.interface import implements
>>> class IGreeter(Interface):
... def greet(name):
... "say hello"
>>> class Greeter(object):
...
... implements(IGreeter)
...
... def greet(self, name):
... print "Hello", name
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> greet = Greeter()
>>> gsm.registerUtility(greet, info='ng5')
>>> reg_adapter = list(gsm.registeredUtilities())
>>> 'ng5' in [x.info for x in reg_adapter]
True
This function is used to register a handler. A handler is a subscriber that doesn't compute an adapter but performs some function when called.
Location: zope.component - IComponentRegistry
Signature: registerHandler(handler, required=None, name=u'', info='')
See also: unregisterHandler
Note: In the current implementation of zope.component doesn't support name attribute.
Example:
>>> import datetime
>>> def documentCreated(event):
... event.doc.created = datetime.datetime.utcnow()
>>> from zope.interface import Interface
>>> from zope.interface import Attribute
>>> from zope.interface import implements
>>> class IDocumentCreated(Interface):
... doc = Attribute("The document that was created")
>>> class DocumentCreated(object):
... implements(IDocumentCreated)
...
... def __init__(self, doc):
... self.doc = doc
>>> def documentCreated(event):
... event.doc.created = datetime.datetime.utcnow()
>>> from zope.component import adapter
>>> @adapter(IDocumentCreated)
... def documentCreated(event):
... event.doc.created = datetime.datetime.utcnow()
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> gsm.registerHandler(documentCreated)
>>> from zope.component import handle
>>> handle(DocumentCreated(doc))
>>> doc.created.__class__.__name__
'datetime'
This function is used to register a subscriber factory.
Location: zope.component - IComponentRegistry
Signature: registerSubscriptionAdapter(factory, required=None, provides=None, name=u'', info='')
See also: unregisterSubscriptionAdapter
Example:
>>> from zope.interface import Interface
>>> from zope.interface import Attribute
>>> from zope.interface import implements
>>> class IValidate(Interface):
... def validate(ob):
... """Determine whether the object is valid
...
... Return a string describing a validation problem.
... An empty string is returned to indicate that the
... object is valid.
... """
>>> class IDocument(Interface):
... summary = Attribute("Document summary")
... body = Attribute("Document text")
>>> class Document(object):
... implements(IDocument)
... def __init__(self, summary, body):
... self.summary, self.body = summary, body
>>> from zope.component import adapts
>>> class AdequateLength(object):
...
... adapts(IDocument)
... implements(IValidate)
...
... def __init__(self, doc):
... self.doc = doc
...
... def validate(self):
... if len(self.doc.body) < 1000:
... return 'too short'
... else:
... return ''
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> gsm.registerSubscriptionAdapter(AdequateLength)
This function is used to register a utility.
Location: zope.component - IComponentRegistry
Signature: registerUtility(component, provided=None, name=u'', info=u'')
See also: unregisterUtility
Example:
>>> from zope.interface import Interface
>>> from zope.interface import implements
>>> class IGreeter(Interface):
... def greet(name):
... "say hello"
>>> class Greeter(object):
...
... implements(IGreeter)
...
... def greet(self, name):
... print "Hello", name
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> greet = Greeter()
>>> gsm.registerUtility(greet)
This function is used to get subscribers. Subscribers are returned that provide the provided interface and that depend on and are computed from the sequence of required objects.
Location: zope.component - IComponentRegistry
Signature: subscribers(required, provided, context=None)
Example:
>>> from zope.interface import Interface
>>> from zope.interface import Attribute
>>> from zope.interface import implements
>>> class IValidate(Interface):
... def validate(ob):
... """Determine whether the object is valid
...
... Return a string describing a validation problem.
... An empty string is returned to indicate that the
... object is valid.
... """
>>> class IDocument(Interface):
... summary = Attribute("Document summary")
... body = Attribute("Document text")
>>> class Document(object):
... implements(IDocument)
... def __init__(self, summary, body):
... self.summary, self.body = summary, body
>>> from zope.component import adapts
>>> class SingleLineSummary:
... adapts(IDocument)
... implements(IValidate)
...
... def __init__(self, doc):
... self.doc = doc
...
... def validate(self):
... if '\n' in self.doc.summary:
... return 'Summary should only have one line'
... else:
... return ''
>>> class AdequateLength(object):
... adapts(IDocument)
... implements(IValidate)
...
... def __init__(self, doc):
... self.doc = doc
...
... def validate(self):
... if len(self.doc.body) < 1000:
... return 'too short'
... else:
... return ''
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> gsm.registerSubscriptionAdapter(SingleLineSummary)
>>> gsm.registerSubscriptionAdapter(AdequateLength)
>>> from zope.component import subscribers
>>> doc = Document("A\nDocument", "blah")
>>> [adapter.validate()
... for adapter in subscribers([doc], IValidate)
... if adapter.validate()]
['Summary should only have one line', 'too short']
>>> doc = Document("A\nDocument", "blah" * 1000)
>>> [adapter.validate()
... for adapter in subscribers([doc], IValidate)
... if adapter.validate()]
['Summary should only have one line']
>>> doc = Document("A Document", "blah")
>>> [adapter.validate()
... for adapter in subscribers([doc], IValidate)
... if adapter.validate()]
['too short']
This function is used to unregister an adapter factory. A boolean is returned indicating whether the registry was changed. If the given component is None and there is no component registered, or if the given component is not None and is not registered, then the function returns False, otherwise it returns True.
Location: zope.component - IComponentRegistry
Signature: unregisterAdapter(factory=None, required=None, provided=None, name=u'')
See also: registerAdapter
Example:
>>> from zope.interface import Attribute
>>> from zope.interface import Interface
>>> class IDesk(Interface):
... """A frontdesk will register object's details"""
...
... def register():
... """Register object's details"""
...
>>> from zope.interface import implements
>>> from zope.component import adapts
>>> class FrontDeskNG(object):
...
... implements(IDesk)
... adapts(IGuest)
...
... def __init__(self, guest):
... self.guest = guest
...
... def register(self):
... next_id = get_next_id()
... bookings_db[next_id] = {
... 'name': guest.name,
... 'place': guest.place,
... 'phone': guest.phone
... }
>>> class Guest(object):
...
... implements(IGuest)
...
... def __init__(self, name, place):
... self.name = name
... self.place = place
>>> jack = Guest("Jack", "Bangalore")
>>> jack_frontdesk = FrontDeskNG(jack)
>>> IDesk.providedBy(jack_frontdesk)
True
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> gsm.registerAdapter(FrontDeskNG,
... (IGuest,), IDesk, 'ng6')
You can test it like this:
>>> queryAdapter(jack, IDesk, 'ng6') #doctest: +ELLIPSIS
<FrontDeskNG object at ...>
Now unregister:
>>> gsm.unregisterAdapter(FrontDeskNG, name='ng6')
True
After unregistration:
>>> print queryAdapter(jack, IDesk, 'ng6')
None
This function is used for unregistering a handler. A handler is a subscriber that doesn't compute an adapter but performs some function when called. A boolean is returned indicating whether the registry was changed.
Location: zope.component - IComponentRegistry
Signature: unregisterHandler(handler=None, required=None, name=u'')
See also: registerHandler
Example:
>>> from zope.interface import Interface
>>> from zope.interface import Attribute
>>> from zope.interface import implements
>>> class IDocument(Interface):
...
... summary = Attribute("Document summary")
... body = Attribute("Document text")
>>> class Document(object):
...
... implements(IDocument)
... def __init__(self, summary, body):
... self.summary, self.body = summary, body
>>> doc = Document("A\nDocument", "blah")
>>> class IDocumentAccessed(Interface):
... doc = Attribute("The document that was accessed")
>>> class DocumentAccessed(object):
... implements(IDocumentAccessed)
...
... def __init__(self, doc):
... self.doc = doc
... self.doc.count = 0
>>> from zope.component import adapter
>>> @adapter(IDocumentAccessed)
... def documentAccessed(event):
... event.doc.count = event.doc.count + 1
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> gsm.registerHandler(documentAccessed)
>>> from zope.component import handle
>>> handle(DocumentAccessed(doc))
>>> doc.count
1
Now unregister:
>>> gsm.unregisterHandler(documentAccessed)
True
After unregistration:
>>> handle(DocumentAccessed(doc))
>>> doc.count
0
This function is used to unregister a subscriber factory. A boolean is returned indicating whether the registry was changed. If the given component is None and there is no component registered, or if the given component is not None and is not registered, then the function returns False, otherwise it returns True.
Location: zope.component - IComponentRegistry
Signature: unregisterSubscriptionAdapter(factory=None, required=None, provides=None, name=u'')
See also: registerSubscriptionAdapter
Example:
>>> from zope.interface import Interface
>>> from zope.interface import Attribute
>>> from zope.interface import implements
>>> class IValidate(Interface):
... def validate(ob):
... """Determine whether the object is valid
...
... Return a string describing a validation problem.
... An empty string is returned to indicate that the
... object is valid.
... """
>>> class IDocument(Interface):
... summary = Attribute("Document summary")
... body = Attribute("Document text")
>>> class Document(object):
... implements(IDocument)
... def __init__(self, summary, body):
... self.summary, self.body = summary, body
>>> from zope.component import adapts
>>> class AdequateLength(object):
...
... adapts(IDocument)
... implements(IValidate)
...
... def __init__(self, doc):
... self.doc = doc
...
... def validate(self):
... if len(self.doc.body) < 1000:
... return 'too short'
... else:
... return ''
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> gsm.registerSubscriptionAdapter(AdequateLength)
>>> from zope.component import subscribers
>>> doc = Document("A\nDocument", "blah")
>>> [adapter.validate()
... for adapter in subscribers([doc], IValidate)
... if adapter.validate()]
['too short']
Now unregister:
>>> gsm.unregisterSubscriptionAdapter(AdequateLength)
True
After unregistration:
>>> [adapter.validate()
... for adapter in subscribers([doc], IValidate)
... if adapter.validate()]
[]
This function is used for unregistering a utility. A boolean is returned indicating whether the registry was changed. If the given component is None and there is no component registered, or if the given component is not None and is not registered, then the function returns False, otherwise it returns True.
Location: zope.component - IComponentRegistry
Signature: unregisterUtility(component=None, provided=None, name=u'')
See also: registerUtility
Example:
>>> from zope.interface import Interface
>>> from zope.interface import implements
>>> class IGreeter(Interface):
... def greet(name):
... "say hello"
>>> class Greeter(object):
...
... implements(IGreeter)
...
... def greet(self, name):
... return "Hello " + name
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> greet = Greeter()
>>> gsm.registerUtility(greet)
>>> queryUtility(IGreeter).greet('Jack')
'Hello Jack'
Now unregister:
>>> gsm.unregisterUtility(greet)
True
After unregistration:
>>> print queryUtility(IGreeter)
None