view.py |
|
---|---|
The WebCore view registry. WebCore uses a registry of callables to transform values returned by controllers for use as a response. This
translation process is used to support the built-in types (see the A view handler takes the result of calling a controller endpoint and applies the result to the response. It can be
as simple as a bare function that accepts the current
Slightly more useful would be to assign the result directly to the response:
As an example pulled from the "template" extension, you can use an "exit early" strategy to selectively handle subsets of the type your view is registered against, such as only handling 2-tuples with a specific first value:
A view may go so far as to wholly replace the If you frequently hand back serialized data, you may be able to simplify your controller code and reduce boilerplate by simply returning your model instances. By registering a view handler for your model class you can implement the serialization in a single, centralized location, making security and testing much easier. When a controller raises an |
|
Imports |
from __future__ import unicode_literals
from webob.multidict import MultiDict
from marrow.package.canonical import name
from marrow.package.host import PluginManager
from .compat import py3, pypy
|
Module Globals |
|
A standard logger object. |
log = __import__('logging').getLogger(__name__)
|
View Registry |
|
A This extends plugin naming to support multiple candidates per name, overrides manual registration to log some
useful messages, and makes instances callable. Executing a |
class WebViews(PluginManager):
__isabstractmethod__ = False # Work around a Python 3.4+ issue, since our instances are callable.
|
Python Protocols |
|
View registry constructor. The view registry is not meant to be instantiated by third-party software. Instead, access the registry as
an attribute of the the current |
def __init__(self, ctx):
super(WebViews, self).__init__('web.view')
self.__dict__['_map'] = MultiDict()
|
Programmers' representation for development-time diagnostics. |
def __repr__(self):
return "WebViews({})".format(len(self._map))
|
Identify view to use based on the type of result when our instance is called as a function. This generates a stream of candidates which should be called in turn until one returns a truthy value. |
def __call__(self, result):
rtype = type(result)
|
Short-circuit on exact matches. |
for candidate in self._map.getall(rtype):
yield candidate
|
More exhaustive search for potentially more crafty use such as ABC, zope.interface, marrow.interface, etc. |
for kind, candidate in self._map.iteritems():
if kind is rtype: continue # We've already seen these.
if isinstance(result, kind):
yield candidate
|
Plugin Registration |
|
Register a handler for a given type, class, interface, or abstract base class. View registration should happen within the
The approach of explicitly referencing a view handler isn't very easy to override without also replacing the
extension originally adding it, however there is another approach. Using named handlers registered as discrete
plugins (via the
Otherwise unknown attributes of the view registry will attempt to look up a handler plugin by that name. |
def register(self, kind, handler):
if __debug__: # In production this logging is completely skipped, regardless of logging level.
if py3 and not pypy: # Where possible, we shorten things to just the cannonical name.
log.debug("Registering view handler.", extra=dict(type=name(kind), handler=name(handler)))
else: # Canonical name lookup is not entirely reliable on some combinations.
log.debug("Registering view handler.", extra=dict(type=repr(kind), handler=repr(handler)))
|
Add the handler to the pool of candidates. This adds to a list instead of replacing the "dictionary item". |
self._map.add(kind, handler)
return handler
|