跳转到主要内容

可插拔遍历器和URL处理工具

项目描述

此软件包提供可插拔遍历器机制,允许开发者在不更改原始遍历实现的情况下向对象添加新的遍历器。

除了可插拔遍历器外,此软件包还包括两个子软件包

  • 视图插件 - 提供使用命名空间遍历视图插件的方法

  • 栈信息 - 提供将URL的部分消费并存储为“消费者”对象的属性的方法。对于类似:/blog/2009/02/02/hello-world的URL很有用

CHANGES

2.0 (2023-02-09)

  • 停止支持Python 2.7,3.3,3.4。

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

  • 停止使用 python setup.py test 运行测试。

1.0.0 (2015-11-09)

  • 标准化命名空间 __init__。

  • 支持Python 3.4。

1.0.0a2 (2013-03-03)

  • 添加了Trove分类器来指定支持的Python版本。

1.0.0a1 (2013-03-03)

  • 添加了对Python 3.3的支持。

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

  • 不再支持Python 2.4和2.5。

  • 由于testbrowser尚未移植,浏览器测试已从zope.testbrowser切换到WebTest

  • 使用最新包和组件路径现代化API。

  • 将测试依赖减少到可能的最小集。

0.3.0 (2010-11-01)

  • 更新测试设置以在ZTK 1.0下运行。

  • 使用Python的doctest模块代替已弃用的zope.testing.doctest[unit]

0.2.5 (2009-03-13)

  • 适应从zope.component到zope.publisher的IDefaultViewName迁移。

0.2.4 (2009-02-02)

  • 使PluggableBrowserTraverser实现IBrowserPublisher接口。

  • 修复测试和弃用警告。

  • 提高测试覆盖率。

  • 通过直接调用替换其用法,消除对zope.app.zapi的依赖。

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

  • 将包的URL中的“cheeseshop”更改为“pypi”。

0.2.3 (2008-07-14)

  • 错误修复:在z3c.traverser.stackinfo中使用具有多个线程的VirtualHost命名空间时,遍历栈变得混乱。

0.2.2 (2008-03-06)

  • 重构:将可插入遍历器的功能分离为两个类,以实现更好的代码重用。

0.2.1 (2007-11-92)

  • 错误修复:如果视图小部件和管理器嵌套,当深度达到3时,由于上下文设置为页面而不是上下文对象,找不到视图小部件。

  • 错误修复:由于从absoluteURL中删除了_getContextName的调用。

0.2.0 (2007-10-31)

  • 更新包元数据。

  • 解决ZopeSecurityPolicy的弃用警告。

0.2.0b2 (2007-10-26)

  • 在未消费的URL计算中仅使用absolute_url适配器,以便它也能适用于可遍历视图小部件或其他特殊用例。

0.2.0b1 (2007-09-21)

  • 添加了一个通用堆栈消费者处理程序,可以注册用于BeforeTraverse事件。

0.1.3 (2007-06-03)

  • 添加了主体命名空间,请参阅namespace.rst

  • 在视图小部件视图中触发BeforeUpdateEvent

0.1.1 (2007-03-22)

  • 第一个egg发布

可插拔遍历器

遍历器是Zope将URI路径转换为应用程序对象的机制。它们提供了一个极端灵活的机制来根据应用程序的策略做出决策。不幸的是,默认的遍历器实现不够灵活,无法处理也希望参与遍历决策过程的任意扩展(通过适配器)的对象。

可插入遍历器允许开发者,特别是第三方开发者,在不更改原始遍历器实现的情况下向对象添加新的遍历器。

>>> from z3c.traverser.traverser import PluggableTraverser

假设我们有一个对象

>>> from zope.interface import Interface, implementer
>>> class IContent(Interface):
...     pass
>>> @implementer(IContent)
... class Content(object):
...     var = True
>>> content = Content()

我们希望遍历到。由于遍历器是呈现特定类型的,它们被实现为视图,因此必须使用请求启动

>>> from zope.publisher.base import TestRequest
>>> request = TestRequest('')
>>> traverser = PluggableTraverser(content, request)

现在我们可以尝试查找变量

>>> traverser.publishTraverse(request, 'var')
Traceback (most recent call last):
...
NotFound: Object: <Content object at ...>, name: 'var'

但是失败了。为什么?因为我们还没有注册一个知道如何查找属性的插件遍历器。这个包已经提供了一个这样的遍历器,所以我们只需要注册它

