跳转到主要内容

zope组件架构的本地注册表

项目描述

zope.site

Latest release Supported Python versions https://github.com/zopefoundation/zope.site/workflows/tests/badge.svg https://coveralls.io/repos/github/zopefoundation/zope.site/badge.svg?branch=master Documentation Status

本包提供了一种本地和持久的站点管理器实现,以便可以注册本地实用程序和适配器。它使用本地适配器注册表来存储其适配器和实用程序注册表。该模块还提供了一些组织本地软件和确保ZODB内正确行为的设施。

文档托管在 https://zopesite.readthedocs.io

站点和本地站点管理器

这是基于位置组件架构的介绍。

创建和访问站点

站点 用于为您的应用程序或网站的部分提供自定义组件设置。每个文件夹

>>> from zope.site import folder
>>> myfolder = folder.rootFolder()

都有可能成为站点

>>> from zope.component.interfaces import ISite, IPossibleSite
>>> IPossibleSite.providedBy(myfolder)
True

但还不是

>>> ISite.providedBy(myfolder)
False

如果您希望您的自定义内容组件能够成为站点,您可以使用 SiteManagerContainer 混合类

>>> from zope import site
>>> class MyContentComponent(site.SiteManagerContainer):
...     pass
>>> myContent = MyContentComponent()
>>> IPossibleSite.providedBy(myContent)
True
>>> ISite.providedBy(myContent)
False

要将可能的站点转换为真实站点,我们必须提供站点管理器

>>> sm = site.LocalSiteManager(myfolder)
>>> myfolder.setSiteManager(sm)
>>> ISite.providedBy(myfolder)
True
>>> myfolder.getSiteManager() is sm
True

请注意,在创建本地站点管理器时会生成一个事件

>>> from zope.component.eventtesting import getEvents
>>> from zope.site.interfaces import INewLocalSite
>>> [event] = getEvents(INewLocalSite)
>>> event.manager is sm
True

如果尝试设置一个无效的站点管理员,将抛出一个ValueError错误

>>> myfolder2 = folder.Folder()
>>> myfolder2.setSiteManager(object)
Traceback (most recent call last):
...
ValueError: setSiteManager requires an IComponentLookup

如果可能已更改到已存在的站点,当尝试添加新的站点管理员时,将抛出一个TypeError错误

>>> myfolder.setSiteManager(site.LocalSiteManager(myfolder))
Traceback (most recent call last):
...
TypeError: Already a site

此外,您还可以使用一个适配器从任何位置获取下一个站点管理员

>>> myfolder['mysubfolder'] = folder.Folder()
>>> import zope.interface.interfaces
>>> zope.interface.interfaces.IComponentLookup(myfolder['mysubfolder']) is sm
True

如果传递的定位符是站点,则返回该站点的站点管理员

>>> zope.interface.interfaces.IComponentLookup(myfolder) is sm
True

使用站点管理员

站点管理员包含几个站点管理文件夹,用于逻辑组织软件。当初始化站点管理员时,将创建一个默认的站点管理文件夹

>>> sm = myfolder.getSiteManager()
>>> default = sm['default']
>>> default.__class__
<class 'zope.site.site.SiteManagementFolder'>

然而,您可以在创建LocalSiteManager时指示不要创建默认站点管理文件夹

>>> nodefault = site.LocalSiteManager(myfolder, default_folder=False)
>>> 'default' in nodefault
False

此外,请注意,在创建LocalSiteManager时,其__parent__设置为构造函数中传递的站点,而__name__设置为++etc++site。

>>> nodefault.__parent__ is myfolder
True
>>> nodefault.__name__ == '++etc++site'
True

您可以轻松地创建一个新的站点管理文件夹

>>> sm['mySMF'] = site.SiteManagementFolder()
>>> sm['mySMF'].__class__
<class 'zope.site.site.SiteManagementFolder'>

一旦您有了站点管理文件夹——让我们使用默认的一个——我们就可以注册一些组件。让我们从一个(我们在一个可以序列化的__module__中定义的)实用工具开始

>>> import zope.interface
>>> __name__ = 'zope.site.tests'
>>> class IMyUtility(zope.interface.Interface):
...     pass
>>> import persistent
>>> from zope.container.contained import Contained
>>> @zope.interface.implementer(IMyUtility)
... class MyUtility(persistent.Persistent, Contained):
...     def __init__(self, title):
...         self.title = title
...     def __repr__(self):
...         return "%s('%s')" %(self.__class__.__name__, self.title)

