from __future__ import annotations from inspect import isawaitable from typing import TYPE_CHECKING, Any, Optional, Union from sanic import Sanic from sanic.compat import Header from sanic.exceptions import SanicException from sanic.request import Request from sanic.response import HTTPResponse from sanic_ext.exceptions import ExtensionNotFound if TYPE_CHECKING: from jinja2 import Environment class TemplateResponse(HTTPResponse): ... class LazyResponse(TemplateResponse): __slots__ = ( "body", "status", "content_type", "headers", "_cookies", "context", ) def __init__( self, context: dict[str, Any], status: int = 0, headers: Optional[Union[Header, dict[str, str]]] = None, content_type: Optional[str] = None, ): super().__init__( content_type=content_type, status=status, headers=headers ) self.context = context async def render( template_name: str = "", status: int = 200, headers: Optional[dict[str, str]] = None, content_type: str = "text/html; charset=utf-8", app: Optional[Sanic] = None, environment: Optional[Environment] = None, context: Optional[dict[str, Any]] = None, *, template_source: str = "", ) -> TemplateResponse: if app is None: try: app = Sanic.get_app() except SanicException as e: raise SanicException( "Cannot render template beause locating the Sanic application " "was ambiguous. Please return render(..., app=some_app)." ) from e if template_name and template_source: raise SanicException( "You must provide template_name OR template_source, not both." ) if environment is None: try: environment = app.ext.environment except AttributeError: raise ExtensionNotFound( "The Templating extension does not appear to be enabled. " "Perhaps jinja2 is not installed." ) kwargs = context if context else {} kwargs["request"] = Request.get_current() if template_name or template_source: template = ( environment.get_template(template_name) if template_name else environment.from_string(template_source) ) render = ( template.render_async if app.config.TEMPLATING_ENABLE_ASYNC else template.render ) content = render(**kwargs) if isawaitable(content): content = await content # type: ignore return TemplateResponse( # type: ignore content, status=status, headers=headers, content_type=content_type ) else: return LazyResponse( kwargs, status=status, headers=headers, content_type=content_type )