跳转到主要内容

创建自定义Zope源的简单方法。

项目描述

源工厂

源工厂用于简化某些标准情况下的源创建。

源将向用户提供具有选择项的输入字段的过程分割成几个组件:上下文绑定器、源类、术语类和术语类。

这是正确的抽象,非常适合许多复杂情况。为了减少某些标准情况下的工作量,源工厂允许用户只定义获取值列表、获取要显示的标记和标题的业务相关代码。

简单情况

在最简单的情况下,您只需提供一个返回值列表的方法,并从BasicSourceFactory派生

>>> import zc.sourcefactory.basic
>>> class MyStaticSource(zc.sourcefactory.basic.BasicSourceFactory):
...     def getValues(self):
...         return ['a', 'b', 'c']

调用源工厂时,我们得到一个源

>>> source = MyStaticSource()
>>> import zope.schema.interfaces
>>> zope.schema.interfaces.ISource.providedBy(source)
True

值匹配工厂的getValues-方法

>>> list(source)
['a', 'b', 'c']
>>> 'a' in source
True
>>> len(source)
3

上下文源

有时我们需要上下文来确定值。在这种情况下,getValues方法获取一个参数context

假设我们有一个包含要由源使用的数据的小对象

>>> class Context(object):
...      values = []
>>> import zc.sourcefactory.contextual
>>> class MyDynamicSource(
...     zc.sourcefactory.contextual.BasicContextualSourceFactory):
...     def getValues(self, context):
...         return context.values

实例化时,我们得到一个ContextSourceBinder

>>> binder = MyDynamicSource()
>>> zope.schema.interfaces.IContextSourceBinder.providedBy(binder)
True

将其绑定到上下文中,我们得到一个源

>>> context = Context()
>>> source = binder(context)
>>> zope.schema.interfaces.ISource.providedBy(source)
True
>>> list(source)
[]

修改上下文也会修改源中的数据

>>> context.values = [1,2,3,4]
>>> list(source)
[1, 2, 3, 4]
>>> 1 in source
True
>>> len(source)
4

通过在调用绑定器时提供source_class参数,可以实现默认机械返回不同的源。还可以向源提供参数。

>>> class MultiplierSource(zc.sourcefactory.source.FactoredContextualSource):
...     def __init__(self, factory, context, multiplier):
...         super(MultiplierSource, self).__init__(factory, context)
...         self.multiplier = multiplier
...
...     def _get_filtered_values(self):
...         for value in self.factory.getValues(self.context):
...             yield self.multiplier * value
>>> class MultiplierSourceFactory(MyDynamicSource):
...     source_class = MultiplierSource
>>> binder = MultiplierSourceFactory()
>>> source = binder(context, multiplier=5)
>>> list(source)
[5, 10, 15, 20]
>>> 5 in source
True
>>> len(source)
4

过滤

除了提供getValues方法外,您还可以提供一个filterValue方法,这将允许您逐步减少列表中的项。

如果您想拥有更具体的源(通过子类化),这些源具有相同的基本数据来源但应用了不同的过滤器,这非常有用。

>>> class FilteringSource(zc.sourcefactory.basic.BasicSourceFactory):
...     def getValues(self):
...         return iter(range(1,20))
...     def filterValue(self, value):
...         return value % 2
>>> source = FilteringSource()
>>> list(source)
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]

子类化修改了过滤器,而不是原始数据

>>> class OtherFilteringSource(FilteringSource):
...     def filterValue(self, value):
...         return not value % 2
>>> source = OtherFilteringSource()
>>> list(source)
[2, 4, 6, 8, 10, 12, 14, 16, 18]

“in”运算符也应用于过滤值

>>> 2 in source
True
>>> 3 in source
False

“len”也应用于过滤值

>>> len(source)
9

扩展

有时通过源可用的项的数量非常大。所以大,以至于您只有在绝对必要时才想要访问它们。这种情况之一是与源的真值测试。默认情况下,Python会调用__nonzero__来获取对象的布尔值,但如果该值不可用,则调用__len__来查看其返回值。这可能非常昂贵,因此我们想要确保它不会被调用。

>>> class MyExpensiveSource(zc.sourcefactory.basic.BasicSourceFactory):
...     def getValues(self):
...         yield 'a'
...         raise RuntimeError('oops, iterated too far')
>>> source = MyExpensiveSource()
>>> bool(source)
True

简单情况

在最简单的情况下,您只需提供一个返回值列表的方法,并从BasicSourceFactory派生