现在我们可以创建实用工具的实例,将其放入站点管理文件夹并注册它

>>> myutil = MyUtility('My custom utility')
>>> default['myutil'] = myutil
>>> sm.registerUtility(myutil, IMyUtility, 'u1')

现在我们可以向站点管理员请求实用工具

>>> sm.queryUtility(IMyUtility, 'u1')
MyUtility('My custom utility')

当然,本地站点管理员也有权访问全局组件注册

>>> gutil = MyUtility('Global Utility')
>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()
>>> gsm.registerUtility(gutil, IMyUtility, 'gutil')
>>> sm.queryUtility(IMyUtility, 'gutil')
MyUtility('Global Utility')

接下来,让我们看看是否也可以成功注册适配器。这里的适配器将提供文件的大小

>>> class IFile(zope.interface.Interface):
...     pass
>>> class ISized(zope.interface.Interface):
...     pass
>>> @zope.interface.implementer(IFile)
... class File(object):
...     pass
>>> @zope.interface.implementer(ISized)
... class FileSize(object):
...     def __init__(self, context):
...         self.context = context

现在我们已经有了适配器,需要将其注册

>>> sm.registerAdapter(FileSize, [IFile])

最后,我们可以为文件获取适配器

>>> file = File()
>>> size = sm.queryAdapter(file, ISized, name='')
>>> isinstance(size, FileSize)
True
>>> size.context is file
True

顺便说一句,一旦设置了站点

>>> from zope.component import hooks
>>> hooks.setSite(myfolder)

您可以直接使用zope.component的getSiteManager()方法来获取最近的站点管理员

>>> from zope.component import getSiteManager
>>> getSiteManager() is sm
True

这也意味着您可以直接使用zope.component来查找您的实用工具

>>> from zope.component import getUtility
>>> getUtility(IMyUtility, 'gutil')
MyUtility('Global Utility')

或通过接口的__call__方法获取适配器

>>> size = ISized(file)
>>> isinstance(size, FileSize)
True
>>> size.context is file
True

多个站点

到目前为止,我们只处理了一个本地站点和全局站点。但是,当我们有多个站点时,事情变得真正有趣。我们可以覆盖其他本地配置。

此行为使用了位置的概念,因此我们需要首先配置zope.location包

>>> import zope.configuration.xmlconfig
>>> _  = zope.configuration.xmlconfig.string("""
... <configure xmlns="http://namespaces.zope.org/zope">
...   <include package="zope.component" file="meta.zcml"/>
...   <include package="zope.location" />
... </configure>
... """)

现在,创建一个名为folder11的新文件夹,将其添加到myfolder中,并将其设置为站点

>>> myfolder11 = folder.Folder()
>>> myfolder['myfolder11'] = myfolder11
>>> myfolder11.setSiteManager(site.LocalSiteManager(myfolder11))
>>> sm11 = myfolder11.getSiteManager()

如果我们要求第二个站点管理员获取其下一个,我们得到

>>> sm11.__bases__ == (sm, )
True

并且第一个站点管理员应该有以下的子管理员

>>> sm.subs == (sm11,)
True

如果我们现在用新的站点管理员文件夹注册一个具有相同名称和接口的第二个实用工具,

>>> default11 = sm11['default']
>>> myutil11 = MyUtility('Utility, uno & uno')
>>> default11['myutil'] = myutil11
>>> sm11.registerUtility(myutil11, IMyUtility, 'u1')

那么它将在第二个站点管理员中可用

>>> sm11.queryUtility(IMyUtility, 'u1')
MyUtility('Utility, uno & uno')

但不在第一个中

>>> sm.queryUtility(IMyUtility, 'u1')
MyUtility('My custom utility')

查看移动和复制站点的用例也非常有趣。为此,我们创建一个第二个根文件夹,并将其设置为站点,以便站点层次结构如下

         _____ global site _____
        /                       \
    myfolder                 myfolder2
        |
    myfolder11


>>> myfolder2 = folder.rootFolder()
>>> myfolder2.setSiteManager(site.LocalSiteManager(myfolder2))

在我们可以移动或复制站点之前,我们需要注册两个事件订阅者来管理移动或复制后站点管理器的连接

