Skip to content

Commit edebd37

Browse files
committed
rewrite docs, clean up typing for template decorators
1 parent daf1510 commit edebd37

File tree

3 files changed

+228
-179
lines changed

3 files changed

+228
-179
lines changed

docs/templating.rst

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -137,35 +137,57 @@ using in this block.
137137

138138
.. _registering-filters:
139139

140-
Registering Filters
141-
-------------------
140+
Registering Filters, Tests, and Globals
141+
---------------------------------------
142142

143-
If you want to register your own filters in Jinja2 you have two ways to do
144-
that. You can either put them by hand into the
145-
:attr:`~flask.Flask.jinja_env` of the application or use the
146-
:meth:`~flask.Flask.template_filter` decorator.
143+
The Flask app and blueprints provide decorators and methods to register your own
144+
filters, tests, and global functions for use in Jinja templates. They all follow
145+
the same pattern, so the following examples only discuss filters.
147146

148-
The following examples work the same and all reverse an object::
147+
Decorate a function with :meth:`~.Flask.template_filter` to register it as a
148+
template filter.
149149

150-
@app.template_filter # use the function name as filter name
151-
def reverse_filter(s):
152-
return s[::-1]
150+
.. code-block:: python
151+
152+
@app.template_filter
153+
def reverse(s):
154+
return reversed(s)
155+
156+
.. code-block:: jinja
153157
154-
@app.template_filter('reverse')
158+
{% for item in data | reverse %}
159+
{% endfor %}
160+
161+
By default it will use the name of the function as the name of the filter, but
162+
that can be changed by passing a name to the decorator.
163+
164+
.. code-block:: python
165+
166+
@app.template_filter("reverse")
155167
def reverse_filter(s):
156-
return s[::-1]
168+
return reversed(s)
169+
170+
A filter can be registered separately using :meth:`~.Flask.add_template_filter`.
171+
The name is optional and will use the function name if not given.
172+
173+
.. code-block:: python
157174
158175
def reverse_filter(s):
159-
return s[::-1]
160-
app.jinja_env.filters['reverse'] = reverse_filter
176+
return reversed(s)
161177
162-
In case of the decorator the argument is optional if you want to use the
163-
function name as name of the filter. Once registered, you can use the filter
164-
in your templates in the same way as Jinja2's builtin filters, for example if
165-
you have a Python list in context called `mylist`::
178+
app.add_template_filter(reverse_filter, "reverse")
166179
167-
{% for x in mylist | reverse %}
168-
{% endfor %}
180+
For template tests, use the :meth:`~.Flask.template_test` decorator or
181+
:meth:`~.Flask.add_template_test` method. For template global functions, use the
182+
:meth:`~.Flask.template_global` decorator or :meth:`~.Flask.add_template_global`
183+
method.
184+
185+
The same methods also exist on :class:`.Blueprint`, prefixed with ``app_`` to
186+
indicate that the registered functions will be avaialble to all templates, not
187+
only when rendering from within the blueprint.
188+
189+
The Jinja environment is also available as :attr:`~.Flask.jinja_env`. It may be
190+
modified directly, as you would when using Jinja outside Flask.
169191

170192

171193
Context Processors

src/flask/sansio/app.py

Lines changed: 83 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -660,37 +660,34 @@ def add_url_rule(
660660
)
661661
self.view_functions[endpoint] = view_func
662662

