/circuits/web/dispatchers/dispatcher.py
https://bitbucket.org/prologic/circuits/ · Python · 147 lines · 101 code · 36 blank · 10 comment · 36 complexity · 59075dd5fff9aba324585ca9d5f337d1 MD5 · raw file
- # Module: dispatcher
- # Date: 13th September 2007
- # Author: James Mills, prologic at shortcircuit dot net dot au
- """Dispatcher
- This module implements a basic URL to Channel dispatcher.
- This is the default dispatcher used by circuits.web
- """
- try:
- from urllib import quote, unquote
- except ImportError:
- from urllib.parse import quote, unquote # NOQA
- from circuits.six import text_type
- from circuits import handler, BaseComponent, Event
- from circuits.web.utils import parse_qs
- from circuits.web.events import response
- from circuits.web.errors import httperror
- from circuits.web.processors import process
- from circuits.web.controllers import BaseController
- def resolve_path(paths, parts):
- def rebuild_path(url_parts):
- return '/%s' % '/'.join(url_parts)
- left_over = []
- while parts:
- if rebuild_path(parts) in paths:
- yield rebuild_path(parts), left_over
- left_over.insert(0, parts.pop())
- if '/' in paths:
- yield '/', left_over
- def resolve_methods(parts):
- if parts:
- method = parts[0]
- vpath = parts[1:]
- yield method, vpath
- yield 'index', parts
- def find_handlers(req, paths):
- def get_handlers(path, method):
- component = paths[path]
- return component._handlers.get(method, None)
- def accepts_vpath(handlers, vpath):
- args_no = len(vpath)
- return all(
- len(h.args) == args_no or h.varargs or (
- h.defaults is not None and args_no <= len(h.defaults)
- )
- for h in handlers
- )
- # Split /hello/world to ['hello', 'world']
- starting_parts = [x for x in req.path.strip("/").split("/") if x]
- for path, parts in resolve_path(paths, starting_parts):
- handlers = get_handlers(path, req.method)
- if handlers:
- return handlers, req.method, path, parts
- for method, vpath in resolve_methods(parts):
- handlers = get_handlers(path, method)
- if handlers and (not vpath or accepts_vpath(handlers, vpath)):
- req.index = (method == 'index')
- return handlers, method, path, vpath
- else:
- method, vpath = "index", [method] + vpath
- handlers = get_handlers(path, method)
- if handlers and (not vpath or accepts_vpath(handlers, vpath)):
- req.index = True
- return handlers, method, path, vpath
- return [], None, None, None
- class Dispatcher(BaseComponent):
- channel = "web"
- def __init__(self, **kwargs):
- super(Dispatcher, self).__init__(**kwargs)
- self.paths = dict()
- @handler("registered", channel="*")
- def _on_registered(self, component, manager):
- if (isinstance(component, BaseController) and component.channel not
- in self.paths):
- self.paths[component.channel] = component
- @handler("unregistered", channel="*")
- def _on_unregistered(self, component, manager):
- if (isinstance(component, BaseController) and component.channel in
- self.paths):
- del self.paths[component.channel]
- @handler("request", priority=0.1)
- def _on_request(self, event, req, res, peer_cert=None):
- if peer_cert:
- event.peer_cert = peer_cert
- handlers, name, channel, vpath = find_handlers(req, self.paths)
- if name is not None and channel is not None:
- event.kwargs = parse_qs(req.qs)
- process(req, event.kwargs)
- if vpath:
- event.args += tuple(vpath)
- if isinstance(name, text_type):
- name = str(name)
- return self.fire(
- Event.create(
- name, *event.args, **event.kwargs
- ),
- channel
- )
- @handler("request_value_changed")
- def _on_request_value_changed(self, value):
- if value.handled:
- return
- req, res = value.event.args[:2]
- if value.result and not value.errors:
- res.body = value.value
- self.fire(response(res))
- elif value.promise:
- value.event.notify = True
- else:
- # Errors are handled by the ``HTTP`` Protocol Component
- return