>>> from zope.component import provideSubscriptionAdapter
>>> from zope.publisher.interfaces import IPublisherRequest
>>> from z3c.traverser.traverser import AttributeTraverserPlugin
>>> provideSubscriptionAdapter(AttributeTraverserPlugin,
...                            (IContent, IPublisherRequest))

现在我们可以尝试查找属性,我们得到值

>>> traverser.publishTraverse(request, 'var')
True

然而,错误的变量名仍然会返回一个NotFound错误

>>> traverser.publishTraverse(request, 'bad')
Traceback (most recent call last):
...
NotFound: Object: <Content object at ...>, name: 'bad'

每个遍历器还应该确保传递的名称不是一个视图。(这允许我们在视图前不指定@@。)所以让我们注册一个

>>> class View(object):
...     def __init__(self, context, request):
...         pass
>>> from zope.component import provideAdapter
>>> from zope.publisher.interfaces import IPublisherRequest
>>> provideAdapter(View,
...                adapts=(IContent, IPublisherRequest),
...                provides=Interface,
...                name='view.html')

现在我们可以查找视图了

>>> traverser.publishTraverse(request, 'view.html')
<View object at ...>

高级用法

要考虑的更有趣的情况之一是容器遍历器。如果你真的不喜欢Zope 3遍历命名空间表示法++namespace++,并且你可以控制容器中的名称,那么可插入遍历器也会提供一个可行的解决方案。假设我们有一个容器

>>> from zope.container.interfaces import IContainer
>>> class IMyContainer(IContainer):
...     pass
>>> from zope.container.btree import BTreeContainer
>>> @implementer(IMyContainer)
... class MyContainer(BTreeContainer):
...     foo = True
...     bar = False
>>> myContainer = MyContainer()
>>> myContainer['blah'] = 123

并且我们希望能够遍历

  • 容器中的所有项目,以及

    >>> from z3c.traverser.traverser import ContainerTraverserPlugin
    >>> from z3c.traverser.interfaces import ITraverserPlugin
    
    >>> provideSubscriptionAdapter(ContainerTraverserPlugin,
    ...                            (IMyContainer, IPublisherRequest),
    ...                            ITraverserPlugin)
    
  • 属性 foo。幸运的是,我们也有一个预先开发的遍历器来完成这个任务

    >>> from z3c.traverser.traverser import \
    ...     SingleAttributeTraverserPlugin
    >>> provideSubscriptionAdapter(SingleAttributeTraverserPlugin('foo'),
    ...                            (IMyContainer, IPublisherRequest))
    

现在我们可以使用可插拔的遍历器

>>> traverser = PluggableTraverser(myContainer, request)

来查找项目

>>> traverser.publishTraverse(request, 'blah')
123

和属性 foo

>>> traverser.publishTraverse(request, 'foo')
True

然而,我们无法查找属性 bar 或任何其他不存在的项目

>>> traverser.publishTraverse(request, 'bar')
Traceback (most recent call last):
...
NotFound: Object: <MyContainer object at ...>, name: 'bar'
>>> traverser.publishTraverse(request, 'bad')
Traceback (most recent call last):
...
NotFound: Object: <MyContainer object at ...>, name: 'bad'

您还可以添加返回适配对象的遍历器。例如,让我们看一下以下适配器

>>> class ISomeAdapter(Interface):
...     pass
>>> from zope.component import adapts
>>> @implementer(ISomeAdapter)
... class SomeAdapter(object):
...     adapts(IMyContainer)
...
...     def __init__(self, context):
...         pass
>>> from zope.component import adapts, provideAdapter
>>> provideAdapter(SomeAdapter)

现在我们将此适配器注册为遍历名称 some

>>> from z3c.traverser.traverser import AdapterTraverserPlugin
>>> provideSubscriptionAdapter(
...     AdapterTraverserPlugin('some', ISomeAdapter),
...     (IMyContainer, IPublisherRequest))

所以这就是结果

>>> traverser.publishTraverse(request, 'some')
<SomeAdapter object at ...>

如果对象不可适配,我们将得到NotFound。让我们注册一个插件,该插件尝试查询名为ISomeAdapter的命名适配器。AdapterTraverserPlugin的第三个参数用于指定适配器名称。