663+
@t.overload
664+
def template_filter(self, name: T_template_filter) -> T_template_filter: ...
665+
@t.overload
666+
def template_filter(
667+
self, name: str | None = None
668+
) -> t.Callable[[T_template_filter], T_template_filter]: ...
663669
@setupmethod
664670
def template_filter(
665-
self, name: t.Callable[..., t.Any] | str | None = None
666-
) -> t.Callable[[T_template_filter], T_template_filter] | T_template_filter:
667-
"""A decorator that is used to register custom template filter.
668-
You can specify a name for the filter, otherwise the function
669-
name will be used. Example::
671+
self, name: T_template_filter | str | None = None
672+
) -> T_template_filter | t.Callable[[T_template_filter], T_template_filter]:
673+
"""Decorate a function to register it as a custom Jinja filter. The name
674+
is optional. The decorator may be used without parentheses.
670675
671-
@app.template_filter()
672-
def reverse(s):
673-
return s[::-1]
676+
.. code-block:: python
674677
675-
The decorator also can be used without parentheses::
678+
@app.template_filter("reverse")
679+
def reverse_filter(s):
680+
return reversed(s)
676681
677-
@app.template_filter
678-
def reverse(s):
679-
return s[::-1]
682+
The :meth:`add_template_filter` method may be used to register a
683+
function later rather than decorating.
680684
681-
:param name: the optional name of the filter, otherwise the
682-
function name will be used.
685+
:param name: The name to register the filter as. If not given, uses the
686+
function's name.
683687
"""
684-
685688
if callable(name):
686-
# If name is callable, it is the function to register.
687-
# This is a shortcut for the common case of
688-
# @app.template_filter
689-
# def func():
690-
691-
func = name
692-
self.add_template_filter(func)
693-
return func
689+
self.add_template_filter(name)
690+
return name
694691

695692
def decorator(f: T_template_filter) -> T_template_filter:
696693
self.add_template_filter(f, name=name)
@@ -702,56 +699,52 @@ def decorator(f: T_template_filter) -> T_template_filter:
702699
def add_template_filter(
703700
self, f: ft.TemplateFilterCallable, name: str | None = None
704701
) -> None:
705-
"""Register a custom template filter. Works exactly like the
706-
:meth:`template_filter` decorator.
702+
"""Register a function to use as a custom Jinja filter.
703+
704+
The :meth:`template_filter` decorator can be used to register a function
705+
by decorating instead.
707706
708-
:param name: the optional name of the filter, otherwise the
709-
function name will be used.
707+
:param f: The function to register.
708+
:param name: The name to register the filter as. If not given, uses the
709+
function's name.
710710
"""
711711
self.jinja_env.filters[name or f.__name__] = f
712712

713+
@t.overload
714+
def template_test(self, name: T_template_test) -> T_template_test: ...
715+
@t.overload
716+
def template_test(
717+
self, name: str | None = None
718+
) -> t.Callable[[T_template_test], T_template_test]: ...
713719
@setupmethod
714720
def template_test(
715-
self, name: t.Callable[..., t.Any] | str | None = None
716-
) -> t.Callable[[T_template_test], T_template_test] | T_template_filter:
717-
"""A decorator that is used to register custom template test.
718-
You can specify a name for the test, otherwise the function
719-
name will be used. Example::
720-
721-
@app.template_test()
722-
def is_prime(n):
721+
self, name: T_template_test | str | None = None
722+
) -> T_template_test | t.Callable[[T_template_test], T_template_test]:
723+
"""Decorate a function to register it as a custom Jinja test. The name
724+
is optional. The decorator may be used without parentheses.
725+
726+
.. code-block:: python
727+
728+
@app.template_test("prime")
729+
def is_prime_test(n):
723730
if n == 2:
724731
return True
725732
for i in range(2, int(math.ceil(math.sqrt(n))) + 1):
726733
if n % i == 0:
727734
return False
728735
return True
729736
730-
The decorator also can be used without parentheses::
737+
The :meth:`add_template_test` method may be used to register a function
738+
later rather than decorating.
731739
732-
@app.template_test
733-
def is_prime(n):
734-
if n == 2:
735-
return True
736-
for i in range(2, int(math.ceil(math.sqrt(n))) + 1):
737-
if n % i == 0:
738-
return False
740+
:param name: The name to register the filter as. If not given, uses the
741+
function's name.
739742
740743
.. versionadded:: 0.10
741-
742-
:param name: the optional name of the test, otherwise the
743-
function name will be used.
744744
"""
745-
746745
if callable(name):
747-
# If name is callable, it is the function to register.
748-
# This is a shortcut for the common case of
749-
# @app.template_test
750-
# def func():
751-
752-
func = name
753-
self.add_template_test(func)
754-
return func
746+
self.add_template_test(name)
747+
return name # type: ignore[return-value]
755748

756749
def decorator(f: T_template_test) -> T_template_test:
757750
self.add_template_test(f, name=name)
@@ -763,49 +756,49 @@ def decorator(f: T_template_test) -> T_template_test:
763756
def add_template_test(
764757
self, f: ft.TemplateTestCallable, name: str | None = None
765758
) -> None:
766-
"""Register a custom template test. Works exactly like the
767-
:meth:`template_test` decorator.
759+
"""Register a function to use as a custom Jinja test.
768760
769-
.. versionadded:: 0.10
761+
The :meth:`template_test` decorator can be used to register a function
762+
by decorating instead.
763+
764+
:param f: The function to register.
765+
:param name: The name to register the test as. If not given, uses the
766+
function's name.
770767
771-
:param name: the optional name of the test, otherwise the
772-
function name will be used.
768+
.. versionadded:: 0.10
773769
"""
774770
self.jinja_env.tests[name or f.__name__] = f
775771

