Example #1
0
 def __init__(self, routes=()):
     self.route_map = {}
     self.routes = Routes(routes)
     self.build_route_map()
Example #2
0
class App(object):
    """WSGI application class.

    Instance of this class used as entry point for WSGI requests. Using
    provided routes list it can determine which handler should be called.

    Args:
        routes (iterable): list of :class:`Route`.
    """

    __slots__ = 'routes', 'route_map'

    def __init__(self, routes=()):
        self.route_map = {}
        self.routes = Routes(routes)
        self.build_route_map()

    def __call__(self, environ, start_response):
        try:
            request = self.make_request_object(environ)
            handler = self.get_handler(request.path)
            response = handler(self, request)
        except http.Error as error:
            response = error
        start_response(
            response.status,
            list(response.headers.items(stringify=True))
        )
        return response

    @staticmethod
    def make_request_object(environ):
        return Request(environ)  # TODO make request_type as instance attribute

    def build_route_map(self, routes=None, parents=()):
        routes = self.routes if routes is None else routes
        for route in routes:
            self.register_route(route, parents=parents)

    def register_route(self, route, parents=()):
        parents = parents + (route, )
        if route.name:
            self.route_map[route.name] = parents
        self.build_route_map(route.routes, parents=parents)

    def route(self, path, **route_params):
        return self.routes.route(path, **route_params)

    def make_path(self, *route_name, **params):
        assert len(route_name) == 1
        return ''.join(
            route.restore_path(**params)
            for route in self.route_map[route_name[0]]
        )

    def get_handler(self, path, routes=None, params=None):
        """Return handler according to the given path.

        Note:
            If you wish for example automatically redirect all requests
            without trailing slash in URL to URL with persisting one you may
            override this method by raising `http.Error` with 301 status and
            necessary 'Location' header when needed.
        """
        routes = routes or self.routes
        params = params or {}
        for route in routes:
            match = route.match(path)
            if not match:
                continue
            rest_path, route_params = match
            if not rest_path:
                if route.handler:
                    params.update(route_params)
                    return functools.partial(route.handler.start, **params)
            else:
                try:
                    return self.get_handler(
                        rest_path,
                        routes=route.routes,
                        params=dict(params, **route_params),
                    )
                except http.Error:
                    pass  # wrong way raises "404 Not Found" at the end
        raise http.Error('404 Not Found')  # matching route not found