>>> provideSubscriptionAdapter(
...     AdapterTraverserPlugin('badadapter', ISomeAdapter, 'other'),
...     (IMyContainer, IPublisherRequest))
>>> traverser.publishTraverse(request, 'badadapter')
Traceback (most recent call last):
...
NotFound: Object: <MyContainer object at ...>, name: 'badadapter'

遍历器插件

traverser 包包含几个默认遍历器插件;其中三个已在上面介绍:SingleAttributeTraverserPluginAdapterTraverserPluginContainerTraverserPlugin。另一个插件是 NullTraverserPlugin,它总是简单地返回对象本身

>>> from z3c.traverser.traverser import NullTraverserPlugin
>>> SomethingPlugin = NullTraverserPlugin('something')
>>> plugin = SomethingPlugin(content, request)
>>> plugin.publishTraverse(request, 'something')
<Content object at ...>
>>> plugin.publishTraverse(request, 'something else')
Traceback (most recent call last):
...
NotFound: Object: <Content object at ...>, name: 'something else'

上述所有遍历器(除了 ContainerTraverserPlugin)都是抽象类 NameTraverserPlugin 的实现。名称遍历器是可以解析特定名称的遍历器。通过使用抽象类 NameTraverserPlugin,可以避免遍历器样板代码。这里有一个简单的示例,它总是为遍历的名称返回一个特定值

>>> from z3c.traverser.traverser import NameTraverserPlugin
>>> class TrueTraverserPlugin(NameTraverserPlugin):
...     traversalName = 'true'
...     def _traverse(self, request, name):
...         return True

如您所见,已实现名称遍历器必须实现 _traverse() 方法,它仅负责返回结果。当然,如果在计算过程中出现问题,它也可以引发 NotFound 错误。我们来检查一下

>>> plugin = TrueTraverserPlugin(content, request)
>>> plugin.publishTraverse(request, 'true')
True
>>> plugin.publishTraverse(request, 'false')
Traceback (most recent call last):
...
NotFound: Object: <Content object at ...>, name: 'false'

包提供的最后一个遍历器是 AttributeTraverserPlugin,它简单地允许遍历对象的所有可访问属性

>>> from z3c.traverser.traverser import AttributeTraverserPlugin
>>> plugin = AttributeTraverserPlugin(myContainer, request)
>>> plugin.publishTraverse(request, 'foo')
True
>>> plugin.publishTraverse(request, 'bar')
False
>>> plugin.publishTraverse(request, 'blah')
Traceback (most recent call last):
...
NotFound: Object: <MyContainer object at ...>, name: 'blah'
>>> plugin.publishTraverse(request, 'some')
Traceback (most recent call last):
...
NotFound: Object: <MyContainer object at ...>, name: 'some'

浏览器遍历器

还有一个特殊的 PluggableTraverser 子类实现了 IBrowserPublisher 接口,从而提供了 browserDefault 方法,该方法返回一个默认对象和一个视图名称,如果遍历没有更多步骤,则用于遍历和使用。

让我们提供一个已注册为 IDefaultView 适配器的视图名称。这通常是通过 zope.publisher 的 browser:defaultView 指令完成的。

>>> from zope.publisher.interfaces import IDefaultViewName
>>> provideAdapter('view.html', (IContent, Interface), IDefaultViewName)
>>> from z3c.traverser.browser import PluggableBrowserTraverser
>>> traverser = PluggableBrowserTraverser(content, request)
>>> traverser.browserDefault(request)
(<Content object at 0x...>, ('@@view.html',))

附加命名空间

主体

principal 命名空间允许在 URL 中区分用户名。这对于基于主体的缓存很有用。命名空间本身不会改变任何东西。它只是检查主体是否是登录的用户。

>>> from z3c.traverser import namespace
>>> from zope.publisher.browser import TestRequest
>>> class Request(TestRequest):
...     principal = None
...
...     def shiftNameToApplication(self):
...         pass
>>> class Principal(object):
...     def __init__(self, id):
...         self.id = id
>>> pid = 'something'
>>> r = Request()
>>> r.principal = Principal('anonymous')

如果我们有错误的主体,我们将得到一个 Unauthorized 异常。

>>> ns = namespace.principal(object(), r)
>>> ns.traverse('another', None) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
Unauthorized: ++principal++another

否则不是

>>> ns.traverse('anonymous', None)
<object object at ...>

遍历视图插件

