Compare commits

...

11 Commits

Author SHA1 Message Date
d-lcw 4788bca78d
Merge 0abda4d277 into d6009c0aeb 2025-06-10 15:54:40 +01:00
David Lord d6009c0aeb
Merge branch 'stable'
pre-commit / main (push) Waiting to run Details
2025-06-09 21:20:46 -07:00
David Lord 2b42a803a2
cleanup svg
pre-commit / main (push) Waiting to run Details
2025-06-09 21:20:36 -07:00
David Lord 211cce038a
Merge branch 'stable'
pre-commit / main (push) Waiting to run Details
2025-06-09 14:33:44 -07:00
David Lord a7b67c99f9
svg logo (#5757)
pre-commit / main (push) Waiting to run Details
2025-06-09 14:33:26 -07:00
David Lord a758915893
svg logo 2025-06-09 14:31:31 -07:00
kadai0308 0abda4d277 docs: update docs and CHANGES.rst 2025-05-26 17:30:47 +08:00
pre-commit-ci-lite[bot] 45a0ff357f
[pre-commit.ci lite] apply automatic fixes 2025-05-26 09:26:20 +00:00
kadai0308 1f027e498d formatting 2025-05-26 17:23:06 +08:00
kadai0308 6d6c377a1f feat: support call template_test & template_global without parens 2025-05-26 17:20:12 +08:00
kadai0308 0a52b6b78d feat: support call template_filter without parens 2025-05-17 12:44:49 +08:00
15 changed files with 280 additions and 26 deletions

View File

@ -5,6 +5,7 @@ Unreleased
- Drop support for Python 3.9. :pr:`5730` - Drop support for Python 3.9. :pr:`5730`
- Remove previously deprecated code: ``__version__``. :pr:`5648` - Remove previously deprecated code: ``__version__``. :pr:`5648`
- Support for using @app.template_filter, @app.template_test, and @app.template_global decorators without parentheses. :pr:`5736`
Version 3.1.1 Version 3.1.1

View File

@ -1,3 +1,5 @@
<div align="center"><img src="https://raw.githubusercontent.com/pallets/flask/refs/heads/stable/docs/_static/flask-horizontal.svg" alt="" height="150"></div>
# Flask # Flask
Flask is a lightweight [WSGI] web application framework. It is designed Flask is a lightweight [WSGI] web application framework. It is designed

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

1
docs/_static/flask-horizontal.svg vendored Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.7 KiB

1
docs/_static/flask-icon.svg vendored Normal file
View File

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 1000 1000" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><g><path d="M448.955,119.915c-0.579,59.76 13.398,192.715 18.542,238.988l-356.626,159.589c-23.367,-39.985 -40.836,-96.035 -51.551,-140.858l-2.503,1.065c-8.573,3.718 -16.932,2.18 -20.609,-3.664l-0.456,-0.734l-34.175,-62.778c-2.402,-4.381 -2.054,-10.547 1.006,-16.32c3.06,-5.777 8.827,-11.157 14.901,-13.815l409.395,-175.316c6.074,-2.657 12.451,-2.59 16.655,0.176c4.44,2.587 5.304,5.387 5.421,13.667Z" style="fill:#3babc3;fill-rule:nonzero;"/><path d="M477.297,411.517l-343.415,153.677c20.185,38.246 45.103,78.851 75.476,118.011l302.647,-154.214c-6.532,-16.924 -12.929,-35.311 -34.708,-117.474Z" style="fill:#3babc3;fill-rule:nonzero;"/><path d="M243.67,724.046l289.522,-147.543c30.054,59.626 66.144,102.294 104.553,132.415c86.929,68.257 183.958,71.364 241.412,65.146l-1.966,-9.7c-0.612,-3.136 0.28,-5.597 3.584,-6.859l30.59,-11.989c5.211,-2.017 10.611,-0.495 14.279,3.584l31.177,-12.015c4.981,-2.008 10.611,-0.494 14.233,3.564l24.268,37.075c11.806,16.723 -1.924,27.342 -6.368,29.035l-39.431,15.407c1.542,5.232 1.471,13.31 -9.022,17.288l-30.636,11.969c-9.584,3.739 -16.61,-2.411 -17.524,-8.292l-0.998,-5.224c-184.485,74.282 -330.818,70.194 -445.692,26.944c-82.561,-31.081 -149.05,-82.639 -201.981,-140.805Z" style="fill:#3babc3;fill-rule:nonzero;"/></g></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

1
docs/_static/flask-vertical.svg vendored Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -58,8 +58,8 @@ html_sidebars = {
} }
singlehtml_sidebars = {"index": ["project.html", "localtoc.html", "ethicalads.html"]} singlehtml_sidebars = {"index": ["project.html", "localtoc.html", "ethicalads.html"]}
html_static_path = ["_static"] html_static_path = ["_static"]
html_favicon = "_static/shortcut-icon.png" html_favicon = "_static/flask-icon.svg"
html_logo = "_static/flask-vertical.png" html_logo = "_static/flask-vertical.svg"
html_title = f"Flask Documentation ({version})" html_title = f"Flask Documentation ({version})"
html_show_sourcelink = False html_show_sourcelink = False