>>> import zope.lifecycleevent.interfaces
>>> gsm.registerHandler(
...    site.changeSiteConfigurationAfterMove,
...    (ISite, zope.lifecycleevent.interfaces.IObjectMovedEvent),
...    )

由于复制操作会创建一个特殊的IObjectMovedEvent类型的IObjectAddedEvent,我们只需要注册一个事件监听器。

首先,请确保一切设置正确

>>> myfolder11.getSiteManager().__bases__ == (myfolder.getSiteManager(), )
True
>>> myfolder.getSiteManager().subs[0] is myfolder11.getSiteManager()
True
>>> myfolder2.getSiteManager().subs
()

现在让我们将myfolder11myfolder移动到myfolder2

>>> myfolder2['myfolder21'] = myfolder11
>>> del myfolder['myfolder11']

现在myfolder11的站点管理员的下一个站点管理员应该已经改变

>>> myfolder21 = myfolder11
>>> myfolder21.getSiteManager().__bases__ == (myfolder2.getSiteManager(), )
True
>>> myfolder2.getSiteManager().subs[0] is myfolder21.getSiteManager()
True
>>> myfolder.getSiteManager().subs
()

请确保我们的接口和类是可序列化的

>>> import sys
>>> sys.modules['zope.site.tests'].IMyUtility = IMyUtility
>>> sys.modules['zope.site.tests'].MyUtility = MyUtility
>>> from pickle import dumps, loads
>>> data = dumps(myfolder2['myfolder21'])
>>> myfolder['myfolder11'] = loads(data)
>>> myfolder11 = myfolder['myfolder11']
>>> myfolder11.getSiteManager().__bases__ == (myfolder.getSiteManager(), )
True
>>> myfolder.getSiteManager().subs[0] is myfolder11.getSiteManager()
True
>>> myfolder2.getSiteManager().subs[0] is myfolder21.getSiteManager()
True

最后,让我们检查当我们的文件夹被移动到不包含任何站点管理员的文件夹时,一切是否正常。我们的文件夹的sitemanager的bases应该设置为全局站点管理员。

>>> myfolder11.getSiteManager().__bases__ == (myfolder.getSiteManager(), )
True
>>> nosm = folder.Folder()
>>> nosm['root'] = myfolder11
>>> myfolder11.getSiteManager().__bases__ == (gsm, )
True

删除站点将取消其站点管理器与其父站点管理器的注册

>>> del myfolder2['myfolder21']
>>> myfolder2.getSiteManager().subs
()

已删除的站点管理器现在没有基础

>>> myfolder21.getSiteManager().__bases__
()

变更

5.0 (2023-06-30)

  • 停止支持Python 2.7, 3.5, 3.6。

  • 添加对Python 3.11的支持。

4.6.1 (2022-09-02)

  • 修复更多弃用警告。

4.6 (2022-08-23)

  • 添加对Python 3.9, 3.10的支持。

  • 修复弃用警告。

4.5.0 (2021-03-04)

  • 修复IRootFolder的接口定义,使其比文件夹和容器接口具有更高的优先级。这通常是期望的结果,但代码定义的不是这样。通常,过去这个问题被隐藏了,因为工厂函数rootFolder()重新排列了接口,将IRoot放在前面。然而,在zope.interface 5的C3解析顺序中,这种重新排列并没有发生;因此,查找rootFolder()对象的适配器时,可能会找到IItemContainer的适配器,而不是预期的IRoot适配器。

    由于这个更改,使用rootFolder()的用户应该与zope.interface 4相比没有发现任何变化。但是,直接定义类以实现IRootFolder的代码可能会在这些对象上注意到不同的解析顺序(与rootFolder()生成的顺序一致)。

    问题17

4.4.0 (2020-09-10)

  • 在删除站点时,清除其站点管理器的基础。这修复了来自父站点管理器的引用泄露。见问题1

4.3.0 (2020-04-01)

  • 添加对Python 3.8的支持。

  • 停止支持Python 3.4。

  • 停止支持已弃用的python setup.py test命令。

  • 修复使用zope.interface 5.0的测试。见问题12

4.2.2 (2018-10-19)

  • 修复更多DeprecationWarnings。见问题10

4.2.1 (2018-10-11)

  • 使用当前导入位置为UtilityRegistrationIUtilityRegistration类,以避免DeprecationWarning

4.2.0 (2018-10-09)

  • 添加对Python 3.7的支持。