此包允许遍历视图小部件和视图管理器。它还提供了描述在此文件中的对象的绝对 URL 视图,有关遍历器,请参阅 BROWSER.rst。

>>> from z3c.traverser.viewlet import browser

让我们定义一些测试类。

>>> import zope.component
>>> from zope.viewlet import manager
>>> from zope.viewlet import interfaces
>>> from zope.publisher.interfaces.browser import IDefaultBrowserLayer
>>> import zope.interface
>>> class ILeftColumn(interfaces.IViewletManager):
...     """Viewlet manager located in the left column."""
>>> LeftColumn = manager.ViewletManager('left', ILeftColumn)
>>> zope.component.provideAdapter(
...     LeftColumn,
...     (zope.interface.Interface,
...     IDefaultBrowserLayer, zope.interface.Interface),
...     interfaces.IViewletManager, name='left')

您现在可以使用此接口创建视图小部件。

>>> from zope.viewlet import viewlet
>>> from zope.container.contained import Contained
>>> class Content(Contained):
...     pass
>>> root['content'] = Content()
>>> content = root['content']
>>> from zope.publisher.browser import TestRequest
>>> request = TestRequest()
>>> from zope.publisher.interfaces.browser import IBrowserView
>>> from zope.publisher.browser import BrowserView
>>> class View(BrowserView):
...     pass

我们必须设置名称,这通常在 zcml 中完成。

>>> view = View(content, request)
>>> view.__name__ = 'test.html'
>>> leftColumn = LeftColumn(content, request, view)

让我们创建一个简单的视图小部件。请注意,我们需要一个 __name__ 属性才能使视图小部件可遍历。通常您不必关心这一点,因为 zcml 指令在注册时设置名称。

>>> class MyViewlet(viewlet.ViewletBase):
...     __name__ = 'myViewlet'
...     def render(self):
...         return u'<div>My Viewlet</div>'
>>> from zope.security.checker import NamesChecker, defineChecker
>>> viewletChecker = NamesChecker(('update', 'render'))
>>> defineChecker(MyViewlet, viewletChecker)
>>> zope.component.provideAdapter(
...     MyViewlet,
...     (zope.interface.Interface, IDefaultBrowserLayer,
...     IBrowserView, ILeftColumn),
...     interfaces.IViewlet, name='myViewlet')

我们现在应该能够获取视图小部件和管理的绝对 URL。我们必须为测试注册适配器。

>>> from zope.traversing.browser.interfaces import IAbsoluteURL
>>> from zope.traversing.browser import absoluteurl
>>> zope.component.provideAdapter(
...     browser.ViewletAbsoluteURL,
...     (interfaces.IViewlet, IDefaultBrowserLayer),
...     IAbsoluteURL)
>>> zope.component.provideAdapter(
...     browser.ViewletManagerAbsoluteURL,
...     (interfaces.IViewletManager, IDefaultBrowserLayer),
...     IAbsoluteURL, name="absolute_url")
>>> zope.component.provideAdapter(
...     browser.ViewletManagerAbsoluteURL,
...     (interfaces.IViewletManager, IDefaultBrowserLayer),
...     IAbsoluteURL)
>>> myViewlet = MyViewlet(content, request, view, leftColumn)
>>> absoluteurl.absoluteURL(leftColumn, request)
'http://127.0.0.1/content/test.html/++manager++left'
>>> absoluteurl.absoluteURL(myViewlet, request)
'.../content/test.html/++manager++left/++viewlet++myViewlet'

视图插件遍历

通过命名空间进行视图小部件的遍历。

>>> from webtest.app import TestApp
>>> browser = TestApp(wsgi_app)
>>> res = browser.get('https://127.0.0.1/@@test.html')

我们已经注册了一个测试页面,其中包含我们的视图小部件。视图小部件本身仅渲染一个链接到其位置的链接(这只是用于测试)。

>>> print(res.html)
<html>
  <body>
     <div><div><a
     href="https://127.0.0.1/test.html/++manager++IMyManager/++viewlet++MyViewlet">My
     Viewlet</a></div></div>
  </body>
</html>

让我们跟随链接直接遍历视图小部件。

>>> res = res.click('My Viewlet')
>>> res.request.url
'https://127.0.0.1/test.html/++manager++IMyManager/++viewlet++MyViewlet'
>>> print(res.body.decode())
<div><a href="https://127.0.0.1/test.html/++manager++IMyManager/++viewlet++MyViewlet">My Viewlet</a></div>