View File

@ -3,8 +3,9 @@
Welcome to Flask Welcome to Flask
================ ================
.. image:: _static/flask-horizontal.png .. image:: _static/flask-horizontal.svg
:align: center :align: center
:height: 200px
Welcome to Flask's documentation. Flask is a lightweight WSGI web application framework. Welcome to Flask's documentation. Flask is a lightweight WSGI web application framework.
It is designed to make getting started quick and easy, with the ability to scale up to It is designed to make getting started quick and easy, with the ability to scale up to

View File

@ -145,7 +145,11 @@ that. You can either put them by hand into the
:attr:`~flask.Flask.jinja_env` of the application or use the :attr:`~flask.Flask.jinja_env` of the application or use the
:meth:`~flask.Flask.template_filter` decorator. :meth:`~flask.Flask.template_filter` decorator.
The two following examples work the same and both reverse an object:: The following examples work the same and all reverse an object::
@app.template_filter # use the function name as filter name
def reverse_filter(s):
return s[::-1]
@app.template_filter('reverse') @app.template_filter('reverse')
def reverse_filter(s): def reverse_filter(s):

View File

@ -662,20 +662,36 @@ class App(Scaffold):
@setupmethod @setupmethod
def template_filter( def template_filter(
self, name: str | None = None self, name: t.Callable[..., t.Any] | str | None = None
) -> t.Callable[[T_template_filter], T_template_filter]: ) -> t.Callable[[T_template_filter], T_template_filter] | T_template_filter:
"""A decorator that is used to register custom template filter. """A decorator that is used to register custom template filter.
You can specify a name for the filter, otherwise the function You can specify a name for the filter, otherwise the function
name will be used. Example:: name will be used. Example::
@app.template_filter() @app.template_filter()
def reverse(s): def reverse(s):
return s[::-1] return s[::-1]
The decorator also can be used without parentheses::
@app.template_filter
def reverse(s):
return s[::-1]
:param name: the optional name of the filter, otherwise the :param name: the optional name of the filter, otherwise the
function name will be used. 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_filter
# def func():
func = name
self.add_template_filter(func)
return func
def decorator(f: T_template_filter) -> T_template_filter: def decorator(f: T_template_filter) -> T_template_filter:
self.add_template_filter(f, name=name) self.add_template_filter(f, name=name)
return f return f
@ -696,27 +712,47 @@ class App(Scaffold):
@setupmethod @setupmethod
def template_test( def template_test(
self, name: str | None = None self, name: t.Callable[..., t.Any] | str | None = None
) -> t.Callable[[T_template_test], T_template_test]: ) -> t.Callable[[T_template_test], T_template_test] | T_template_filter:
"""A decorator that is used to register custom template test. """A decorator that is used to register custom template test.
You can specify a name for the test, otherwise the function You can specify a name for the test, otherwise the function
name will be used. Example:: name will be used. Example::
@app.template_test() @app.template_test()
def is_prime(n): def is_prime(n):
if n == 2: if n == 2:
return True return True
for i in range(2, int(math.ceil(math.sqrt(n))) + 1): for i in range(2, int(math.ceil(math.sqrt(n))) + 1):
if n % i == 0: if n % i == 0:
return False return False
return True return True
The decorator also can be used without parentheses::
@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
.. versionadded:: 0.10 .. versionadded:: 0.10
:param name: the optional name of the test, otherwise the :param name: the optional name of the test, otherwise the
function name will be used. 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
def decorator(f: T_template_test) -> T_template_test: def decorator(f: T_template_test) -> T_template_test:
self.add_template_test(f, name=name) self.add_template_test(f, name=name)
return f return f
@ -739,8 +775,8 @@ class App(Scaffold):
@setupmethod @setupmethod
def template_global( def template_global(
self, name: str | None = None self, name: t.Callable[..., t.Any] | str | None = None
) -> t.Callable[[T_template_global], T_template_global]: ) -> t.Callable[[T_template_global], T_template_global] | T_template_filter:
"""A decorator that is used to register a custom template global function. """A decorator that is used to register a custom template global function.
You can specify a name for the global function, otherwise the function You can specify a name for the global function, otherwise the function
name will be used. Example:: name will be used. Example::
@ -749,12 +785,28 @@ class App(Scaffold):
def double(n): def double(n):
return 2 * n return 2 * n
The decorator also can be used without parentheses::
@app.template_global
def double(n):
return 2 * n
.. versionadded:: 0.10 .. versionadded:: 0.10
:param name: the optional name of the global function, otherwise the :param name: the optional name of the global function, otherwise the
function name will be used. 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
def decorator(f: T_template_global) -> T_template_global: def decorator(f: T_template_global) -> T_template_global:
self.add_template_global(f, name=name) self.add_template_global(f, name=name)
return f return f