>>> import zc.sourcefactory.basic
>>> class MyStaticSource(zc.sourcefactory.basic.BasicSourceFactory):
...     def getValues(self):
...         return ['a', 'b', 'c']

调用源工厂时,我们得到一个源

>>> source = MyStaticSource()
>>> import zope.schema.interfaces
>>> zope.schema.interfaces.ISource.providedBy(source)
True

值匹配工厂的getValues-方法

>>> list(source)
['a', 'b', 'c']
>>> 'a' in source
True
>>> len(source)
3

关于ITerms标准适配器的警告

ITerms的标准适配器仅适用于您的getValues函数返回的值类型是同质的。在单个源中混合整数、持久性对象、字符串和unicode可能会创建非唯一的标记。在这种情况下,您必须提供一个自定义的getToken方法来提供唯一且无歧义的标记。

映射源值

有时源提供正确的对象选择,但我们想要讨论的实际值是那些对象的属性或计算视图。映射代理源帮助我们将源映射到不同的值空间。

我们从一个源开始

>>> source = [1,2,3,4,5]

我们提供了一个方法,将原始源中的值映射到我们想要的值(我们将数字映射到英文字母中的字符)

>>> map = lambda x: chr(x+96)

现在我们可以创建一个映射源

>>> from zc.sourcefactory.mapping import ValueMappingSource
>>> mapped_source = ValueMappingSource(source, map)
>>> list(mapped_source)
['a', 'b', 'c', 'd', 'e']
>>> len(mapped_source)
5
>>> 'a' in mapped_source
True
>>> 1 in mapped_source
False

您还可以使用上下文相关的源

>>> def bindSource(context):
...     return [1,2,3,4,5]
>>> from zc.sourcefactory.mapping import ValueMappingSourceContextBinder
>>> binder = ValueMappingSourceContextBinder(bindSource, map)
>>> bound_source = binder(object())
>>> list(bound_source)
['a', 'b', 'c', 'd', 'e']
>>> len(bound_source)
5
>>> 'a' in bound_source
True
>>> 1 in bound_source
False

扩展

有时通过源可用的项的数量非常大。所以大,以至于您只有在绝对必要时才想要访问它们。这种情况之一是与源的真值测试。默认情况下,Python会调用__nonzero__来获取对象的布尔值,但如果该值不可用,则调用__len__来查看其返回值。这可能非常昂贵,因此我们想要确保它不会被调用。

>>> class ExpensiveSource(object):
...     def __len__(self):
...         raise RuntimeError("oops, don't want to call __len__")
...
...     def __iter__(self):
...         return iter(range(999999))
>>> expensive_source = ExpensiveSource()
>>> mapped_source = ValueMappingSource(expensive_source, map)
>>> bool(mapped_source)
True

自定义构造函数

源工厂旨在尽可能自然地工作。在基类上使用自定义工厂方法(__new__)的一个副作用是,如果子类的构造函数(__init__)有不同的签名,子类可能会遇到困难。

zc.sourcefactory采取了额外措施,允许使用具有不同签名的自定义构造函数。

>>> import zc.sourcefactory.basic
>>> class Source(zc.sourcefactory.basic.BasicSourceFactory):
...
...     def __init__(self, values):
...         super(Source, self).__init__()
...         self.values = values
...
...     def getValues(self):
...         return self.values
>>> source = Source([1, 2, 3])
>>> list(source)
[1, 2, 3]

这对于上下文相关的源也是如此。这个例子有点愚蠢,但它表明它原则上是可以工作的

>>> import zc.sourcefactory.contextual
>>> default_values = (4, 5, 6)
>>> context_values = (6, 7, 8)
>>> class ContextualSource(
...     zc.sourcefactory.contextual.BasicContextualSourceFactory):
...
...     def __init__(self, defaults):
...         super(ContextualSource, self).__init__()
...         self.defaults = defaults
...
...     def getValues(self, context):
...         return self.defaults + context
>>> contextual_source = ContextualSource(default_values)(context_values)
>>> list(contextual_source)
[4, 5, 6, 6, 7, 8]

源常见适配器

为了允许根据工厂特定地调整生成的源,一些可以作为适配器使用的标准接口被重新调整为使用(FactoredSource,SourceFactory)的多适配器。

ISourceQueriables

