rewrite docs, clean up typing for template decorators

This commit is contained in:
David Lord 2025-08-19 12:29:47 -07:00
parent daf1510a4b
commit edebd37044
No known key found for this signature in database
GPG key ID: 43368A7AA8CC5926
3 changed files with 229 additions and 180 deletions

View file

@ -137,36 +137,58 @@ using in this block.
.. _registering-filters:
Registering Filters
-------------------
Registering Filters, Tests, and Globals
---------------------------------------
If you want to register your own filters in Jinja2 you have two ways to do
that. You can either put them by hand into the
:attr:`~flask.Flask.jinja_env` of the application or use the
:meth:`~flask.Flask.template_filter` decorator.
The Flask app and blueprints provide decorators and methods to register your own
filters, tests, and global functions for use in Jinja templates. They all follow
the same pattern, so the following examples only discuss filters.
The following examples work the same and all reverse an object::
Decorate a function with :meth:`~.Flask.template_filter` to register it as a
template filter.
@app.template_filter # use the function name as filter name
def reverse_filter(s):
return s[::-1]
.. code-block:: python
@app.template_filter('reverse')
def reverse_filter(s):
return s[::-1]
@app.template_filter
def reverse(s):
return reversed(s)
def reverse_filter(s):
return s[::-1]
app.jinja_env.filters['reverse'] = reverse_filter
.. code-block:: jinja
In case of the decorator the argument is optional if you want to use the
function name as name of the filter. Once registered, you can use the filter
in your templates in the same way as Jinja2's builtin filters, for example if
you have a Python list in context called `mylist`::
{% for x in mylist | reverse %}
{% for item in data | reverse %}
{% endfor %}
By default it will use the name of the function as the name of the filter, but
that can be changed by passing a name to the decorator.
.. code-block:: python
@app.template_filter("reverse")
def reverse_filter(s):
return reversed(s)
A filter can be registered separately using :meth:`~.Flask.add_template_filter`.
The name is optional and will use the function name if not given.
.. code-block:: python
def reverse_filter(s):
return reversed(s)
app.add_template_filter(reverse_filter, "reverse")
For template tests, use the :meth:`~.Flask.template_test` decorator or
:meth:`~.Flask.add_template_test` method. For template global functions, use the
:meth:`~.Flask.template_global` decorator or :meth:`~.Flask.add_template_global`
method.
The same methods also exist on :class:`.Blueprint`, prefixed with ``app_`` to
indicate that the registered functions will be avaialble to all templates, not
only when rendering from within the blueprint.
The Jinja environment is also available as :attr:`~.Flask.jinja_env`. It may be
modified directly, as you would when using Jinja outside Flask.
Context Processors
------------------

View file