View File

@ -442,8 +442,8 @@ class Blueprint(Scaffold):
@setupmethod @setupmethod
def app_template_filter( def app_template_filter(
self, name: str | None = None self, name: t.Callable[..., t.Any] | str | None = None
) -> t.Callable[[T_template_filter], T_template_filter]: ) -> t.Callable[[T_template_filter], T_template_filter] | T_template_filter:
"""Register a template filter, available in any template rendered by the """Register a template filter, available in any template rendered by the
application. Equivalent to :meth:`.Flask.template_filter`. application. Equivalent to :meth:`.Flask.template_filter`.
@ -451,6 +451,16 @@ class Blueprint(Scaffold):
function name will be used. 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_filter
# def func():
func = name
self.add_app_template_filter(func)
return func
def decorator(f: T_template_filter) -> T_template_filter: 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)
return f return f
@ -476,8 +486,8 @@ class Blueprint(Scaffold):
@setupmethod @setupmethod
def app_template_test( def app_template_test(
self, name: str | None = None self, name: t.Callable[..., t.Any] | str | None = None
) -> t.Callable[[T_template_test], T_template_test]: ) -> t.Callable[[T_template_test], T_template_test] | T_template_filter:
"""Register a template test, available in any template rendered by the """Register a template test, available in any template rendered by the
application. Equivalent to :meth:`.Flask.template_test`. application. Equivalent to :meth:`.Flask.template_test`.
@ -487,6 +497,16 @@ class Blueprint(Scaffold):
function name will be used. 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
def decorator(f: T_template_test) -> T_template_test: 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)
return f return f
@ -514,8 +534,8 @@ class Blueprint(Scaffold):
@setupmethod @setupmethod
def app_template_global( def app_template_global(
self, name: str | None = None self, name: t.Callable[..., t.Any] | str | None = None
) -> t.Callable[[T_template_global], T_template_global]: ) -> t.Callable[[T_template_global], T_template_global] | T_template_filter:
"""Register a template global, available in any template rendered by the """Register a template global, available in any template rendered by the
application. Equivalent to :meth:`.Flask.template_global`. application. Equivalent to :meth:`.Flask.template_global`.
@ -524,6 +544,15 @@ class Blueprint(Scaffold):
:param name: the optional name of the global, otherwise the :param name: the optional name of the global, otherwise the
function name will be used. 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
def decorator(f: T_template_global) -> T_template_global: 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)