>>> from zc.sourcefactory.basic import BasicSourceFactory
>>> class Factory(BasicSourceFactory):
...     def getValues(self):
...         return [1,2,3]
>>> source = Factory()
>>> from zope.schema.interfaces import ISourceQueriables
>>> import zope.interface
>>> @zope.interface.implementer(ISourceQueriables)
... class SourceQueriables(object):
...     def __init__(self, source, factory):
...         self.source = source
...         self.factory = factory
...     def getQueriables(self):
...         return [('test', None)]
>>> from zc.sourcefactory.source import FactoredSource
>>> zope.component.provideAdapter(factory=SourceQueriables,
...                               provides=ISourceQueriables,
...                               adapts=(FactoredSource, Factory))
>>> queriables = ISourceQueriables(source)
>>> queriables.factory
<Factory object at 0x...>
>>> queriables.source
<zc.sourcefactory.source.FactoredSource object at 0x...>
>>> queriables.getQueriables()
[('test', None)]

清理

>>> zope.component.getSiteManager().unregisterAdapter(factory=SourceQueriables,
...     provided=ISourceQueriables, required=(FactoredSource, Factory))
True

由源工厂创建的源浏览器视图

使用源工厂创建的源已经包含了现成的术语和术语对象。

简单使用

让我们从一个简单的源工厂开始

>>> import zc.sourcefactory.basic
>>> class DemoSource(zc.sourcefactory.basic.BasicSourceFactory):
...     def getValues(self):
...         return [b'a', b'b', b'c', b'd']
>>> source = DemoSource()
>>> list(source)
[b'a', b'b', b'c', b'd']

我们首先需要一个请求,然后我们可以将源适配到ITerms

>>> from zope.publisher.browser import TestRequest
>>> import zope.browser.interfaces
>>> import zope.component
>>> request = TestRequest()
>>> terms = zope.component.getMultiAdapter(
...     (source, request), zope.browser.interfaces.ITerms)
>>> terms
<zc.sourcefactory.browser.source.FactoredTerms object at 0x...>

对于每个值,我们得到一个分解术语

>>> terms.getTerm(b'a')
<zc.sourcefactory.browser.source.FactoredTerm object at 0x...>
>>> terms.getTerm(b'b')
<zc.sourcefactory.browser.source.FactoredTerm object at 0x...>
>>> terms.getTerm(b'c')
<zc.sourcefactory.browser.source.FactoredTerm object at 0x...>
>>> terms.getTerm(b'd')
<zc.sourcefactory.browser.source.FactoredTerm object at 0x...>

也允许使用Unicode值

>>> terms.getTerm('\xd3')
<zc.sourcefactory.browser.source.FactoredTerm object at 0x...>

我们的术语与ITitledTokenizedTerm兼容

>>> import zope.schema.interfaces
>>> zope.schema.interfaces.ITitledTokenizedTerm.providedBy(
...     terms.getTerm('a'))
True

在最简单的情况下,术语的标题是对象的字符串表示

>>> terms.getTerm('a').title
'a'

如果存在从值到IDCDescriptiveProperties的适配器,标题将从这个适配器检索

>>> import persistent
>>> class MyObject(persistent.Persistent):
...    custom_title = 'My custom title'
...    _p_oid = 12
>>> class DCDescriptivePropertiesAdapter(object):
...    def __init__(self, context):
...        self.title = context.custom_title
...        self.description = u""
>>> from zope.component import provideAdapter
>>> from zope.dublincore.interfaces import IDCDescriptiveProperties
>>> provideAdapter(DCDescriptivePropertiesAdapter, [MyObject],
...     IDCDescriptiveProperties)
>>> terms.getTerm(MyObject()).title
'My custom title'

扩展使用:提供自己的标题

您可以通过在源工厂上指定getTitle方法来确定值的标题,而不是依赖于字符串表示或IDCDescriptiveProperties适配器

>>> class DemoSourceWithTitles(DemoSource):
...     def getTitle(self, value):
...         return 'Custom title ' + value.custom_title
>>> source2 = DemoSourceWithTitles()
>>> terms2 = zope.component.getMultiAdapter(
...     (source2, request), zope.browser.interfaces.ITerms)
>>> o1 = MyObject()
>>> o1.custom_title = u"Object one"
>>> o2 = MyObject()
>>> o2.custom_title = u"Object two"
>>> terms2.getTerm(o1).title
'Custom title Object one'
>>> terms2.getTerm(o2).title
'Custom title Object two'

扩展使用:提供自己的标记

您可以通过在源工厂上覆盖getToken方法来决定值的令牌,而不是依赖于默认适配器为您的值生成令牌