@ -660,37 +660,34 @@ class App(Scaffold):
)
self.view_functions[endpoint] = view_func
@t.overload
def template_filter(self, name: T_template_filter) -> T_template_filter: ...
@t.overload
def template_filter(
self, name: str | None = None
) -> t.Callable[[T_template_filter], T_template_filter]: ...
@setupmethod
def template_filter(
self, name: t.Callable[..., t.Any] | str | None = None
) -> t.Callable[[T_template_filter], T_template_filter] | T_template_filter:
"""A decorator that is used to register custom template filter.
You can specify a name for the filter, otherwise the function
name will be used. Example::
self, name: T_template_filter | str | None = None
) -> T_template_filter | t.Callable[[T_template_filter], T_template_filter]:
"""Decorate a function to register it as a custom Jinja filter. The name
is optional. The decorator may be used without parentheses.
@app.template_filter()
def reverse(s):
return s[::-1]
.. code-block:: python
The decorator also can be used without parentheses::
@app.template_filter("reverse")
def reverse_filter(s):
return reversed(s)
@app.template_filter
def reverse(s):
return s[::-1]
The :meth:`add_template_filter` method may be used to register a
function later rather than decorating.
:param name: the optional name of the filter, otherwise the
function name will be used.
:param name: The name to register the filter as. If not given, uses the
function's name.
"""
if callable(name):
# If name is callable, it is the function to register.
# This is a shortcut for the common case of
# @app.template_filter
# def func():
func = name
self.add_template_filter(func)
return func
self.add_template_filter(name)
return name
def decorator(f: T_template_filter) -> T_template_filter:
self.add_template_filter(f, name=name)
@ -702,24 +699,34 @@ class App(Scaffold):
def add_template_filter(
self, f: ft.TemplateFilterCallable, name: str | None = None
) -> None:
"""Register a custom template filter. Works exactly like the
:meth:`template_filter` decorator.
"""Register a function to use as a custom Jinja filter.
:param name: the optional name of the filter, otherwise the
function name will be used.
The :meth:`template_filter` decorator can be used to register a function
by decorating instead.
:param f: The function to register.
:param name: The name to register the filter as. If not given, uses the
function's name.
"""
self.jinja_env.filters[name or f.__name__] = f
@t.overload
def template_test(self, name: T_template_test) -> T_template_test: ...
@t.overload
def template_test(
self, name: str | None = None
) -> t.Callable[[T_template_test], T_template_test]: ...
@setupmethod
def template_test(
self, name: t.Callable[..., t.Any] | str | None = None
) -> t.Callable[[T_template_test], T_template_test] | T_template_filter:
"""A decorator that is used to register custom template test.
You can specify a name for the test, otherwise the function
name will be used. Example::
self, name: T_template_test | str | None = None
) -> T_template_test | t.Callable[[T_template_test], T_template_test]:
"""Decorate a function to register it as a custom Jinja test. The name
is optional. The decorator may be used without parentheses.
@app.template_test()
def is_prime(n):
.. code-block:: python
@app.template_test("prime")
def is_prime_test(n):
if n == 2:
return True
for i in range(2, int(math.ceil(math.sqrt(n))) + 1):
@ -727,31 +734,17 @@ class App(Scaffold):
return False
return True
The decorator also can be used without parentheses::
The :meth:`add_template_test` method may be used to register a function
later rather than decorating.
@app.template_test
def is_prime(n):
if n == 2:
return True
for i in range(2, int(math.ceil(math.sqrt(n))) + 1):
if n % i == 0:
return False
:param name: The name to register the filter as. If not given, uses the
function's name.
.. versionadded:: 0.10
:param name: the optional name of the test, otherwise the
function name will be used.
"""
if callable(name):
# If name is callable, it is the function to register.
# This is a shortcut for the common case of
# @app.template_test
# def func():
func = name
self.add_template_test(func)
return func
self.add_template_test(name)
return name # type: ignore[return-value]
def decorator(f: T_template_test) -> T_template_test:
self.add_template_test(f, name=name)
@ -763,49 +756,49 @@ class App(Scaffold):
def add_template_test(
self, f: ft.TemplateTestCallable, name: str | None = None
) -> None:
"""Register a custom template test. Works exactly like the
:meth:`template_test` decorator.
"""Register a function to use as a custom Jinja test.
The :meth:`template_test` decorator can be used to register a function
by decorating instead.
:param f: The function to register.
:param name: The name to register the test as. If not given, uses the
function's name.
.. versionadded:: 0.10
:param name: the optional name of the test, otherwise the
function name will be used.
"""
self.jinja_env.tests[name or f.__name__] = f
@t.overload
def template_global(self, name: T_template_global) -> T_template_global: ...
@t.overload
def template_global(
self, name: str | None = None
) -> t.Callable[[T_template_global], T_template_global]: ...
@setupmethod
def template_global(
self, name: t.Callable[..., t.Any] | str | None = None
) -> t.Callable[[T_template_global], T_template_global] | T_template_filter:
"""A decorator that is used to register a custom template global function.
You can specify a name for the global function, otherwise the function
name will be used. Example::
self, name: T_template_global | str | None = None
) -> T_template_global | t.Callable[[T_template_global], T_template_global]:
"""Decorate a function to register it as a custom Jinja global. The name
is optional. The decorator may be used without parentheses.
@app.template_global()
def double(n):
return 2 * n
The decorator also can be used without parentheses::
.. code-block:: python
@app.template_global
def double(n):
return 2 * n
The :meth:`add_template_global` method may be used to register a
function later rather than decorating.
:param name: The name to register the global as. If not given, uses the
function's name.
.. versionadded:: 0.10
:param name: the optional name of the global function, otherwise the
function name will be used.
"""
if callable(name):
# If name is callable, it is the function to register.
# This is a shortcut for the common case of
# @app.template_global
# def func():
func = name
self.add_template_global(func)
return func
self.add_template_global(name)
return name
def decorator(f: T_template_global) -> T_template_global:
self.add_template_global(f, name=name)
@ -817,13 +810,16 @@ class App(Scaffold):
def add_template_global(
self, f: ft.TemplateGlobalCallable, name: str | None = None
) -> None:
"""Register a custom template global function. Works exactly like the
:meth:`template_global` decorator.
"""Register a function to use as a custom Jinja global.
The :meth:`template_global` decorator can be used to register a function
by decorating instead.
:param f: The function to register.
:param name: The name to register the global as. If not given, uses the
function's name.
.. versionadded:: 0.10
:param name: the optional name of the global function, otherwise the
function name will be used.
"""
self.jinja_env.globals[name or f.__name__] = f

