dispatch.py |
|
---|---|
Imports |
from __future__ import unicode_literals
from collections import deque
from inspect import isclass
from marrow.package.host import PluginManager
|
Module Globals |
|
A standard logger object. |
log = __import__('logging').getLogger(__name__)
|
Dispatch Plugin Manager |
|
WebCore dispatch protocol adapter. The process of resolving a request path to an endpoint. The dispatch protocol utilized is documented in full
in the The allowable controller structures offered by individual methods of dispatch is documented in the relevant dispatch project. Examples of dispatch include:
Others may exist, and dispatch middleware may be available to perform more complex behaviours. The default dispatcher if not otherwise configured is object dispatch. |
class WebDispatchers(PluginManager):
__isabstractmethod__ = False # Work around an issue in modern (3.4+) Python due to our instances being callable.
|
Dispatch registry constructor. The dispatch registry is not meant to be instantiated by third-party software. Instead, access the registry as
an attribute of the current Application or Request context: |
def __init__(self, ctx):
super(WebDispatchers, self).__init__('web.dispatch')
|
Having been bound to an appropriate context, find a handler for the request path. This is the part of the WebCore request cycle that speaks the Dispatch protocol and performs event bridging. This requires a context prepared with a |
def __call__(self, context, handler, path):
is_endpoint = False # We'll search until we find an endpoint.
|
This technically doesn't help Pypy at all, but saves repeated deep lookup in CPython.(E) |
callbacks = context.extension.signal.dispatch # These extensions want to be notified.
self = context.dispatch # Dispatch plugin registry.
if __debug__:
log.debug("Preparing dispatch.", extra=dict(
request = id(context.request),
path = context.request.path_info,
handler = repr(handler)
))
|
Now we need the remaining path elements as a deque. |
path = deque(path.split('/'))
|
We don't want a singular leading / in the path to cause trouble. |
if path and not path[0]:
path.popleft()
try:
while not is_endpoint:
|
Pull the dispatcher out of the current handler, defaulting to object dispatch. |
dispatcher = self[getattr(handler, '__dispatch__', 'object')]
|
We don't stop if the same dispatcher is loaded twice since some dispatchers might want to do that. |
starting = handler
|
Iterate dispatch events, issuing appropriate callbacks as we descend. |
for consumed, handler, is_endpoint in dispatcher(context, handler, path):
|
DO NOT add production logging statements (ones not wrapped in |
for ext in callbacks: ext(context, consumed, handler, is_endpoint)
|
Repeat of earlier, we do this after extensions in case anything above modifies the environ path. |
path = deque(context.environ['PATH_INFO'].split('/'))
if path and not path[0]:
path.popleft()
if not is_endpoint and starting is handler: # We didn't go anywhere.
break
|
Dispatch failed utterly. |
except LookupError:
pass # `is_endpoint` can only be `False` here.
return is_endpoint, handler if is_endpoint else None
|
Retrieve a dispatcher from the registry. This performs some additional work beyond the standard plugin manager in order to construct configured instances of the dispatchers instead of simply returning them bare. This allows for configuration and caching of these configured dispatchers to happen in a single place. |
def __getitem__(self, dispatcher):
name = None
if callable(dispatcher) and not isclass(dispatcher):
return dispatcher
|
If the dispatcher isn't already executable, it's probably an entry point reference. Load it from cache. |
if not isclass(dispatcher):
name = dispatcher
dispatcher = super(WebDispatchers, self).__getitem__(dispatcher)
|
If it's uninstantiated, instantiate it. |
if isclass(dispatcher):
|
TODO: Extract **kw settings from context. |
dispatcher = dispatcher() # Instantiate the dispatcher.
if name: # Update the entry point cache if loaded by name.
self.named[name] = dispatcher
if __debug__:
log.debug("Loaded dispatcher.", extra=dict(dispatcher=repr(dispatcher)))
return dispatcher
|