Пример #1
0
 def init_templates(self,
                    template_dir: str = None,
                    packages: typing.Sequence[str] = None):
     if not template_dir and not packages:
         self.templates = None
     else:
         template_globals = {'reverse_url': self.reverse_url}
         self.templates = Templates(template_dir, packages,
                                    template_globals)
Пример #2
0
class App():
    interface = 'wsgi'

    def __init__(self,
                 routes,
                 template_dir=None,
                 static_dir=None,
                 packages=None,
                 schema_url='/schema/',
                 docs_url='/docs/',
                 static_url='/static/',
                 components=None,
                 event_hooks=None):

        packages = tuple() if packages is None else tuple(packages)

        if docs_url is not None:
            packages += ('apistar', )

        if static_dir is None and not packages:
            static_url = None

        # Guard against some easy misconfiguration.
        if components:
            msg = 'components must be a list of instances of Component.'
            assert all([
                isinstance(component, Component) for component in components
            ]), msg
        if event_hooks:
            msg = 'event_hooks must be a list.'
            assert isinstance(event_hooks, (list, tuple)), msg

        routes = routes + self.include_extra_routes(schema_url, docs_url,
                                                    static_url)
        self.init_document(routes)
        self.init_router(routes)
        self.init_templates(template_dir, packages)
        self.init_staticfiles(static_url, static_dir, packages)
        self.init_injector(components)
        self.debug = False
        self.event_hooks = event_hooks

        # Ensure event hooks can all be instantiated.
        self.get_event_hooks()

    def include_extra_routes(self,
                             schema_url=None,
                             docs_url=None,
                             static_url=None):
        extra_routes = []

        from apistar.server.handlers import serve_documentation, serve_schema, serve_static_wsgi

        if schema_url:
            extra_routes += [
                Route(schema_url,
                      method='GET',
                      handler=serve_schema,
                      documented=False)
            ]
        if docs_url:
            extra_routes += [
                Route(docs_url,
                      method='GET',
                      handler=serve_documentation,
                      documented=False)
            ]
        if static_url:
            static_url = static_url.rstrip('/') + '/{+filename}'
            extra_routes += [
                Route(static_url,
                      method='GET',
                      handler=serve_static_wsgi,
                      name='static',
                      documented=False,
                      standalone=True)
            ]
        return extra_routes

    def init_document(self, routes):
        self.document = generate_document(routes)

    def init_router(self, routes):
        self.router = Router(routes)

    def init_templates(self,
                       template_dir: str = None,
                       packages: typing.Sequence[str] = None):
        if not template_dir and not packages:
            self.templates = None
        else:
            template_globals = {'reverse_url': self.reverse_url}
            self.templates = Templates(template_dir, packages,
                                       template_globals)

    def init_staticfiles(self,
                         static_url: str,
                         static_dir: str = None,
                         packages: typing.Sequence[str] = None):
        if not static_dir and not packages:
            self.statics = None
        else:
            self.statics = StaticFiles(static_url, static_dir, packages)

    def init_injector(self, components=None):
        components = components if components else []
        components = list(WSGI_COMPONENTS + VALIDATION_COMPONENTS) + components
        initial_components = {
            'environ': WSGIEnviron,
            'start_response': WSGIStartResponse,
            'exc': Exception,
            'app': App,
            'path_params': PathParams,
            'route': Route,
            'response': Response
        }
        self.injector = Injector(components, initial_components)

    def get_event_hooks(self):
        event_hooks = []
        for hook in self.event_hooks or []:
            if isinstance(hook, type):
                # New style usage, instantiate hooks on requests.
                event_hooks.append(hook())
            else:
                # Old style usage, to be deprecated on the next version bump.
                event_hooks.append(hook)

        on_request = [
            hook.on_request for hook in event_hooks
            if hasattr(hook, 'on_request')
        ]

        on_response = [
            hook.on_response for hook in reversed(event_hooks)
            if hasattr(hook, 'on_response')
        ]

        on_error = [
            hook.on_error for hook in reversed(event_hooks)
            if hasattr(hook, 'on_error')
        ]

        return on_request, on_response, on_error

    def reverse_url(self, name: str, **params):
        return self.router.reverse_url(name, **params)

    def render_template(self, path: str, **context):
        return self.templates.render_template(path, **context)

    def serve(self, host, port, debug=False, **options):
        self.debug = debug
        if 'use_debugger' not in options:
            options['use_debugger'] = debug
        if 'use_reloader' not in options:
            options['use_reloader'] = debug
        werkzeug.run_simple(host, port, self, **options)

    def render_response(self, return_value: ReturnValue) -> Response:
        if isinstance(return_value, Response):
            return return_value
        elif isinstance(return_value, str):
            return HTMLResponse(return_value)
        return JSONResponse(return_value)

    def exception_handler(self, exc: Exception) -> Response:
        if isinstance(exc, exceptions.HTTPException):
            return JSONResponse(exc.detail, exc.status_code, exc.get_headers())
        raise

    def error_handler(self) -> Response:
        return JSONResponse('Server error', 500, exc_info=sys.exc_info())

    def finalize_wsgi(self, response: Response,
                      start_response: WSGIStartResponse):
        if self.debug and response.exc_info is not None:
            exc_info = response.exc_info
            raise exc_info[0].with_traceback(exc_info[1], exc_info[2])

        start_response(RESPONSE_STATUS_TEXT[response.status_code],
                       list(response.headers), response.exc_info)
        return [response.content]

    def __call__(self, environ, start_response):
        state = {
            'environ': environ,
            'start_response': start_response,
            'exc': None,
            'app': self,
            'path_params': None,
            'route': None,
            'response': None,
        }
        method = environ['REQUEST_METHOD'].upper()
        path = environ['PATH_INFO']

        if self.event_hooks is None:
            on_request, on_response, on_error = [], [], []
        else:
            on_request, on_response, on_error = self.get_event_hooks()

        try:
            route, path_params = self.router.lookup(path, method)
            state['route'] = route
            state['path_params'] = path_params
            if route.standalone:
                funcs = [route.handler]
            else:
                funcs = (on_request + [route.handler, self.render_response] +
                         on_response + [self.finalize_wsgi])
            return self.injector.run(funcs, state)
        except Exception as exc:
            try:
                state['exc'] = exc
                funcs = ([self.exception_handler] + on_response +
                         [self.finalize_wsgi])
                return self.injector.run(funcs, state)
            except Exception as inner_exc:
                try:
                    state['exc'] = inner_exc
                    self.injector.run(on_error, state)
                finally:
                    funcs = [self.error_handler, self.finalize_wsgi]
                    return self.injector.run(funcs, state)
Пример #3
0
 def init_templates(self, template_dir: str=None):
     if not template_dir:
         self.templates = None
     else:
         template_globals = {'reverse_url': self.reverse_url}
         self.templates = Templates(template_dir, template_globals)