diff --git a/docs/templating.rst b/docs/templating.rst index e5cb955a..778c960e 100644 --- a/docs/templating.rst +++ b/docs/templating.rst @@ -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 ------------------ diff --git a/src/flask/sansio/app.py b/src/flask/sansio/app.py index b8a1c4cf..dcccbce3 100644 --- a/src/flask/sansio/app.py +++ b/src/flask/sansio/app.py @@ -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 diff --git a/src/flask/sansio/blueprints.py b/src/flask/sansio/blueprints.py index 37da2b3c..c8819563 100644 --- a/src/flask/sansio/blueprints.py +++ b/src/flask/sansio/blueprints.py @@ -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: