Question

In old school webapp, app.yaml based routing allowed you to check for regular expressions and if matched, process a request by a handler whose name is based on the found pattern. For example \1_handler.py would dispatch to user_handler.py if the matched pattern was 'user'.

Is there a way to do the same thing with webapp2.Route? Can the lazy handler or the method_handler parameters be based on the matched patterns in template?

Was it helpful?

Solution

webapp2 does not allow to route like that. I think the most reasonable solution is to write a custom dispatcher for webapp2.Router.

It is possible to set a custom dispatcher like this:

app = WSGIApplication(...)
app.router.set_dispatcher(custom_dispatcher)

Here is a not tested sketch for dispatcher, code is based on webapp2.Router.default_dispatcher:

from webapp2 import import_string

def custom_dispatcher(router, request, response):
    route, args, kwargs = rv = router.match(request)
    request.route, request.route_args, request.route_kwargs = rv

    handler = route.handler
    if isinstance(handler, basestring):

        handler, args, kwargs = _parse_handler_template(handler, args, kwargs)

        if handler not in self.handlers:
            router.handlers[handler] = handler = import_string(handler)
        else:
            handler = router.handlers[handler]

    return router.adapt(handler)(request, response)

def _parse_handler_template(handler, args, kwargs):
    """replace {key} in `handler` with values from `args` or `kwargs`.
    Replaced values are removed from args/kwargs."""
    args = list(args)
    kwargs = dict(kwargs)
    def sub(match):
        if kwargs:
            return kwargs.pop(match.group().strip('{}'))
        else:
            return args.pop(int(match.group().strip('{}'))
    return re.sub('{.*?}', sub, handler), args, kwargs

This code should allow to register a rules like this:

 app = WSGIApplication([
     (r'module/<module>/<action>/<argument>', 'modules.{module}.action_{action}'),
 ])

This example does not allow to use variables from pattern in method name, for example: module.Class:action_{method}. In Route class this endpoint is split by semicolon and values stored in route.method_name and route.handler.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top