>>> class DemoObjectWithToken(object):
...     token = None
>>> o1 = DemoObjectWithToken()
>>> o1.token = "one"
>>> o2 = DemoObjectWithToken()
>>> o2.token = "two"
>>> class DemoSourceWithTokens(DemoSource):
...     values = [o1, o2]
...     def getValues(self):
...         return self.values
...     def getToken(self, value):
...         return value.token
>>> source3 = DemoSourceWithTokens()
>>> terms3 = zope.component.getMultiAdapter(
...     (source3, request), zope.browser.interfaces.ITerms)
>>> terms3.getTerm(o1).token
'one'
>>> terms3.getTerm(o2).token
'two'

通过自定义令牌查找也有效

>>> terms3.getValue("one") is o1
True
>>> terms3.getValue("two") is o2
True
>>> terms3.getValue("three")
Traceback (most recent call last):
KeyError: "No value with token 'three'"

值映射源

待续

上下文源

让我们从一个可以用作上下文的对象开始

>>> zip_to_city = {'06112': 'Halle',
...                '06844': 'Dessa'}
>>> import zc.sourcefactory.contextual
>>> class DemoContextualSource(
...     zc.sourcefactory.contextual.BasicContextualSourceFactory):
...     def getValues(self, context):
...         return context.keys()
...     def getTitle(self, context, value):
...         return context[value]
...     def getToken(self, context, value):
...         return 'token-%s' % value
>>> source = DemoContextualSource()(zip_to_city)
>>> sorted(list(source))
['06112', '06844']

让我们看看术语

>>> terms = zope.component.getMultiAdapter(
...     (source, request), zope.browser.interfaces.ITerms)
>>> terms
<zc.sourcefactory.browser.source.FactoredContextualTerms object at 0x...>

对于每个值,我们得到一个具有正确标题的分解术语

>>> terms.getTerm('06112')
<zc.sourcefactory.browser.source.FactoredTerm object at 0x...>
>>> terms.getTerm('06112').title
'Halle'
>>> terms.getTerm('06844')
<zc.sourcefactory.browser.source.FactoredTerm object at 0x...>
>>> terms.getTerm('06844').title
'Dessa'
>>> terms.getTerm('06844').token
'token-06844'

反之,我们也可以根据给定的令牌获取值

>>> terms.getValue('token-06844')
'06844'

接口

FactoredSource和FactoredContextualSource都有相关的接口。

>>> from zc.sourcefactory import interfaces
>>> from zc.sourcefactory import source
>>> from zope import interface
>>> interface.classImplements(
...     source.FactoredSource, interfaces.IFactoredSource)
>>> interface.classImplements(
...     source.FactoredContextualSource, interfaces.IContextualSource)

标记

令牌是对象的识别表示,适用于在URL编码数据之间传输。

sourcefactory包提供了一些标准令牌生成器

>>> import zc.sourcefactory.browser.token

我们有字符串生成器

>>> zc.sourcefactory.browser.token.fromString('somestring')
'1f129c42de5e4f043cbd88ff6360486f'

Unicode

啊,我必须用Unicode转义序列来写入元音,否则distutils将在准备上传到pypi时出现编码错误

>>> zc.sourcefactory.browser.token.fromUnicode(
...     'somestring with umlauts \u00F6\u00E4\u00FC')
'45dadc304e0d6ae7f4864368bad74951'

整数

>>> zc.sourcefactory.browser.token.fromInteger(12)
'12'

持久性

>>> import persistent
>>> class PersistentDummy(persistent.Persistent):
...     pass
>>> p = PersistentDummy()
>>> p._p_oid = 1234
>>> zc.sourcefactory.browser.token.fromPersistent(p)
'1234'

如果对象是持久的但尚未添加到数据库中,它将被添加到其__parent__的数据库中

>>> root = rootFolder
>>> p1 = PersistentDummy()
>>> p1.__parent__ = root
>>> zc.sourcefactory.browser.token.fromPersistent(p1)
'0x01'

如果对象没有父对象,我们将失败

>>> p2 = PersistentDummy()
>>> zc.sourcefactory.browser.token.fromPersistent(p2)
Traceback (most recent call last):
...
ValueError: Can not determine OID for <builtins.PersistentDummy object at 0x...>

安全代理对象将被解包以获取其oid或连接属性

