可插拔遍历器和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 包包含几个默认遍历器插件;其中三个已在上面介绍:SingleAttributeTraverserPlugin、AdapterTraverserPlugin 和 ContainerTraverserPlugin。另一个插件是 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的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 1ef5a0ee392ccb8c5da199a7cc0a5bacf7624bd04606afe354742f0659722809 |
|
MD5 | 5a22aaa10d8268699d04cd702f6204a6 |
|
BLAKE2b-256 | 48059aff783cfe80678ee8c736b0438c689ac90f2c7e0ba588667db30a95b10d |
z3c.traverser-2.0-py3-none-any.whl的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 990348bbdf948a502211a7f217e021d16b856f9ad4c8586bc9feddc947a52960 |
|
MD5 | 750b68f49287ee200def101d850ba48b |
|
BLAKE2b-256 | ebafc61c7edd5169ad42884cf9ec0b0d0507612151695a1e1d229c76137bc6e4 |