Simplify the async handling code

Firstly `run_sync` was a misleading name as it didn't run anything,
instead I think `async_to_sync` is much clearer as it converts a
coroutine function to a function. (Name stolen from asgiref).

Secondly trying to run the ensure_sync during registration made the
code more complex and brittle, e.g. the _flask_async_wrapper
usage. This was done to pay any setup costs during registration rather
than runtime, however this only saved a iscoroutne check. It allows
the weirdness of the Blueprint and Scaffold ensure_sync methods to be
removed.

Switching to runtime ensure_sync usage provides a method for
extensions to also support async, as now documented.
This commit is contained in:
pgjones 2021-05-02 20:49:46 +01:00 committed by David Lord
parent cb13128cf0
commit 7f87f3dd93
No known key found for this signature in database
GPG key ID: 7A1C87E3F5BC42A8
6 changed files with 53 additions and 68 deletions

View file

@ -292,13 +292,10 @@ class Blueprint(Scaffold):
# Merge blueprint data into parent.
if first_registration:
def extend(bp_dict, parent_dict, ensure_sync=False):
def extend(bp_dict, parent_dict):
for key, values in bp_dict.items():
key = self.name if key is None else f"{self.name}.{key}"
if ensure_sync:
values = [app.ensure_sync(func) for func in values]
parent_dict[key].extend(values)
for key, value in self.error_handler_spec.items():
@ -307,8 +304,7 @@ class Blueprint(Scaffold):
dict,
{
code: {
exc_class: app.ensure_sync(func)
for exc_class, func in code_values.items()
exc_class: func for exc_class, func in code_values.items()
}
for code, code_values in value.items()
},
@ -316,16 +312,13 @@ class Blueprint(Scaffold):
app.error_handler_spec[key] = value
for endpoint, func in self.view_functions.items():
app.view_functions[endpoint] = app.ensure_sync(func)
app.view_functions[endpoint] = func
extend(
self.before_request_funcs, app.before_request_funcs, ensure_sync=True
)
extend(self.after_request_funcs, app.after_request_funcs, ensure_sync=True)
extend(self.before_request_funcs, app.before_request_funcs)
extend(self.after_request_funcs, app.after_request_funcs)
extend(
self.teardown_request_funcs,
app.teardown_request_funcs,
ensure_sync=True,
)
extend(self.url_default_functions, app.url_default_functions)
extend(self.url_value_preprocessors, app.url_value_preprocessors)
@ -478,9 +471,7 @@ class Blueprint(Scaffold):
before each request, even if outside of a blueprint.
"""
self.record_once(
lambda s: s.app.before_request_funcs.setdefault(None, []).append(
s.app.ensure_sync(f)
)
lambda s: s.app.before_request_funcs.setdefault(None, []).append(f)
)
return f
@ -490,9 +481,7 @@ class Blueprint(Scaffold):
"""Like :meth:`Flask.before_first_request`. Such a function is
executed before the first request to the application.
"""
self.record_once(
lambda s: s.app.before_first_request_funcs.append(s.app.ensure_sync(f))
)
self.record_once(lambda s: s.app.before_first_request_funcs.append(f))
return f
def after_app_request(self, f: AfterRequestCallable) -> AfterRequestCallable:
@ -500,9 +489,7 @@ class Blueprint(Scaffold):
is executed after each request, even if outside of the blueprint.
"""
self.record_once(
lambda s: s.app.after_request_funcs.setdefault(None, []).append(
s.app.ensure_sync(f)
)
lambda s: s.app.after_request_funcs.setdefault(None, []).append(f)
)
return f
@ -553,14 +540,3 @@ class Blueprint(Scaffold):
lambda s: s.app.url_default_functions.setdefault(None, []).append(f)
)
return f
def ensure_sync(self, f: t.Callable) -> t.Callable:
"""Ensure the function is synchronous.
Override if you would like custom async to sync behaviour in
this blueprint. Otherwise the app's
:meth:`~flask.Flask.ensure_sync` is used.
.. versionadded:: 2.0
"""
return f