4.1.0 (2017-08-08)

  • 添加对Python 3.5和3.6的支持。

  • 停止支持Python 2.6和3.3。

  • 使用zope.deprecation弃用zope.site.hooks.*zope.site.site.setSitezope.site.next.getNextUtilityzope.site.next.queryNextUtility。这些将在版本5.0中删除。它们都在zope.component中有替代方案。

  • 在LocalSiteManager中添加了_p_repr的实现。有关更多信息,请参阅问题8

  • 达到100%测试覆盖率,并确保保持在该水平。

4.0.0 (2014-12-24)

  • 添加对PyPy的支持。

  • 添加对Python 3.4的支持。

  • 添加对Travis上的测试支持。

4.0.0a1 (2013-02-20)

  • 添加了对Python 3.3的支持。

  • 用等效的zope.interface.implementer装饰器替换了已弃用的zope.interface.implements的使用。

  • 停止支持Python 2.4和2.5。

  • 在configure.zcml中包含zcml依赖项,并添加了zcml的测试。

3.9.2 (2010-09-25)

  • 添加了对未声明但需要的测试依赖项zope.testing

3.9.1 (2010-04-30)

  • 使用stdlib的‘doctest’代替了‘zope.testing.doctest’的使用。

  • 使用stdlib的‘doctest’代替了‘zope.testing.doctestunit’的使用。

3.9.0 (2009-12-29)

  • 通过使用正常的pickle模块测试站点管理器的正确持久行为,避免了对zope.copypastemove的测试依赖。

3.8.0 (2009-12-15)

  • 删除了功能测试设置和对zope.app.testing的依赖。

3.7.1 (2009-11-18)

  • 将zope.site.hooks功能移动到zope.component.hooks,因为它实际上并没有处理zope.site的站点概念。

  • 在它们从zope.location移动到那里之后,从zope.component导入ISite和IPossibleSite。

3.7.0 (2009-09-29)

  • 将zope.app.publication中未声明的依赖项清理,通过将两个相关的订阅者注册及其测试移动到该包中。

  • 删除了对zope.traversing的依赖,因为该依赖项仅用于访问zope.location功能。为一些测试配置zope.location。

  • 将zope.configuration降级为测试依赖。

3.6.4 (2009-09-01)

  • 在调用其超类构造函数之后,在LocalSiteManager的构造函数中设置__parent__和__name__,以防止__name__被Components构造函数覆盖为空字符串。

  • 在SiteManagerContainer的setSiteManager方法中不要设置site管理器的__parent__和__name__属性,因为它们对于LocalSiteManager已经设置。其他site管理器实现不需要有这些属性,所以我们不再添加它们。

3.6.3 (2009-07-27)

  • 当删除SiteManagerContainer时,向SiteManager传播ObjectRemovedEvent。

3.6.2 (2009-07-24)

  • 修复测试以通过最新包。

  • 删除了持久接口的失败测试,因为它没有测试此包中的任何内容,并使用了已弃用的zodbcode模块。

  • 修复在调用zope.site.testing.siteSetUp(site=True)时的NameError。

  • getNextUtilityqueryNextUtility函数从zope.traversing移动到zope.component。虽然提供了向后兼容的导入,但强烈建议更新您的导入。

3.6.1 (2009-02-28)

  • 从新位置将导入符号从zope.traversing移动到zope.location。

  • 在将ISite对象移动到非ISite对象时,更改组件注册基类时不会失败。

  • 允许在初始化LocalSiteManager时指定是否创建‘默认’SiteManagementFolder。使用default_folder参数。

  • 向SiteManagementFolder添加一个约束条件,使其只能包含在ILocalSiteManagers和其他ISiteManagementFolders中。

  • 将软件包的邮件列表地址更改为zope-dev at zope.org,因为zope3-dev at zope.org现已停用。

  • 删除旧的不使用的代码。更新软件包描述。

3.6.0 (2009-01-31)

  • 使用zope.container代替zope.app.container。

3.5.1 (2009-01-27)

  • 作为清理Zope包之间依赖关系的一部分,从zope.app.component(trunk,3.5.1处于开发中)中提取出来。

下载文件

下载您平台的文件。如果您不确定选择哪个,请了解有关安装软件包的更多信息。

源分布

zope.site-5.0.tar.gz (37.3 kB 查看哈希值

上传时间

构建分布

zope.site-5.0-py3-none-any.whl (30.3 kB 查看哈希值

上传于 Python 3

由...