Presentation Inspection Utilities¶
The presentation
module provides some nice utilities to inspect presentation
registrations.
>>> from zope.app.apidoc import presentation
getViewFactoryData()
¶
This function tries really hard to determine the correct information about a view factory. For example, when you create a page, a new type is dynamically generated upon registration. Let’s look at a couple examples.
First, let’s inspect a case where a simple browser page was configured without
a special view class. In these cases the factory is a SimpleViewClass
:
>>> from zope.browserpage.simpleviewclass import SimpleViewClass
>>> view = SimpleViewClass('browser/index.pt')
>>> info = presentation.getViewFactoryData(view)
Before we can check the result, we have to make sure that all Windows paths are converted to Unix-like paths. We also clip off instance-specific parts of the template path:
>>> info['template'] = info['template'].replace('\\', '/')[-32:]
>>> from pprint import pprint
>>> pprint(info)
{'path': 'zope.browserpage.simpleviewclass.simple',
'referencable': True,
'resource': None,
'template': 'zope/app/apidoc/browser/index.pt',
'template_obj': <BoundPageTemplateFile of None>,
'url': 'zope/browserpage/simpleviewclass/simple'}
So in the result above we see what the function returns. It is a dictionary
(converted to a list for test purposes) that contains the Python path of the
view class, a flag that specifies whether the factory can be referenced and
thus be viewed by the class browser, the (page) template used for the view and
the URL under which the factory will be found in the class browser. Some
views, like icons, also use resources to provide their data. In these cases
the name of the resource will be provided. Of course, not in all cases all
values will be available. Empty values are marked with None
.
Believe it or not, in some cases the factory is just a simple type. In these cases we cannot retrieve any useful information:
>>> info = presentation.getViewFactoryData(3)
>>> pprint(info)
{'path': 'builtins.int',
'referencable': False,
'resource': None,
'template': None,
'url': None}
In some cases factories are callable class instances, where we cannot directly have a referencable name, so we lookup the class and use its name:
>>> class Factory(object):
... pass
>>> info = presentation.getViewFactoryData(Factory())
>>> pprint(info)
{'path': 'zope.app.apidoc.doctest.Factory',
'referencable': True,
'resource': None,
'template': None,
'url': 'zope/app/apidoc/doctest/Factory'}
One of the more common cases, however, is that the factory is a class or type. In this case we can just retrieve the reference directly:
>>> info = presentation.getViewFactoryData(Factory)
>>> pprint(info)
{'path': 'zope.app.apidoc.doctest.Factory',
'referencable': True,
'resource': None,
'template': None,
'url': 'zope/app/apidoc/doctest/Factory'}
When factories are created by a directive, they can also be functions. In those cases we just simply return the function path:
>>> def factory():
... pass
>>> factory.__module__ = 'zope.app.apidoc.doctest' # The testing framework does not set the __module__ correctly
>>> info = presentation.getViewFactoryData(factory)
>>> pprint(info)
{'path': 'zope.app.apidoc.doctest.factory',
'referencable': True,
'resource': None,
'template': None,
'url': 'zope/app/apidoc/doctest/factory'}
However, the function is rather unhelpful, since it will be the same for all
views that use that code path. For this reason the function keeps track of the
original factory component in a function attribute called factory
:
>>> factory.factory = Factory
>>> info = presentation.getViewFactoryData(factory)
>>> pprint(info)
{'path': 'zope.app.apidoc.doctest.Factory',
'referencable': True,
'resource': None,
'template': None,
'url': 'zope/app/apidoc/doctest/Factory'}
Let’s now have a look at some extremly specific cases. If a view is registered
using the zope:view
directive and a permission is specified, a
ProxyView
class instance is created that references its original factory:
>>> class ProxyView(object):
...
... def __init__(self, factory):
... self.factory = factory
>>> proxyView = ProxyView(Factory)
>>> info = presentation.getViewFactoryData(proxyView)
>>> pprint(info)
{'path': 'zope.app.apidoc.doctest.Factory',
'referencable': True,
'resource': None,
'template': None,
'url': 'zope/app/apidoc/doctest/Factory'}
Another use case is when a new type is created by the browser:page
or
browser:view
directive. In those cases the true/original factory is really
the first base class. Those cases are detected by inspecting the
__module__
string of the type:
>>> new_class = type(Factory.__name__, (Factory,), {})
>>> new_class.__module__ = 'zope.app.publisher.browser.viewmeta'
>>> info = presentation.getViewFactoryData(new_class)
>>> pprint(info)
{'path': 'zope.app.apidoc.doctest.Factory',
'referencable': True,
'resource': None,
'template': None,
'url': 'zope/app/apidoc/doctest/Factory'}
The same sort of thing happens for XML-RPC views, except that those are wrapped twice:
>>> new_class = type(Factory.__name__, (Factory,), {})
>>> new_class.__module__ = 'zope.app.publisher.xmlrpc.metaconfigure'
>>> new_class2 = type(Factory.__name__, (new_class,), {})
>>> new_class2.__module__ = 'zope.app.publisher.xmlrpc.metaconfigure'
>>> info = presentation.getViewFactoryData(new_class2)
>>> pprint(info)
{'path': 'zope.app.apidoc.doctest.Factory',
'referencable': True,
'resource': None,
'template': None,
'url': 'zope/app/apidoc/doctest/Factory'}
Finally, it sometimes happens that a factory is wrapped and the wrapper is wrapped in return:
>>> def wrapper1(*args):
... return Factory(*args)
>>> def wrapper2(*args):
... return wrapper1(*args)
Initially, the documentation is not very helpful:
>>> info = presentation.getViewFactoryData(wrapper2)
>>> pprint(info)
{'path': 'zope.app.apidoc.doctest.wrapper2',
'referencable': True,
'resource': None,
'template': None,
'url': 'zope/app/apidoc/doctest/wrapper2'}
However, if those wrappers play nicely, they provide a factory attribute each step of the way …
>>> wrapper1.factory = Factory
>>> wrapper2.factory = wrapper1
and the result is finally our original factory:
>>> info = presentation.getViewFactoryData(wrapper2)
>>> pprint(info)
{'path': 'zope.app.apidoc.doctest.Factory',
'referencable': True,
'resource': None,
'template': None,
'url': 'zope/app/apidoc/doctest/Factory'}
getPresentationType()
¶
In Zope 3, presentation types (i.e. browser, ftp, …) are defined through
their special request interface, such as IBrowserRequest
or
IFTPRequest
. To complicate matters further, layer interfaces are used in
browser presentations to allow skinning. Layers extend any request type, but
most commonly IBrowserRequest
. This function inspects the request interface
of any presentation multi-adapter and determines its type, which is returned
in form of an interface.
>>> from zope.app.apidoc.presentation import getPresentationType
>>> from zope.publisher.interfaces.http import IHTTPRequest
>>> from zope.publisher.interfaces.browser import IBrowserRequest
>>> class ILayer1(IBrowserRequest):
... pass
>>> presentation.getPresentationType(ILayer1)
<InterfaceClass zope.publisher.interfaces.browser.IBrowserRequest>
>>> class ILayer2(IHTTPRequest):
... pass
>>> presentation.getPresentationType(ILayer2)
<InterfaceClass zope.publisher.interfaces.http.IHTTPRequest>
If the function cannot determine the presentation type, the interface itself is returned:
>>> from zope.interface import Interface
>>> class ILayer3(Interface):
... pass
>>> presentation.getPresentationType(ILayer3)
<InterfaceClass zope.app.apidoc.doctest.ILayer3>
Note that more specific presentation types are considered first. For
example, zope.publisher.interfaces.browser.IBrowserRequest
extends zope.publisher.interfaces.http.IHTTPRequest
, but it
will always determine the presentation type to be an
IBrowserRequest
.
getViews()
¶
This function retrieves all available view registrations for a given
interface and presentation type. The default argument for the
presentation type is zope.publisher.interfaces.IRequest
,
which will effectively return all views for the specified interface.
To see how this works, we first have to register some views:
>>> class IFoo(Interface):
... pass
>>> from zope import component as ztapi
>>> ztapi.provideAdapter(adapts=(IFoo, IHTTPRequest), provides=Interface, factory=None, name='foo')
>>> ztapi.provideAdapter(adapts=(Interface, IHTTPRequest), provides=Interface, factory=None,
... name='bar')
>>> ztapi.provideAdapter(adapts=(IFoo, IBrowserRequest), provides=Interface, factory=None,
... name='blah')
Now let’s see what we’ve got. If we do not specify a type, all registrations should be returned:
>>> regs = list(presentation.getViews(IFoo))
>>> regs.sort()
>>> regs
[AdapterRegistration(<BaseGlobalComponents base>,
[IFoo, IBrowserRequest], Interface, 'blah', None, ''),
AdapterRegistration(<BaseGlobalComponents base>,
[IFoo, IHTTPRequest], Interface, 'foo', None, ''),
AdapterRegistration(<BaseGlobalComponents base>,
[Interface, IHTTPRequest], Interface, 'bar', None, '')]
>>> regs = list(presentation.getViews(Interface, IHTTPRequest))
>>> regs.sort()
>>> regs
[AdapterRegistration(<BaseGlobalComponents base>,
[Interface, IHTTPRequest], Interface, 'bar', None, '')]
filterViewRegistrations()
¶
Oftentimes the amount of views that are being returned for a particular
interface are too much to show at once. It is then good to split the view into
categories. The filterViewRegistrations()
function allows you to filter the
views on how specific they are to the interface. Here are the three levels you
can select from:
- SPECIFC_INTERFACE_LEVEL – Only return registrations that require the
specified interface directly.
- EXTENDED_INTERFACE_LEVEL – Only return registrations that require an
interface that the specified interface extends.
- GENERIC_INTERFACE_LEVEL – Only return registrations that explicitely
require the
Interface
interface.
So, let’s see how this is done. We first need to create a couple of interfaces and register some views:
>>> class IContent(Interface): ... pass >>> class IFile(IContent): ... passClear out the registries first, so we know what we have. >>> from zope.testing.cleanup import cleanUp >>> cleanUp()
>>> ztapi.provideAdapter(adapts=(IContent, IHTTPRequest), provides=Interface, ... factory=None, name='view.html') >>> ztapi.provideAdapter(adapts=(IContent, IHTTPRequest), provides=Interface, ... factory=None, name='edit.html') >>> ztapi.provideAdapter(adapts=(IFile, IHTTPRequest), provides=Interface, ... factory=None, name='view.html') >>> ztapi.provideAdapter(adapts=(Interface, IHTTPRequest), provides=Interface, ... factory=None, name='view.html')
Now we get all the registrations:
>>> regs = list(presentation.getViews(IFile, IHTTPRequest))
Let’s now filter those registrations:
>>> result = list(presentation.filterViewRegistrations(
... regs, IFile, level=presentation.SPECIFIC_INTERFACE_LEVEL))
>>> result.sort()
>>> result
[AdapterRegistration(<BaseGlobalComponents base>,
[IFile, IHTTPRequest], Interface, 'view.html', None, '')]
>>> result = list(presentation.filterViewRegistrations(
... regs, IFile, level=presentation.EXTENDED_INTERFACE_LEVEL))
>>> result.sort()
>>> result
[AdapterRegistration(<BaseGlobalComponents base>,
[IContent, IHTTPRequest], Interface, 'edit.html', None, ''),
AdapterRegistration(<BaseGlobalComponents base>,
[IContent, IHTTPRequest], Interface, 'view.html', None, '')]
>>> result = list(presentation.filterViewRegistrations(
... regs, IFile, level=presentation.GENERIC_INTERFACE_LEVEL))
>>> result.sort()
>>> result
[AdapterRegistration(<BaseGlobalComponents base>,
[Interface, IHTTPRequest], Interface, 'view.html', None, '')]
You can also specify multiple levels at once using the Boolean OR operator, since all three levels are mutually exclusive.
>>> result = list(presentation.filterViewRegistrations(
... regs, IFile, level=presentation.SPECIFIC_INTERFACE_LEVEL |
... presentation.EXTENDED_INTERFACE_LEVEL))
>>> result.sort()
>>> result
[AdapterRegistration(<BaseGlobalComponents base>,
[IContent, IHTTPRequest], Interface, 'edit.html', None, ''),
AdapterRegistration(<BaseGlobalComponents base>,
[IContent, IHTTPRequest], Interface, 'view.html', None, ''),
AdapterRegistration(<BaseGlobalComponents base>,
[IFile, IHTTPRequest], Interface, 'view.html', None, '')]
>>> result = list(presentation.filterViewRegistrations(
... regs, IFile, level=presentation.SPECIFIC_INTERFACE_LEVEL |
... presentation.GENERIC_INTERFACE_LEVEL))
>>> result.sort()
>>> result
[AdapterRegistration(<BaseGlobalComponents base>,
[IFile, IHTTPRequest], Interface, 'view.html', None, ''),
AdapterRegistration(<BaseGlobalComponents base>,
[Interface, IHTTPRequest], Interface, 'view.html', None, '')]
getViewInfoDictionary()
¶
Now that we have all these utilities to select the registrations, we need to prepare the them for output. For page templates the best data structures are dictionaries and tuples/lists. This utility will generate an informational dictionary for the specified registration.
Let’s first create a registration:
>>> from zope.interface.registry import AdapterRegistration
>>> reg = AdapterRegistration(None, (IFile, Interface, IHTTPRequest),
... Interface, 'view.html', Factory, 'reg info')
>>> pprint(presentation.getViewInfoDictionary(reg), width=50)
{'doc': 'reg info',
'factory': {'path': 'zope.app.apidoc.doctest.Factory',
'referencable': True,
'resource': None,
'template': None,
'url': 'zope/app/apidoc/doctest/Factory'},
'name': 'view.html',
'provided': {'module': 'zope.interface',
'name': 'Interface'},
'read_perm': None,
'required': [{'module': 'zope.app.apidoc.doctest',
'name': 'IFile'},
{'module': 'zope.interface',
'name': 'Interface'},
{'module': 'zope.publisher.interfaces.http',
'name': 'IHTTPRequest'}],
'type': 'zope.publisher.interfaces.http.IHTTPRequest',
'write_perm': None,
'zcml': None}