def __init__(self, routes=()): self.route_map = {} self.routes = Routes(routes) self.build_route_map()
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