如果一个视图管理器嵌套在另一个视图小部件中会发生什么?为了测试这一点,我们将创建另一个管理器和另一个视图小部件

>>> res = browser.get('https://127.0.0.1/@@nested.html')
>>> print(res.html)
<html>
  <body>
    <div><div><a href="https://127.0.0.1/nested.html/++manager++IOuterManager/++viewlet++OuterViewlet/++manager++IInnerManager/++viewlet++InnerViewlet/++manager++IMostInnerManager/++viewlet++MostInnerViewlet">Most inner viewlet</a></div></div>
  </body>
</html>

让我们跟随链接直接遍历视图小部件。

>>> res = res.click('Most inner viewlet')
>>> res.request.url
'https://127.0.0.1/nested.html/++manager++IOuterManager/++viewlet++OuterViewlet/++manager++IInnerManager/++viewlet++InnerViewlet/++manager++IMostInnerManager/++viewlet++MostInnerViewlet'
>>> print(res.body.decode())
<div><a href="https://127.0.0.1/nested.html/++manager++IOuterManager/++viewlet++OuterViewlet/++manager++IInnerManager/++viewlet++InnerViewlet/++manager++IMostInnerManager/++viewlet++MostInnerViewlet">Most inner viewlet</a></div>

注意事项

未调用管理器更新,因为这可能过于昂贵,通常管理器更新只是收集视图。

从遍历堆栈中提取信息

此包允许定义虚拟遍历路径,以便从遍历堆栈中收集任意信息,而不是例如查询字符串。

与通常定义自定义遍历器的常用方式相比,此实现不需要逐步执行整个遍历过程。所需遍历信息直接从遍历堆栈中获取,并消耗使用的堆栈部分。这样,就不必仅为了遍历而定义代理类。

此实现不适用于 tales,因为它需要请求的遍历堆栈。

对于遍历堆栈中的每个名称,都会查找一个名为 ITraversalStackConsumer 的命名多适配器,如果找到,则从堆栈中删除该项目,并将适配器添加到请求注解中。

>>> from z3c.traverser.stackinfo import traversing
>>> from z3c.traverser.stackinfo import interfaces

如果没有定义适配器,则遍历堆栈保持不变。为了展示此行为,我们定义了一些示例类。

>>> from zope import interface
>>> class IContent(interface.Interface):
...     pass
>>> from zope.site.folder import Folder
>>> @interface.implementer(IContent)
... class Content(Folder):
...     pass

存在一个方便的函数,它返回一个迭代器,该迭代器遍历适配器名称和适配器的元组。如果需要,还会消耗请求的遍历堆栈。

>>> from zope.publisher.browser import TestRequest
>>> from zope.publisher.interfaces.browser import IBrowserRequest
>>> request = TestRequest()

在此处手动设置遍历堆栈以进行测试。

>>> request.setTraversalStack(['index.html', 'path', 'some'])
>>> content = Content()

因此,如果没有找到 ITraversalStackConsumer 适配器,则堆栈保持不变。

>>> list(traversing.getStackConsumers(content, request))
[]
>>> request.getTraversalStack()
['index.html', 'path', 'some']

存在一个针对消费者实现的基本类,它实现了 ITraversalStackConsumer 接口。

>>> from z3c.traverser.stackinfo import consumer
>>> from zope.interface.verify import verifyObject
>>> o = consumer.BaseConsumer(None, None)
>>> verifyObject(interfaces.ITraversalStackConsumer,o)
True

让我们定义一个自定义消费者。

>>> from zope import component
>>> class DummyConsumer(consumer.BaseConsumer):
...     component.adapts(IContent, IBrowserRequest)
>>> component.provideAdapter(DummyConsumer, name='some')

现在我们将找到新注册的消费者,并且堆栈中的“some”部分被消耗。

>>> consumers = list(traversing.getStackConsumers(content, request))
>>> consumers
[('some', <DummyConsumer named 'some'>)]
>>> request.getTraversalStack()
['index.html', 'path']

每个消费者至少必须消耗一个元素,这始终是适配器注册的名称。

>>> name, cons = consumers[0]
>>> cons.__name__
'some'

让我们提供一个其他适配器,以演示适配器始终具有遍历堆栈的相反顺序。这实际上是 URL 中的顺序。