View File

@ -366,11 +366,35 @@ def test_template_filter(app):
def my_reverse(s): def my_reverse(s):
return s[::-1] return s[::-1]
@bp.app_template_filter
def my_reverse_2(s):
return s[::-1]
@bp.app_template_filter("my_reverse_custom_name_3")
def my_reverse_3(s):
return s[::-1]
@bp.app_template_filter(name="my_reverse_custom_name_4")
def my_reverse_4(s):
return s[::-1]
app.register_blueprint(bp, url_prefix="/py") app.register_blueprint(bp, url_prefix="/py")
assert "my_reverse" in app.jinja_env.filters.keys() assert "my_reverse" in app.jinja_env.filters.keys()
assert app.jinja_env.filters["my_reverse"] == my_reverse assert app.jinja_env.filters["my_reverse"] == my_reverse
assert app.jinja_env.filters["my_reverse"]("abcd") == "dcba" assert app.jinja_env.filters["my_reverse"]("abcd") == "dcba"
assert "my_reverse_2" in app.jinja_env.filters.keys()
assert app.jinja_env.filters["my_reverse_2"] == my_reverse_2
assert app.jinja_env.filters["my_reverse_2"]("abcd") == "dcba"
assert "my_reverse_custom_name_3" in app.jinja_env.filters.keys()
assert app.jinja_env.filters["my_reverse_custom_name_3"] == my_reverse_3
assert app.jinja_env.filters["my_reverse_custom_name_3"]("abcd") == "dcba"
assert "my_reverse_custom_name_4" in app.jinja_env.filters.keys()
assert app.jinja_env.filters["my_reverse_custom_name_4"] == my_reverse_4
assert app.jinja_env.filters["my_reverse_custom_name_4"]("abcd") == "dcba"
def test_add_template_filter(app): def test_add_template_filter(app):
bp = flask.Blueprint("bp", __name__) bp = flask.Blueprint("bp", __name__)
@ -502,11 +526,35 @@ def test_template_test(app):
def is_boolean(value): def is_boolean(value):
return isinstance(value, bool) return isinstance(value, bool)
@bp.app_template_test
def boolean_2(value):
return isinstance(value, bool)
@bp.app_template_test("my_boolean_custom_name")
def boolean_3(value):
return isinstance(value, bool)
@bp.app_template_test(name="my_boolean_custom_name_2")
def boolean_4(value):
return isinstance(value, bool)
app.register_blueprint(bp, url_prefix="/py") app.register_blueprint(bp, url_prefix="/py")
assert "is_boolean" in app.jinja_env.tests.keys() assert "is_boolean" in app.jinja_env.tests.keys()
assert app.jinja_env.tests["is_boolean"] == is_boolean assert app.jinja_env.tests["is_boolean"] == is_boolean
assert app.jinja_env.tests["is_boolean"](False) assert app.jinja_env.tests["is_boolean"](False)
assert "boolean_2" in app.jinja_env.tests.keys()
assert app.jinja_env.tests["boolean_2"] == boolean_2
assert app.jinja_env.tests["boolean_2"](False)
assert "my_boolean_custom_name" in app.jinja_env.tests.keys()
assert app.jinja_env.tests["my_boolean_custom_name"] == boolean_3
assert app.jinja_env.tests["my_boolean_custom_name"](False)
assert "my_boolean_custom_name_2" in app.jinja_env.tests.keys()
assert app.jinja_env.tests["my_boolean_custom_name_2"] == boolean_4
assert app.jinja_env.tests["my_boolean_custom_name_2"](False)
def test_add_template_test(app): def test_add_template_test(app):
bp = flask.Blueprint("bp", __name__) bp = flask.Blueprint("bp", __name__)
@ -679,6 +727,18 @@ def test_template_global(app):
def get_answer(): def get_answer():
return 42 return 42
@bp.app_template_global
def get_stuff_1():
return "get_stuff_1"
@bp.app_template_global("my_get_stuff_custom_name_2")
def get_stuff_2():
return "get_stuff_2"
@bp.app_template_global(name="my_get_stuff_custom_name_3")
def get_stuff_3():
return "get_stuff_3"
# Make sure the function is not in the jinja_env already # Make sure the function is not in the jinja_env already
assert "get_answer" not in app.jinja_env.globals.keys() assert "get_answer" not in app.jinja_env.globals.keys()
app.register_blueprint(bp) app.register_blueprint(bp)
@ -688,10 +748,31 @@ def test_template_global(app):
assert app.jinja_env.globals["get_answer"] is get_answer assert app.jinja_env.globals["get_answer"] is get_answer
assert app.jinja_env.globals["get_answer"]() == 42 assert app.jinja_env.globals["get_answer"]() == 42
assert "get_stuff_1" in app.jinja_env.globals.keys()
assert app.jinja_env.globals["get_stuff_1"] == get_stuff_1
assert app.jinja_env.globals["get_stuff_1"](), "get_stuff_1"
assert "my_get_stuff_custom_name_2" in app.jinja_env.globals.keys()
assert app.jinja_env.globals["my_get_stuff_custom_name_2"] == get_stuff_2
assert app.jinja_env.globals["my_get_stuff_custom_name_2"](), "get_stuff_2"
assert "my_get_stuff_custom_name_3" in app.jinja_env.globals.keys()
assert app.jinja_env.globals["my_get_stuff_custom_name_3"] == get_stuff_3
assert app.jinja_env.globals["my_get_stuff_custom_name_3"](), "get_stuff_3"
with app.app_context(): with app.app_context():
rv = flask.render_template_string("{{ get_answer() }}") rv = flask.render_template_string("{{ get_answer() }}")
assert rv == "42" assert rv == "42"
rv = flask.render_template_string("{{ get_stuff_1() }}")
assert rv == "get_stuff_1"
rv = flask.render_template_string("{{ my_get_stuff_custom_name_2() }}")
assert rv == "get_stuff_2"
rv = flask.render_template_string("{{ my_get_stuff_custom_name_3() }}")
assert rv == "get_stuff_3"
def test_request_processing(app, client): def test_request_processing(app, client):
bp = flask.Blueprint("bp", __name__) bp = flask.Blueprint("bp", __name__)