>>> from zope.security.proxy import ProxyFactory
>>> p3 = PersistentDummy()
>>> root['p3'] = p3
>>> p3.__parent__ = root
>>> p3p = ProxyFactory(p3)
>>> p3p._p_jar
Traceback (most recent call last):
  ...
zope.security.interfaces.ForbiddenAttribute: ('_p_jar', <builtins.PersistentDummy object at 0x...>)
>>> zc.sourcefactory.browser.token.fromPersistent(p3p)
'0x02'

作为副作用,现在有一个_p_oid分配。当一个对象已经有一个OID时,不会查询连接,因此不需要__parent__

>>> del p3.__parent__
>>> zc.sourcefactory.browser.token.fromPersistent(p3p)
'0x02'

接口

>>> from zope.interface import Interface
>>> class I(Interface):
...     pass
>>> zc.sourcefactory.browser.token.fromInterface(I)
'builtins.I'

更改

2.0 (2023-02-23)

  • 添加对Python 3.8、3.9、3.10、3.11的支持。

  • 放弃对Python 2.7、3.5、3.6的支持。

1.1 (2018-11-07)

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

  • 放弃对Python 3.3和3.4的支持。

1.0.0 (2016-08-02)

  • 声称支持Python 3.4和3.5。

  • 放弃对Python 2.6的支持。

1.0.0a1 (2013-02-23)

  • 添加了对Python 3.3的支持。

  • 大幅减少测试依赖项,以使移植更容易。

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

  • 放弃对Python 2.4和2.5的支持。

0.8.0 (2013-10-04)

  • BasicSourceFactory 现在使用类变量来指定要创建的源类型。(与在 0.5.0 版本中为 ContextualSourceFactory 添加的机制相同)。

0.7.0 (2010-09-17)

  • 使用 Python 的 doctest 而不是已弃用的 zope.testing.doctest

  • 使用 zope.keyreference 作为测试依赖项,而不是 zope.app.keyreference

0.6.0 (2009-08-15)

  • 将包主页更改为 PyPI 而不是 Subversion。

  • 通过删除条件导入,移除了对 Zope 3.2 的支持。

  • 使用 hashlib(Python 2.5 及以后版本)以避免弃用警告。

0.5.0 (2009-02-03)

  • FactoredContextualSourceBinder.__call__ 现在接受参数,这些参数指定传递给源类的参数。ContextualSourceFactory 现在使用类变量来指定要创建的 Source 类型。

  • 使用 zope.intid 而不是 zope.app.intid。

  • 已将电子邮件地址更正为 zope3-dev@zope.org 已弃用。

0.4.0 (2008-12-11)

  • 移除了 zope.app.form 依赖项。将 ITerms 导入从 zope.app.form.browser.interfaces 更改为 zope.browser.interfaces。[projekt01]

0.3.5 (2008-12-08)

  • 修复了 contexual 工厂 __new__ 中存在的错误,这将阻止子类使用具有不同签名的构造函数。[icemac]

0.3.4 (2008-08-27)

  • 将包中的所有文档添加到长描述中,以便在 PyPI 中可读。[icemac]

0.3.3 (2008-06-10)

  • 修复了 factories 的 __new__ 中存在的错误,这将阻止子类使用具有不同签名的构造函数。(感谢 Sebastian Wehrmann 提供补丁。)

0.3.2 (2008-04-09)

  • 修复了由 ValueMappingSource 中缺少 __nonzero__ 引起的可扩展性错误。

0.3.1 (2008-02-12)

  • 修复了由 BasicSourceFactory 中缺少 __nonzero__ 引起的可扩展性错误。

0.3.0 (??????????)

  • 为接口中声明的属性添加了类级别默认值,以避免 Zope 2 安全机制对它们进行投诉。

0.2.1 (2007-07-10)

  • 修复了处理特定令牌值解析的上下文令牌策略中的错误。

0.2.0 (2007-07-10)

  • 添加了一个上下文令牌策略接口,允许 getToken 和 getValue 访问上下文以访问上下文源。

  • 添加了一个上下文术语策略接口,允许 createTerm 和 getTitle 访问上下文以访问上下文源。

  • 添加了对 Zope 3.2 和 Zope 2.9 的兼容性(通过 Five 1.3)。

项目详情


下载文件

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

源分布

zc.sourcefactory-2.0.tar.gz (29.5 kB 查看哈希值)

上传时间

构建分布

zc.sourcefactory-2.0-py3-none-any.whl (33.0 kB 查看哈希值)

上传时间 Python 3

由以下支持