Fix template decorators to work without parentheses
This fixes an issue where template decorators like @app.template_filter don't work when used without parentheses. The fix allows the decorators to be used in both ways: - @app.template_filter - @app.template_filter() The following decorators have been fixed: - template_filter, template_test, template_global in Flask - app_template_filter, app_template_test, app_template_global in Blueprint Fixes #5729
This commit is contained in:
parent
a42c4d54a3
commit
08f2779039
3 changed files with 149 additions and 30 deletions
|
|
@ -662,8 +662,8 @@ class App(Scaffold):
|
|||
|
||||
@setupmethod
|
||||
def template_filter(
|
||||
self, name: str | None = None
|
||||
) -> t.Callable[[T_template_filter], T_template_filter]:
|
||||
self, name_or_function: str | T_template_filter | 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::
|
||||
|
|
@ -672,12 +672,19 @@ class App(Scaffold):
|
|||
def reverse(s):
|
||||
return s[::-1]
|
||||
|
||||
:param name: the optional name of the filter, otherwise the
|
||||
function name will be used.
|
||||
:param name_or_function: the optional name of the filter, otherwise the
|
||||
function name will be used. Or the function itself when
|
||||
used as ``@app.template_filter`` without parentheses.
|
||||
"""
|
||||
# Check if the decorator was called without parentheses
|
||||
# (name_or_function is the actual function to decorate)
|
||||
if callable(name_or_function):
|
||||
self.add_template_filter(name_or_function)
|
||||
return name_or_function
|
||||
|
||||
# Otherwise, the decorator was called with a name or empty parentheses
|
||||
def decorator(f: T_template_filter) -> T_template_filter:
|
||||
self.add_template_filter(f, name=name)
|
||||
self.add_template_filter(f, name=name_or_function)
|
||||
return f
|
||||
|
||||
return decorator
|
||||
|
|
@ -696,8 +703,8 @@ class App(Scaffold):
|
|||
|
||||
@setupmethod
|
||||
def template_test(
|
||||
self, name: str | None = None
|
||||
) -> t.Callable[[T_template_test], T_template_test]:
|
||||
self, name_or_function: str | T_template_test | None = None
|
||||
) -> t.Callable[[T_template_test], T_template_test] | T_template_test:
|
||||
"""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::
|
||||
|
|
@ -713,12 +720,19 @@ class App(Scaffold):
|
|||
|
||||
.. versionadded:: 0.10
|
||||
|
||||
:param name: the optional name of the test, otherwise the
|
||||
function name will be used.
|
||||
:param name_or_function: the optional name of the test, otherwise the
|
||||
function name will be used. Or the function itself when
|
||||
used as ``@app.template_test`` without parentheses.
|
||||
"""
|
||||
# Check if the decorator was called without parentheses
|
||||
# (name_or_function is the actual function to decorate)
|
||||
if callable(name_or_function):
|
||||
self.add_template_test(name_or_function)
|
||||
return name_or_function
|
||||
|
||||
# Otherwise, the decorator was called with a name or empty parentheses
|
||||
def decorator(f: T_template_test) -> T_template_test:
|
||||
self.add_template_test(f, name=name)
|
||||
self.add_template_test(f, name=name_or_function)
|
||||
return f
|
||||
|
||||
return decorator
|
||||
|
|
@ -739,8 +753,8 @@ class App(Scaffold):
|
|||
|
||||
@setupmethod
|
||||
def template_global(
|
||||
self, name: str | None = None
|
||||
) -> t.Callable[[T_template_global], T_template_global]:
|
||||
self, name_or_function: str | T_template_global | None = None
|
||||
) -> t.Callable[[T_template_global], T_template_global] | T_template_global:
|
||||
"""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::
|
||||
|
|
@ -751,12 +765,19 @@ class App(Scaffold):
|
|||
|
||||
.. versionadded:: 0.10
|
||||
|
||||
:param name: the optional name of the global function, otherwise the
|
||||
function name will be used.
|
||||
:param name_or_function: the optional name of the global function, otherwise the
|
||||
function name will be used. Or the function itself when
|
||||
used as ``@app.template_global`` without parentheses.
|
||||
"""
|
||||
# Check if the decorator was called without parentheses
|
||||
# (name_or_function is the actual function to decorate)
|
||||
if callable(name_or_function):
|
||||
self.add_template_global(name_or_function)
|
||||
return name_or_function
|
||||
|
||||
# Otherwise, the decorator was called with a name or empty parentheses
|
||||
def decorator(f: T_template_global) -> T_template_global:
|
||||
self.add_template_global(f, name=name)
|
||||
self.add_template_global(f, name=name_or_function)
|
||||
return f
|
||||
|
||||
return decorator
|
||||
|
|
|
|||
|
|
@ -442,17 +442,24 @@ class Blueprint(Scaffold):
|
|||
|
||||
@setupmethod
|
||||
def app_template_filter(
|
||||
self, name: str | None = None
|
||||
) -> t.Callable[[T_template_filter], T_template_filter]:
|
||||
self, name_or_function: str | T_template_filter | 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`.
|
||||
|
||||
:param name: the optional name of the filter, otherwise the
|
||||
function name will be used.
|
||||
:param name_or_function: the optional name of the filter, otherwise the
|
||||
function name will be used. Or the function itself when
|
||||
used as ``@blueprint.app_template_filter`` without parentheses.
|
||||
"""
|
||||
# Check if the decorator was called without parentheses
|
||||
# (name_or_function is the actual function to decorate)
|
||||
if callable(name_or_function):
|
||||
self.add_app_template_filter(name_or_function)
|
||||
return name_or_function
|
||||
|
||||
# Otherwise, the decorator was called with a name or empty parentheses
|
||||
def decorator(f: T_template_filter) -> T_template_filter:
|
||||
self.add_app_template_filter(f, name=name)
|
||||
self.add_app_template_filter(f, name=name_or_function)
|
||||
return f
|
||||
|
||||
return decorator
|
||||
|
|
@ -476,19 +483,26 @@ class Blueprint(Scaffold):
|
|||
|
||||
@setupmethod
|
||||
def app_template_test(
|
||||
self, name: str | None = None
|
||||
) -> t.Callable[[T_template_test], T_template_test]:
|
||||
self, name_or_function: str | T_template_test | None = None
|
||||
) -> t.Callable[[T_template_test], T_template_test] | T_template_test:
|
||||
"""Register a template test, available in any template rendered by the
|
||||
application. Equivalent to :meth:`.Flask.template_test`.
|
||||
|
||||
.. versionadded:: 0.10
|
||||
|
||||
:param name: the optional name of the test, otherwise the
|
||||
function name will be used.
|
||||
:param name_or_function: the optional name of the test, otherwise the
|
||||
function name will be used. Or the function itself when
|
||||
used as ``@blueprint.app_template_test`` without parentheses.
|
||||
"""
|
||||
# Check if the decorator was called without parentheses
|
||||
# (name_or_function is the actual function to decorate)
|
||||
if callable(name_or_function):
|
||||
self.add_app_template_test(name_or_function)
|
||||
return name_or_function
|
||||
|
||||
# Otherwise, the decorator was called with a name or empty parentheses
|
||||
def decorator(f: T_template_test) -> T_template_test:
|
||||
self.add_app_template_test(f, name=name)
|
||||
self.add_app_template_test(f, name=name_or_function)
|
||||
return f
|
||||
|
||||
return decorator
|
||||
|
|
@ -514,19 +528,26 @@ class Blueprint(Scaffold):
|
|||
|
||||
@setupmethod
|
||||
def app_template_global(
|
||||
self, name: str | None = None
|
||||
) -> t.Callable[[T_template_global], T_template_global]:
|
||||
self, name_or_function: str | T_template_global | None = None
|
||||
) -> t.Callable[[T_template_global], T_template_global] | T_template_global:
|
||||
"""Register a template global, available in any template rendered by the
|
||||
application. Equivalent to :meth:`.Flask.template_global`.
|
||||
|
||||
.. versionadded:: 0.10
|
||||
|
||||
:param name: the optional name of the global, otherwise the
|
||||
function name will be used.
|
||||
:param name_or_function: the optional name of the global, otherwise the
|
||||
function name will be used. Or the function itself when
|
||||
used as ``@blueprint.app_template_global`` without parentheses.
|
||||
"""
|
||||
# Check if the decorator was called without parentheses
|
||||
# (name_or_function is the actual function to decorate)
|
||||
if callable(name_or_function):
|
||||
self.add_app_template_global(name_or_function)
|
||||
return name_or_function
|
||||
|
||||
# Otherwise, the decorator was called with a name or empty parentheses
|
||||
def decorator(f: T_template_global) -> T_template_global:
|
||||
self.add_app_template_global(f, name=name)
|
||||
self.add_app_template_global(f, name=name_or_function)
|
||||
return f
|
||||
|
||||
return decorator
|
||||
|
|
|
|||
77
tests/test_decorator_no_parens.py
Normal file
77
tests/test_decorator_no_parens.py
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
import pytest
|
||||
|
||||
import flask
|
||||
|
||||
|
||||
def test_template_filter_no_parens(app):
|
||||
"""Test that @app.template_filter works without parentheses."""
|
||||
@app.template_filter
|
||||
def double(x):
|
||||
return x * 2
|
||||
|
||||
assert "double" in app.jinja_env.filters
|
||||
assert app.jinja_env.filters["double"] == double
|
||||
assert app.jinja_env.filters["double"](2) == 4
|
||||
|
||||
|
||||
def test_template_test_no_parens(app):
|
||||
"""Test that @app.template_test works without parentheses."""
|
||||
@app.template_test
|
||||
def is_even(x):
|
||||
return x % 2 == 0
|
||||
|
||||
assert "is_even" in app.jinja_env.tests
|
||||
assert app.jinja_env.tests["is_even"] == is_even
|
||||
assert app.jinja_env.tests["is_even"](2) is True
|
||||
assert app.jinja_env.tests["is_even"](3) is False
|
||||
|
||||
|
||||
def test_template_global_no_parens(app):
|
||||
"""Test that @app.template_global works without parentheses."""
|
||||
@app.template_global
|
||||
def get_answer():
|
||||
return 42
|
||||
|
||||
assert "get_answer" in app.jinja_env.globals
|
||||
assert app.jinja_env.globals["get_answer"] == get_answer
|
||||
assert app.jinja_env.globals["get_answer"]() == 42
|
||||
|
||||
|
||||
def test_blueprint_app_template_filter_no_parens(app):
|
||||
"""Test that @blueprint.app_template_filter works without parentheses."""
|
||||
bp = flask.Blueprint("test_bp", __name__)
|
||||
|
||||
@bp.app_template_filter
|
||||
def triple(x):
|
||||
return x * 3
|
||||
|
||||
app.register_blueprint(bp)
|
||||
assert "triple" in app.jinja_env.filters
|
||||
assert app.jinja_env.filters["triple"](3) == 9
|
||||
|
||||
|
||||
def test_blueprint_app_template_test_no_parens(app):
|
||||
"""Test that @blueprint.app_template_test works without parentheses."""
|
||||
bp = flask.Blueprint("test_bp", __name__)
|
||||
|
||||
@bp.app_template_test
|
||||
def is_odd(x):
|
||||
return x % 2 == 1
|
||||
|
||||
app.register_blueprint(bp)
|
||||
assert "is_odd" in app.jinja_env.tests
|
||||
assert app.jinja_env.tests["is_odd"](3) is True
|
||||
assert app.jinja_env.tests["is_odd"](2) is False
|
||||
|
||||
|
||||
def test_blueprint_app_template_global_no_parens(app):
|
||||
"""Test that @blueprint.app_template_global works without parentheses."""
|
||||
bp = flask.Blueprint("test_bp", __name__)
|
||||
|
||||
@bp.app_template_global
|
||||
def get_pi():
|
||||
return 3.14
|
||||
|
||||
app.register_blueprint(bp)
|
||||
assert "get_pi" in app.jinja_env.globals
|
||||
assert app.jinja_env.globals["get_pi"]() == 3.14
|
||||
Loading…
Add table
Add a link
Reference in a new issue