PageRenderTime 20ms CodeModel.GetById 10ms app.highlight 7ms RepoModel.GetById 1ms app.codeStats 0ms

/circuits/web/dispatchers/dispatcher.py

https://bitbucket.org/prologic/circuits/
Python | 147 lines | 101 code | 36 blank | 10 comment | 42 complexity | 59075dd5fff9aba324585ca9d5f337d1 MD5 | raw file
  1# Module:   dispatcher
  2# Date:     13th September 2007
  3# Author:   James Mills, prologic at shortcircuit dot net dot au
  4
  5"""Dispatcher
  6
  7This module implements a basic URL to Channel dispatcher.
  8This is the default dispatcher used by circuits.web
  9"""
 10
 11try:
 12    from urllib import quote, unquote
 13except ImportError:
 14    from urllib.parse import quote, unquote  # NOQA
 15
 16from circuits.six import text_type
 17
 18from circuits import handler, BaseComponent, Event
 19
 20from circuits.web.utils import parse_qs
 21from circuits.web.events import response
 22from circuits.web.errors import httperror
 23from circuits.web.processors import process
 24from circuits.web.controllers import BaseController
 25
 26
 27def resolve_path(paths, parts):
 28
 29    def rebuild_path(url_parts):
 30        return '/%s' % '/'.join(url_parts)
 31
 32    left_over = []
 33
 34    while parts:
 35        if rebuild_path(parts) in paths:
 36            yield rebuild_path(parts), left_over
 37        left_over.insert(0, parts.pop())
 38
 39    if '/' in paths:
 40        yield '/', left_over
 41
 42
 43def resolve_methods(parts):
 44    if parts:
 45        method = parts[0]
 46        vpath = parts[1:]
 47        yield method, vpath
 48
 49    yield 'index', parts
 50
 51
 52def find_handlers(req, paths):
 53    def get_handlers(path, method):
 54        component = paths[path]
 55        return component._handlers.get(method, None)
 56
 57    def accepts_vpath(handlers, vpath):
 58        args_no = len(vpath)
 59        return all(
 60            len(h.args) == args_no or h.varargs or (
 61                h.defaults is not None and args_no <= len(h.defaults)
 62            )
 63            for h in handlers
 64        )
 65
 66    # Split /hello/world to ['hello', 'world']
 67    starting_parts = [x for x in req.path.strip("/").split("/") if x]
 68
 69    for path, parts in resolve_path(paths, starting_parts):
 70        handlers = get_handlers(path, req.method)
 71        if handlers:
 72            return handlers, req.method, path, parts
 73
 74        for method, vpath in resolve_methods(parts):
 75            handlers = get_handlers(path, method)
 76            if handlers and (not vpath or accepts_vpath(handlers, vpath)):
 77                req.index = (method == 'index')
 78                return handlers, method, path, vpath
 79            else:
 80                method, vpath = "index", [method] + vpath
 81                handlers = get_handlers(path, method)
 82                if handlers and (not vpath or accepts_vpath(handlers, vpath)):
 83                    req.index = True
 84                    return handlers, method, path, vpath
 85
 86    return [], None, None, None
 87
 88
 89class Dispatcher(BaseComponent):
 90
 91    channel = "web"
 92
 93    def __init__(self, **kwargs):
 94        super(Dispatcher, self).__init__(**kwargs)
 95
 96        self.paths = dict()
 97
 98    @handler("registered", channel="*")
 99    def _on_registered(self, component, manager):
100        if (isinstance(component, BaseController) and component.channel not
101                in self.paths):
102            self.paths[component.channel] = component
103
104    @handler("unregistered", channel="*")
105    def _on_unregistered(self, component, manager):
106        if (isinstance(component, BaseController) and component.channel in
107                self.paths):
108            del self.paths[component.channel]
109
110    @handler("request", priority=0.1)
111    def _on_request(self, event, req, res, peer_cert=None):
112        if peer_cert:
113            event.peer_cert = peer_cert
114
115        handlers, name, channel, vpath = find_handlers(req, self.paths)
116
117        if name is not None and channel is not None:
118            event.kwargs = parse_qs(req.qs)
119            process(req, event.kwargs)
120
121            if vpath:
122                event.args += tuple(vpath)
123
124            if isinstance(name, text_type):
125                name = str(name)
126
127            return self.fire(
128                Event.create(
129                    name, *event.args, **event.kwargs
130                ),
131                channel
132            )
133
134    @handler("request_value_changed")
135    def _on_request_value_changed(self, value):
136        if value.handled:
137            return
138
139        req, res = value.event.args[:2]
140        if value.result and not value.errors:
141            res.body = value.value
142            self.fire(response(res))
143        elif value.promise:
144            value.event.notify = True
145        else:
146            # Errors are handled by the ``HTTP`` Protocol Component
147            return