async def awaitable(receive: Receive, send: Send) -> None: app = scope["app"] route, route_scope = app.router.get_route_from_scope(scope) state = { "scope": scope, "receive": receive, "send": send, "exc": None, "app": app, "path_params": route_scope["path_params"], "route": route, "request": http.Request(scope, receive), } injected_func = await app.injector.inject(endpoint, state) if asyncio.iscoroutinefunction(endpoint): response = await injected_func() else: response = await run_in_threadpool(injected_func) # Wrap response data with a proper response class if isinstance(response, (dict, list)): response = APIResponse(content=response, schema=get_output_schema(endpoint)) elif isinstance(response, str): response = APIResponse(content=response) elif response is None: response = APIResponse(content="") await response(receive, send)
def page_number(func): """ Decorator for adding pagination behavior to a view. That decorator produces a view based on page numbering and it adds three query parameters to control the pagination: page, page_size and count. Page has a default value of first page, page_size default value is defined in :class:`PageNumberResponse` and count defines if the response will define the total number of elements. The output schema is also modified by :class:`PageNumberSchema`, creating a new schema based on it but using the old output schema as the content of its data field. :param func: View to be decorated. :return: Decorated view. """ assert forge is not None, "`python-forge` must be installed to use Paginator." resource_schema = get_output_schema(func) data_schema = marshmallow.fields.Nested(resource_schema, many=True) if resource_schema else marshmallow.fields.Raw() schema = type( "PageNumberPaginated" + resource_schema.__class__.__name__, # Add a prefix to avoid collision (PageNumberSchema,), {"data": data_schema}, # Replace generic with resource schema )() forge_revision_list = ( forge.copy(func), forge.insert(forge.arg("page", default=None, type=int), index=-1), forge.insert(forge.arg("page_size", default=None, type=int), index=-1), forge.insert(forge.arg("count", default=True, type=bool), index=-1), forge.delete("kwargs"), forge.returns(schema), ) try: if asyncio.iscoroutinefunction(func): @forge.compose(*forge_revision_list) @functools.wraps(func) async def decorator(*args, page: int = None, page_size: int = None, count: bool = True, **kwargs): return PageNumberResponse( schema=schema, page=page, page_size=page_size, count=count, content=await func(*args, **kwargs) ) else: @forge.compose(*forge_revision_list) @functools.wraps(func) def decorator(*args, page: int = None, page_size: int = None, count: bool = True, **kwargs): return PageNumberResponse( schema=schema, page=page, page_size=page_size, count=count, content=func(*args, **kwargs) ) except ValueError as e: raise TypeError("Paginated views must define **kwargs param") from e return decorator
async def dispatch(self) -> None: request = Request(self.scope, receive=self.receive) app = self.scope["app"] route, route_scope = app.router.get_route_from_scope(self.scope) state = { "scope": self.scope, "receive": self.receive, "send": self.send, "exc": None, "app": app, "path_params": route_scope["path_params"], "route": route, "request": request, } handler_name = "get" if request.method == "HEAD" else request.method.lower() handler = getattr(self, handler_name, self.method_not_allowed) injected_func = await app.injector.inject(handler, state) background_task = next( (v for k, v in state.items() if k.startswith('backgroundtasks:') and isinstance(v, BackgroundTask)), None ) if asyncio.iscoroutinefunction(handler): response = await injected_func() else: response = await run_in_threadpool(injected_func) # Wrap response data with a proper response class if isinstance(response, (dict, list)): response = APIResponse(content=response, schema=get_output_schema(handler), background=background_task) elif isinstance(response, str): response = APIResponse(content=response, background=background_task) elif response is None: response = APIResponse(content="", background=background_task) elif not isinstance(response, Response): schema = get_output_schema(handler) if schema is not None: response = APIResponse(content=response, schema=get_output_schema(handler), background=background_task) await response(self.scope, self.receive, self.send)
async def dispatch(self, request: Request, state: typing.Dict, **kwargs) -> Response: handler_name = "get" if request.method == "HEAD" else request.method.lower( ) handler = getattr(self, handler_name, self.method_not_allowed) app = state["app"] injected_func = await app.injector.inject(handler, state) if asyncio.iscoroutinefunction(handler): response = await injected_func() else: response = await run_in_threadpool(injected_func) # Wrap response data with a proper response class if isinstance(response, (dict, list)): response = APIResponse(content=response, schema=get_output_schema(handler)) elif isinstance(response, str): response = APIResponse(content=response) elif response is None: response = APIResponse(content="") return response
async def _app(scope: Scope, receive: Receive, send: Send) -> None: app = scope["app"] route, route_scope = app.router.get_route_from_scope(scope) state = { "scope": scope, "receive": receive, "send": send, "exc": None, "app": app, "path_params": route_scope["path_params"], "route": route, "request": http.Request(scope, receive), } try: injected_func = await app.injector.inject(endpoint, state) background_task = next((v for k, v in state.items() if k.startswith('backgroundtasks:') and isinstance(v, BackgroundTask)), None) if asyncio.iscoroutinefunction(endpoint): response = await injected_func() else: response = await run_in_threadpool(injected_func) # Wrap response data with a proper response class if isinstance(response, (dict, list)): response = APIResponse( content=response, schema=get_output_schema(endpoint), background=background_task, status_code=self.status_code, ) elif isinstance(response, str): response = APIResponse( content=response, background=background_task, status_code=self.status_code, ) elif response is None: response = APIResponse( content="", background=background_task, status_code=self.status_code, ) elif not isinstance(response, Response): schema = get_output_schema(endpoint) if schema is not None: response = APIResponse( content=response, schema=get_output_schema(endpoint), background=background_task, status_code=self.status_code, ) except Exception: logger.exception("Error building response") raise await response(scope, receive, send)