772+
@t.overload
773+
def template_global(self, name: T_template_global) -> T_template_global: ...
774+
@t.overload
775+
def template_global(
776+
self, name: str | None = None
777+
) -> t.Callable[[T_template_global], T_template_global]: ...
776778
@setupmethod
777779
def template_global(
778-
self, name: t.Callable[..., t.Any] | str | None = None
779-
) -> t.Callable[[T_template_global], T_template_global] | T_template_filter:
780-
"""A decorator that is used to register a custom template global function.
781-
You can specify a name for the global function, otherwise the function
782-
name will be used. Example::
780+
self, name: T_template_global | str | None = None
781+
) -> T_template_global | t.Callable[[T_template_global], T_template_global]:
782+
"""Decorate a function to register it as a custom Jinja global. The name
783+
is optional. The decorator may be used without parentheses.
783784
784-
@app.template_global()
785-
def double(n):
786-
return 2 * n
787-
788-
The decorator also can be used without parentheses::
785+
.. code-block:: python
789786
790787
@app.template_global
791788
def double(n):
792789
return 2 * n
793790
794-
.. versionadded:: 0.10
791+
The :meth:`add_template_global` method may be used to register a
792+
function later rather than decorating.
795793
796-
:param name: the optional name of the global function, otherwise the
797-
function name will be used.
798-
"""
794+
:param name: The name to register the global as. If not given, uses the
795+
function's name.
799796
797+
.. versionadded:: 0.10
798+
"""
800799
if callable(name):
801-
# If name is callable, it is the function to register.
802-
# This is a shortcut for the common case of
803-
# @app.template_global
804-
# def func():
805-
806-
func = name
807-
self.add_template_global(func)
808-
return func
800+
self.add_template_global(name)
801+
return name
809802

810803
def decorator(f: T_template_global) -> T_template_global:
811804
self.add_template_global(f, name=name)
@@ -817,13 +810,16 @@ def decorator(f: T_template_global) -> T_template_global:
817810
def add_template_global(
818811
self, f: ft.TemplateGlobalCallable, name: str | None = None
819812
) -> None:
820-
"""Register a custom template global function. Works exactly like the
821-
:meth:`template_global` decorator.
813+
"""Register a function to use as a custom Jinja global.
822814
823-
.. versionadded:: 0.10
815+
The :meth:`template_global` decorator can be used to register a function
816+
by decorating instead.
824817
825-
:param name: the optional name of the global function, otherwise the
826-
function name will be used.
818+
:param f: The function to register.
819+
:param name: The name to register the global as. If not given, uses the
820+
function's name.
821+
822+
.. versionadded:: 0.10
827823
"""
828824
self.jinja_env.globals[name or f.__name__] = f
829825

0 commit comments

Comments
 (0)