>>> component.provideAdapter(DummyConsumer, name='other')
>>> stack = ['index.html', 'path', 'some', 'other']
>>> request.setTraversalStack(stack)
>>> consumers = list(traversing.getStackConsumers(content, request))
>>> consumers
[('other', <DummyConsumer named 'other'>),
 ('some', <DummyConsumer named 'some'>)]
>>> [c.__name__ for name, c in consumers]
['other', 'some']

消费者类的 arguments 属性定义了从堆栈中消耗/需要的参数数量。让我们创建一个键值消费者,该消费者应从堆栈中提取键值对。

>>> class KeyValueConsumer(DummyConsumer):
...     arguments=('key', 'value')
>>> component.provideAdapter(KeyValueConsumer, name='kv')
>>> stack = ['index.html', 'value', 'key', 'kv']
>>> request.setTraversalStack(stack)
>>> consumers = list(traversing.getStackConsumers(content, request))
>>> consumers
[('kv', <KeyValueConsumer named 'kv'>)]
>>> request.getTraversalStack()
['index.html']
>>> name, cons = consumers[0]
>>> cons.key
'key'
>>> cons.value
'value'

当然,我们可以使用相同类型的多个消费者。

>>> stack = ['index.html', 'v2', 'k2', 'kv', 'v1', 'k1', 'kv']
>>> request.setTraversalStack(stack)
>>> consumers = list(traversing.getStackConsumers(content, request))
>>> [(c.__name__, c.key, c.value) for name, c in consumers]
[('kv', 'k1', 'v1'), ('kv', 'k2', 'v2')]

如果我们有太少的参数,则抛出 NotFound 异常。

>>> stack = ['k2', 'kv', 'v1', 'k1', 'kv']
>>> request.setTraversalStack(stack)
>>> consumers = list(traversing.getStackConsumers(content, request))
Traceback (most recent call last):
  ...
NotFound: Object: <Content object at ...>, name: 'kv'

为了实际使用堆栈消费者检索信息,还有一个方便的函数,该函数将消费者存储在请求注解中。这通常应在 BeforeTraverseEvents 上调用。

>>> stack = ['index.html', 'v2', 'k2', 'kv', 'v1', 'k1', 'kv']
>>> request.setTraversalStack(stack)
>>> traversing.applyStackConsumers(content, request)
>>> request.annotations[traversing.CONSUMERS_ANNOTATION_KEY]
[<KeyValueConsumer named 'kv'>,
 <KeyValueConsumer named 'kv'>]

而不是与注解纠缠,可以直接将请求适配到 ITraversalStackInfo。

>>> component.provideAdapter(consumer.requestTraversalStackInfo)
>>> ti = interfaces.ITraversalStackInfo(request)
>>> ti
(<KeyValueConsumer named 'kv'>, <KeyValueConsumer named 'kv'>)
>>> len(ti)
2

如果没有遍历堆栈信息,适配器始终返回一个空的 TraversalStackInfoObject。

>>> request = TestRequest()
>>> ti = interfaces.ITraversalStackInfo(request)
>>> len(ti)
0

虚拟主机

如果使用虚拟主机,遍历堆栈将包含虚拟主机的一些附加信息,这将干扰堆栈消费者。

>>> stack = ['index.html', 'value', 'key',
...          'kv', '++', 'inside vh', '++vh++something']
>>> request.setTraversalStack(stack)
>>> consumers = list(traversing.getStackConsumers(content, request))
>>> consumers
[('kv', <KeyValueConsumer named 'kv'>)]
>>> request.getTraversalStack()
['index.html', '++', 'inside vh', '++vh++something']

URL处理

让我们用真实的 URL 尝试这些事情,在我们的测试中,根是站点。

>>> from zope.traversing.browser.absoluteurl import absoluteURL
>>> absoluteURL(root, request)
'http://127.0.0.1'

存在一个 unconsumedURL 函数,它返回具有遍历信息的对象的 URL,这通常被省略。

>>> request = TestRequest()
>>> root['content'] = content
>>> absoluteURL(root['content'], request)
'http://127.0.0.1/content'
>>> stack = ['index.html', 'v2 space', 'k2', 'kv', 'v1', 'k1', 'kv']
>>> request.setTraversalStack(stack)
>>> traversing.applyStackConsumers(root['content'], request)
>>> traversing.unconsumedURL(root['content'], request)
'http://127.0.0.1/content/kv/k1/v1/kv/k2/v2%20space'

