From 6ff24afee826daf4348b1f04911dc51120b8827b Mon Sep 17 00:00:00 2001 From: Akshay Date: Fri, 16 May 2025 14:49:45 +0530 Subject: [PATCH] Fix template_filter decorator to support both usage styles and update docstring; update CHANGES.rst --- CHANGES.rst | 2 ++ src/flask/sansio/app.py | 39 ++++++++++++++-------------------- tests/test_template_filters.py | 24 +++++++++++++++++++++ 3 files changed, 42 insertions(+), 23 deletions(-) create mode 100644 tests/test_template_filters.py diff --git a/CHANGES.rst b/CHANGES.rst index 37e777dc..cf8b7a6f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,6 +6,8 @@ Unreleased - Drop support for Python 3.9. :pr:`5730` - Remove previously deprecated code: ``__version__``. :pr:`5648` +- Fix template_filter decorator to correctly register filters and support usage with or without parentheses. +- Improve docstring for `template_filter` decorator for better clarity and examples. Version 3.1.1 ------------- diff --git a/src/flask/sansio/app.py b/src/flask/sansio/app.py index 9c85d234..b4bb6639 100644 --- a/src/flask/sansio/app.py +++ b/src/flask/sansio/app.py @@ -664,37 +664,30 @@ class App(Scaffold): def template_filter( self, name: str | None = None ) -> t.Callable[[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:: + """A decorator that is used to register custom template filters. + You can use this with or without parentheses. Example:: - @app.template_filter() - def reverse(s): - return s[::-1] + @app.template_filter + def double(x): + return x * 2 - :param name: the optional name of the filter, otherwise the - function name will be used. + @app.template_filter() + def reverse(s): + return s[::-1] + :param name: the optional name of the filter, otherwise the + function name will be used. """ + if callable(name): func = name - name = func.__name__ # Use function name as default - self.add_template_filter(func, name=name) # Register filter with Flask - - @wraps(func) - def wrapper(*args, **kwargs): - return func(*args, **kwargs) - - return wrapper + name = func.__name__ + self.add_template_filter(func, name=name) + return func # ✅ return original function (not a wrapper) def decorator(func: T_template_filter) -> T_template_filter: - self.add_template_filter(func, name=name) # Register filter with Flask - - @wraps(func) - def wrapper(*args, **kwargs): - return func(*args, **kwargs) - - return wrapper + self.add_template_filter(func, name=name) + return func # ✅ return original function (not a wrapper) return decorator diff --git a/tests/test_template_filters.py b/tests/test_template_filters.py new file mode 100644 index 00000000..7dda15f8 --- /dev/null +++ b/tests/test_template_filters.py @@ -0,0 +1,24 @@ +import pytest +from flask import Flask, render_template_string + +def test_template_filter_without_parentheses(): + app = Flask(__name__) + + @app.template_filter + def double(x): + return x * 2 + + with app.app_context(): + output = render_template_string("{{ 2 | double }}") + assert output == "4" + +def test_template_filter_with_parentheses(): + app = Flask(__name__) + + @app.template_filter() + def triple(x): + return x * 3 + + with app.app_context(): + output = render_template_string("{{ 3 | triple }}") + assert output == "9"