View File

@ -129,6 +129,30 @@ def test_template_filter(app):
assert app.jinja_env.filters["my_reverse"] == my_reverse assert app.jinja_env.filters["my_reverse"] == my_reverse
assert app.jinja_env.filters["my_reverse"]("abcd") == "dcba" assert app.jinja_env.filters["my_reverse"]("abcd") == "dcba"
@app.template_filter
def my_reverse_2(s):
return s[::-1]
assert "my_reverse_2" in app.jinja_env.filters.keys()
assert app.jinja_env.filters["my_reverse_2"] == my_reverse_2
assert app.jinja_env.filters["my_reverse_2"]("abcd") == "dcba"
@app.template_filter("my_reverse_custom_name_3")
def my_reverse_3(s):
return s[::-1]
assert "my_reverse_custom_name_3" in app.jinja_env.filters.keys()
assert app.jinja_env.filters["my_reverse_custom_name_3"] == my_reverse_3
assert app.jinja_env.filters["my_reverse_custom_name_3"]("abcd") == "dcba"
@app.template_filter(name="my_reverse_custom_name_4")
def my_reverse_4(s):
return s[::-1]
assert "my_reverse_custom_name_4" in app.jinja_env.filters.keys()
assert app.jinja_env.filters["my_reverse_custom_name_4"] == my_reverse_4
assert app.jinja_env.filters["my_reverse_custom_name_4"]("abcd") == "dcba"
def test_add_template_filter(app): def test_add_template_filter(app):
def my_reverse(s): def my_reverse(s):
@ -223,6 +247,30 @@ def test_template_test(app):
assert app.jinja_env.tests["boolean"] == boolean assert app.jinja_env.tests["boolean"] == boolean
assert app.jinja_env.tests["boolean"](False) assert app.jinja_env.tests["boolean"](False)
@app.template_test
def boolean_2(value):
return isinstance(value, bool)
assert "boolean_2" in app.jinja_env.tests.keys()
assert app.jinja_env.tests["boolean_2"] == boolean_2
assert app.jinja_env.tests["boolean_2"](False)
@app.template_test("my_boolean_custom_name")
def boolean_3(value):
return isinstance(value, bool)
assert "my_boolean_custom_name" in app.jinja_env.tests.keys()
assert app.jinja_env.tests["my_boolean_custom_name"] == boolean_3
assert app.jinja_env.tests["my_boolean_custom_name"](False)
@app.template_test(name="my_boolean_custom_name_2")
def boolean_4(value):
return isinstance(value, bool)
assert "my_boolean_custom_name_2" in app.jinja_env.tests.keys()
assert app.jinja_env.tests["my_boolean_custom_name_2"] == boolean_4
assert app.jinja_env.tests["my_boolean_custom_name_2"](False)
def test_add_template_test(app): def test_add_template_test(app):
def boolean(value): def boolean(value):
@ -320,6 +368,39 @@ def test_add_template_global(app, app_ctx):
rv = flask.render_template_string("{{ get_stuff() }}") rv = flask.render_template_string("{{ get_stuff() }}")
assert rv == "42" assert rv == "42"
@app.template_global
def get_stuff_1():
return "get_stuff_1"
assert "get_stuff_1" in app.jinja_env.globals.keys()
assert app.jinja_env.globals["get_stuff_1"] == get_stuff_1
assert app.jinja_env.globals["get_stuff_1"](), "get_stuff_1"
rv = flask.render_template_string("{{ get_stuff_1() }}")
assert rv == "get_stuff_1"
@app.template_global("my_get_stuff_custom_name_2")
def get_stuff_2():
return "get_stuff_2"
assert "my_get_stuff_custom_name_2" in app.jinja_env.globals.keys()
assert app.jinja_env.globals["my_get_stuff_custom_name_2"] == get_stuff_2
assert app.jinja_env.globals["my_get_stuff_custom_name_2"](), "get_stuff_2"
rv = flask.render_template_string("{{ my_get_stuff_custom_name_2() }}")
assert rv == "get_stuff_2"
@app.template_global(name="my_get_stuff_custom_name_3")
def get_stuff_3():
return "get_stuff_3"
assert "my_get_stuff_custom_name_3" in app.jinja_env.globals.keys()
assert app.jinja_env.globals["my_get_stuff_custom_name_3"] == get_stuff_3
assert app.jinja_env.globals["my_get_stuff_custom_name_3"](), "get_stuff_3"
rv = flask.render_template_string("{{ my_get_stuff_custom_name_3() }}")
assert rv == "get_stuff_3"
def test_custom_template_loader(client): def test_custom_template_loader(client):
class MyFlask(flask.Flask): class MyFlask(flask.Flask):