让我们有多个内容对象。

>>> under = content['under'] = Content()
>>> request = TestRequest()
>>> traversing.unconsumedURL(under, request)
'http://127.0.0.1/content/under'

我们向上述对象添加一些消费者。

>>> request = TestRequest()
>>> stack = ['index.html', 'value1', 'key1', 'kv']
>>> request.setTraversalStack(stack)
>>> traversing.applyStackConsumers(root['content'], request)
>>> traversing.unconsumedURL(root['content'], request)
'http://127.0.0.1/content/kv/key1/value1'
>>> traversing.unconsumedURL(under, request)
'http://127.0.0.1/content/kv/key1/value1/under'

并将它们添加到下面的对象中。

>>> request = TestRequest()
>>> stack = ['index.html', 'value1', 'key1', 'kv']
>>> request.setTraversalStack(stack)
>>> traversing.applyStackConsumers(root['content'], request)
>>> stack = ['index.html', 'value2', 'key2', 'kv']
>>> request.setTraversalStack(stack)
>>> traversing.applyStackConsumers(under, request)
>>> traversing.unconsumedURL(root['content'], request)
'http://127.0.0.1/content/kv/key1/value1'
>>> traversing.unconsumedURL(under, request)
'http://127.0.0.1/content/kv/key1/value1/under/kv/key2/value2'

或者仅添加到下面的对象中。

>>> request = TestRequest()
>>> traversing.applyStackConsumers(root['content'], request)
>>> stack = ['index.html', 'value2', 'key2', 'kv']
>>> request.setTraversalStack(stack)
>>> traversing.applyStackConsumers(under, request)
>>> traversing.unconsumedURL(root['content'], request)
'http://127.0.0.1/content'
>>> traversing.unconsumedURL(under, request)
'http://127.0.0.1/content/under/kv/key2/value2'

unconsumedURL 函数也作为视图提供,名为 unconsumed_url,类似于 absolute_url

>>> from zope.component import getMultiAdapter
>>> url = getMultiAdapter((under, request), name='unconsumed_url')
>>> str(url)
'http://127.0.0.1/content/under/kv/key2/value2'
>>> url()
'http://127.0.0.1/content/under/kv/key2/value2'

从遍历堆栈中提取信息

这是一个简单的示例,以展示此包的用法。请查看测试目录,了解应该如何设置。

>>> from webtest.app import TestApp
>>> browser = TestApp(wsgi_app,
...     extra_environ={'wsgi.handleErrors': False,
...                    'paste.throw_errors': True,
...                    'x-wsgiorg.throw_errors': True})
>>> res = browser.get('https://127.0.0.1/@@stackinfo.html')

因此,基本上我们没有任何堆栈信息。

>>> print(res.body.decode())
Stack Info from object at https://127.0.0.1/stackinfo.html:

让我们尝试将 foo 设置为 bar。

>>> res = browser.get('https://127.0.0.1/kv/foo/bar/@@stackinfo.html')
>>> print(res.body.decode())
Stack Info from object at https://127.0.0.1/stackinfo.html:
consumer kv:
key = 'foo'
value = 'bar'

两个消费者。

>>> res = browser.get(
...     'https://127.0.0.1/kv/foo/bar/kv/time/late/@@stackinfo.html')
>>> print(res.body.decode())
Stack Info from object at https://127.0.0.1/stackinfo.html:
consumer kv:
key = 'foo'
value = 'bar'
consumer kv:
key = 'time'
value = 'late'

无效的 URL

>>> browser.get('https://127.0.0.1/kv/foo/bar/kv/@@stackinfo.html') \
...     # doctes: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
NotFound: Object: <...Folder object at ...>, name: 'kv'

项目详情


下载文件

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

源代码分发

z3c.traverser-2.0.tar.gz (36.7 kB 查看哈希值)

上传时间 源代码

构建分发

z3c.traverser-2.0-py3-none-any.whl (40.3 kB 查看哈希值)

上传时间 Python 3

由以下支持

AWSAWS云计算和安全赞助商DatadogDatadog监控FastlyFastlyCDNGoogleGoogle下载分析MicrosoftMicrosoftPSF赞助商PingdomPingdom监控SentrySentry错误日志StatusPageStatusPage状态页面