View file

@ -440,26 +440,31 @@ class Blueprint(Scaffold):
)
)
@t.overload
def app_template_filter(self, name: T_template_filter) -> T_template_filter: ...
@t.overload
def app_template_filter(
self, name: str | None = None
) -> t.Callable[[T_template_filter], T_template_filter]: ...
@setupmethod
def app_template_filter(
self, name: t.Callable[..., t.Any] | str | None = None
) -> t.Callable[[T_template_filter], T_template_filter] | T_template_filter:
"""Register a template filter, available in any template rendered by the
application. Equivalent to :meth:`.Flask.template_filter`.
self, name: T_template_filter | str | None = None
) -> T_template_filter | t.Callable[[T_template_filter], T_template_filter]:
"""Decorate a function to register it as a custom Jinja filter. The name
is optional. The decorator may be used without parentheses.
:param name: the optional name of the filter, otherwise the
function name will be used.
The :meth:`add_app_template_filter` method may be used to register a
function later rather than decorating.
The filter is available in all templates, not only those under this
blueprint. Equivalent to :meth:`.Flask.template_filter`.
:param name: The name to register the filter as. If not given, uses the
function's name.
"""
if callable(name):
# If name is callable, it is the function to register.
# This is a shortcut for the common case of
# @bp.add_template_filter
# def func():
func = name
self.add_app_template_filter(func)
return func
self.add_app_template_filter(name)
return name
def decorator(f: T_template_filter) -> T_template_filter:
self.add_app_template_filter(f, name=name)
@ -471,41 +476,51 @@ class Blueprint(Scaffold):
def add_app_template_filter(
self, f: ft.TemplateFilterCallable, name: str | None = None
) -> None:
"""Register a template filter, available in any template rendered by the
application. Works like the :meth:`app_template_filter` decorator. Equivalent to
:meth:`.Flask.add_template_filter`.
"""Register a function to use as a custom Jinja filter.
:param name: the optional name of the filter, otherwise the
function name will be used.
The :meth:`app_template_filter` decorator can be used to register a
function by decorating instead.
The filter is available in all templates, not only those under this
blueprint. Equivalent to :meth:`.Flask.add_template_filter`.
:param f: The function to register.
:param name: The name to register the filter as. If not given, uses the
function's name.
"""
def register_template(state: BlueprintSetupState) -> None:
state.app.jinja_env.filters[name or f.__name__] = f
def register_template_filter(state: BlueprintSetupState) -> None:
state.app.add_template_filter(f, name=name)
self.record_once(register_template)
self.record_once(register_template_filter)
@t.overload
def app_template_test(self, name: T_template_test) -> T_template_test: ...
@t.overload
def app_template_test(
self, name: str | None = None
) -> t.Callable[[T_template_test], T_template_test]: ...
@setupmethod
def app_template_test(
self, name: t.Callable[..., t.Any] | str | None = None
) -> t.Callable[[T_template_test], T_template_test] | T_template_filter:
"""Register a template test, available in any template rendered by the
application. Equivalent to :meth:`.Flask.template_test`.
self, name: T_template_test | str | None = None
) -> T_template_test | t.Callable[[T_template_test], T_template_test]:
"""Decorate a function to register it as a custom Jinja test. The name
is optional. The decorator may be used without parentheses.
The :meth:`add_app_template_test` method may be used to register a
function later rather than decorating.
The test is available in all templates, not only those under this
blueprint. Equivalent to :meth:`.Flask.template_test`.
:param name: The name to register the filter as. If not given, uses the
function's name.
.. versionadded:: 0.10
:param name: the optional name of the test, otherwise the
function name will be used.
"""
if callable(name):
# If name is callable, it is the function to register.
# This is a shortcut for the common case of
# @bp.add_template_test
# def func():
func = name
self.add_app_template_test(func)
return func
self.add_app_template_test(name)
return name # type: ignore[return-value]
def decorator(f: T_template_test) -> T_template_test:
self.add_app_template_test(f, name=name)
@ -517,42 +532,53 @@ class Blueprint(Scaffold):
def add_app_template_test(
self, f: ft.TemplateTestCallable, name: str | None = None
) -> None:
"""Register a template test, available in any template rendered by the
application. Works like the :meth:`app_template_test` decorator. Equivalent to
:meth:`.Flask.add_template_test`.
"""Register a function to use as a custom Jinja test.
The :meth:`app_template_test` decorator can be used to register a
function by decorating instead.
The test is available in all templates, not only those under this
blueprint. Equivalent to :meth:`.Flask.add_template_test`.
:param f: The function to register.
:param name: The name to register the test as. If not given, uses the
function's name.
.. versionadded:: 0.10
:param name: the optional name of the test, otherwise the
function name will be used.
"""
def register_template(state: BlueprintSetupState) -> None:
state.app.jinja_env.tests[name or f.__name__] = f
def register_template_test(state: BlueprintSetupState) -> None:
state.app.add_template_test(f, name=name)
self.record_once(register_template)
self.record_once(register_template_test)
@t.overload
def app_template_global(self, name: T_template_global) -> T_template_global: ...
@t.overload
def app_template_global(
self, name: str | None = None
) -> t.Callable[[T_template_global], T_template_global]: ...
@setupmethod
def app_template_global(
self, name: t.Callable[..., t.Any] | str | None = None
) -> t.Callable[[T_template_global], T_template_global] | T_template_filter:
"""Register a template global, available in any template rendered by the
application. Equivalent to :meth:`.Flask.template_global`.
self, name: T_template_global | str | None = None
) -> T_template_global | t.Callable[[T_template_global], T_template_global]:
"""Decorate a function to register it as a custom Jinja global. The name
is optional. The decorator may be used without parentheses.
The :meth:`add_app_template_global` method may be used to register a
function later rather than decorating.
The global is available in all templates, not only those under this
blueprint. Equivalent to :meth:`.Flask.template_global`.
:param name: The name to register the global as. If not given, uses the
function's name.
.. versionadded:: 0.10
:param name: the optional name of the global, otherwise the
function name will be used.
"""
if callable(name):
# If name is callable, it is the function to register.
# This is a shortcut for the common case of
# @bp.add_template_global
# def func():
func = name
self.add_app_template_global(func)
return func
self.add_app_template_global(name)
return name
def decorator(f: T_template_global) -> T_template_global:
self.add_app_template_global(f, name=name)
@ -564,20 +590,25 @@ class Blueprint(Scaffold):
def add_app_template_global(
self, f: ft.TemplateGlobalCallable, name: str | None = None
) -> None:
"""Register a template global, available in any template rendered by the
application. Works like the :meth:`app_template_global` decorator. Equivalent to
:meth:`.Flask.add_template_global`.
"""Register a function to use as a custom Jinja global.
The :meth:`app_template_global` decorator can be used to register a function
by decorating instead.
The global is available in all templates, not only those under this
blueprint. Equivalent to :meth:`.Flask.add_template_global`.
:param f: The function to register.
:param name: The name to register the global as. If not given, uses the
function's name.
.. versionadded:: 0.10
:param name: the optional name of the global, otherwise the
function name will be used.
"""
def register_template(state: BlueprintSetupState) -> None:
state.app.jinja_env.globals[name or f.__name__] = f
def register_template_global(state: BlueprintSetupState) -> None:
state.app.add_template_global(f, name=name)
self.record_once(register_template)
self.record_once(register_template_global)
@setupmethod
def before_app_request(self, f: T_before_request) -> T_before_request: