from __future__ import annotations
import typing as t
from jinja2 import BaseLoader
from jinja2 import Environment as BaseEnvironment
from jinja2 import Template
from jinja2 import TemplateNotFound
from . globals import _cv_app
from . globals import _cv_request
from . globals import current_app
from . globals import request
from . helpers import stream_with_context
from . signals import before_render_template
from . signals import template_rendered
if t . TYPE_CHECKING : # pragma: no cover
from . app import Flask
from . sansio . app import App
from . sansio . scaffold import Scaffold
def _default_template_ctx_processor ( ) - > dict [ str , t . Any ] :
""" Default template context processor. Injects `request`,
` session ` and ` g ` .
"""
appctx = _cv_app . get ( None )
reqctx = _cv_request . get ( None )
rv : dict [ str , t . Any ] = { }
if appctx is not None :
rv [ " g " ] = appctx . g
if reqctx is not None :
rv [ " request " ] = reqctx . request
rv [ " session " ] = reqctx . session
return rv
class Environment ( BaseEnvironment ) :
""" Works like a regular Jinja2 environment but has some additional
knowledge of how Flask ' s blueprint works so that it can prepend the
name of the blueprint to referenced templates if necessary .
"""
def __init__ ( self , app : App , * * options : t . Any ) - > None :
if " loader " not in options :
options [ " loader " ] = app . create_global_jinja_loader ( )
BaseEnvironment . __init__ ( self , * * options )
self . app = app
class DispatchingJinjaLoader ( BaseLoader ) :
""" A loader that looks for templates in the application and all
the blueprint folders .
"""
def __init__ ( self , app : App ) - > None :
self . app = app
def get_source (
self , environment : BaseEnvironment , template : str
) - > tuple [ str , str | None , t . Callable [ [ ] , bool ] | None ] :
if self . app . config [ " EXPLAIN_TEMPLATE_LOADING " ] :
return self . _get_source_explained ( environment , template )
return self . _get_source_fast ( environment , template )
def _get_source_explained (
self , environment : BaseEnvironment , template : str
) - > tuple [ str , str | None , t . Callable [ [ ] , bool ] | None ] :
attempts = [ ]
rv : tuple [ str , str | None , t . Callable [ [ ] , bool ] | None ] | None
trv : None | ( tuple [ str , str | None , t . Callable [ [ ] , bool ] | None ] ) = None
for srcobj , loader in self . _iter_loaders ( template ) :
try :
rv = loader . get_source ( environment , template )
if trv is None :
trv = rv
except TemplateNotFound :
rv = None
attempts . append ( ( loader , srcobj , rv ) )
from . debughelpers import explain_template_loading_attempts
explain_template_loading_attempts ( self . app , template , attempts )
if trv is not None :
return trv
raise TemplateNotFound ( template )
def _get_source_fast (
self , environment : BaseEnvironment , template : str
) - > tuple [ str , str | None , t . Callable [ [ ] , bool ] | None ] :
for _srcobj , loader in self . _iter_loaders ( template ) :
try :
return loader . get_source ( environment , template )
except TemplateNotFound :
continue
raise TemplateNotFound ( template )
def _iter_loaders ( self , template : str ) - > t . Iterator [ tuple [ Scaffold , BaseLoader ] ] :
loader = self . app . jinja_loader
if loader is not None :
yield self . app , loader
for blueprint in self . app . iter_blueprints ( ) :
loader = blueprint . jinja_loader
if loader is not None :
yield blueprint , loader
def list_templates ( self ) - > list [ str ] :
result = set ( )
loader = self . app . jinja_loader
if loader is not None :
result . update ( loader . list_templates ( ) )
for blueprint in self . app . iter_blueprints ( ) :
loader = blueprint . jinja_loader
if loader is not None :
for template in loader . list_templates ( ) :
result . add ( template )
return list ( result )
def _render ( app : Flask , template : Template , context : dict [ str , t . Any ] ) - > str :
app . update_template_context ( context )
before_render_template . send (
app , _async_wrapper = app . ensure_sync , template = template , context = context
)
rv = template . render ( context )
template_rendered . send (
app , _async_wrapper = app . ensure_sync , template = template , context = context
)
return rv
def render_template (
template_name_or_list : str | Template | list [ str | Template ] ,
* * context : t . Any ,
) - > str :
""" Render a template by name with the given context.
: param template_name_or_list : The name of the template to render . If
a list is given , the first name to exist will be rendered .
: param context : The variables to make available in the template .
"""
app = current_app . _get_current_object ( ) # type: ignore[attr-defined]
template = app . jinja_env . get_or_select_template ( template_name_or_list )
return _render ( app , template , context )
def render_template_string ( source : str , * * context : t . Any ) - > str :
""" Render a template from the given source string with the given
context .
: param source : The source code of the template to render .
: param context : The variables to make available in the template .
"""
app = current_app . _get_current_object ( ) # type: ignore[attr-defined]
template = app . jinja_env . from_string ( source )
return _render ( app , template , context )
def _stream (
app : Flask , template : Template , context : dict [ str , t . Any ]
) - > t . Iterator [ str ] :
app . update_template_context ( context )
before_render_template . send (
app , _async_wrapper = app . ensure_sync , template = template , context = context
)
def generate ( ) - > t . Iterator [ str ] :
yield from template . generate ( context )
template_rendered . send (
app , _async_wrapper = app . ensure_sync , template = template , context = context
)
rv = generate ( )
# If a request context is active, keep it while generating.
if request :
rv = stream_with_context ( rv )
return rv
def stream_template (
template_name_or_list : str | Template | list [ str | Template ] ,
* * context : t . Any ,
) - > t . Iterator [ str ] :
""" Render a template by name with the given context as a stream.
This returns an iterator of strings , which can be used as a
streaming response from a view .
: param template_name_or_list : The name of the template to render . If
a list is given , the first name to exist will be rendered .
: param context : The variables to make available in the template .
. . versionadded : : 2.2
"""
app = current_app . _get_current_object ( ) # type: ignore[attr-defined]
template = app . jinja_env . get_or_select_template ( template_name_or_list )
return _stream ( app , template , context )
def stream_template_string ( source : str , * * context : t . Any ) - > t . Iterator [ str ] :
""" Render a template from the given source string with the given
context as a stream . This returns an iterator of strings , which can
be used as a streaming response from a view .
: param source : The source code of the template to render .
: param context : The variables to make available in the template .
. . versionadded : : 2.2
"""
app = current_app . _get_current_object ( ) # type: ignore[attr-defined]
template = app . jinja_env . from_string ( source )
return _stream ( app , template , context )