Skip to content

Commit a52cb05

Browse files
committed
feat: better support use of aio decorator
1 parent 0e137cf commit a52cb05

File tree

3 files changed

+57
-5
lines changed

3 files changed

+57
-5
lines changed

src/functions_framework/__init__.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,16 @@ def crash_handler(e):
327327

328328

329329
def create_app(target=None, source=None, signature_type=None):
330+
"""Create an app for the function.
331+
332+
Args:
333+
target: The name of the target function to invoke
334+
source: The source file containing the function
335+
signature_type: The signature type of the function
336+
337+
Returns:
338+
A Flask WSGI app or Starlette ASGI app depending on function decorators
339+
"""
330340
target = _function_registry.get_function_target(target)
331341
source = _function_registry.get_function_source(source)
332342

@@ -370,6 +380,7 @@ def handle_none(rv):
370380
setup_logging()
371381

372382
_app.wsgi_app = execution_id.WsgiMiddleware(_app.wsgi_app)
383+
373384
# Execute the module, within the application context
374385
with _app.app_context():
375386
try:
@@ -394,6 +405,23 @@ def function(*_args, **_kwargs):
394405
# command fails.
395406
raise e from None
396407

408+
use_asgi = target in _function_registry.ASGI_FUNCTIONS
409+
if use_asgi:
410+
# This function needs ASGI, delegate to create_asgi_app
411+
# Note: @aio decorators only register functions in ASGI_FUNCTIONS when the
412+
# module is imported. We can't know if a function uses @aio until after
413+
# we load the module.
414+
#
415+
# To avoid loading modules twice, we always create a Flask app first, load the
416+
# module within its context, then check if ASGI is needed. This results in an
417+
# unused Flask app for ASGI functions, but we accept this memory overhead as a
418+
# trade-off.
419+
from functions_framework.aio import create_asgi_app_from_module
420+
421+
return create_asgi_app_from_module(
422+
target, source, signature_type, source_module, spec
423+
)
424+
397425
# Get the configured function signature type
398426
signature_type = _function_registry.get_func_signature_type(target, signature_type)
399427

src/functions_framework/_cli.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,10 @@
3939
help="Use ASGI server for function execution",
4040
)
4141
def _cli(target, source, signature_type, host, port, debug, asgi):
42-
if not asgi and target in _function_registry.ASGI_FUNCTIONS:
43-
asgi = True
44-
45-
if asgi: # pragma: no cover
42+
if asgi:
4643
from functions_framework.aio import create_asgi_app
4744

4845
app = create_asgi_app(target, source, signature_type)
4946
else:
5047
app = create_app(target, source, signature_type)
51-
5248
create_server(app, debug).run(host, port)

src/functions_framework/aio/__init__.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,29 @@ async def __call__(self, scope, receive, send):
215215
# Don't re-raise to prevent starlette from printing traceback again
216216

217217

218+
def create_asgi_app_from_module(target, source, signature_type, source_module, spec):
219+
"""Create an ASGI application from an already-loaded module.
220+
221+
Args:
222+
target: The name of the target function to invoke
223+
source: The source file containing the function
224+
signature_type: The signature type of the function
225+
source_module: The already-loaded module
226+
spec: The module spec
227+
228+
Returns:
229+
A Starlette ASGI application instance
230+
"""
231+
enable_id_logging = _enable_execution_id_logging()
232+
if enable_id_logging:
233+
_configure_app_execution_id_logging()
234+
235+
function = _function_registry.get_user_function(source, source_module, target)
236+
signature_type = _function_registry.get_func_signature_type(target, signature_type)
237+
238+
return _create_asgi_app_with_function(function, signature_type, enable_id_logging)
239+
240+
218241
def create_asgi_app(target=None, source=None, signature_type=None):
219242
"""Create an ASGI application for the function.
220243
@@ -245,6 +268,11 @@ def create_asgi_app(target=None, source=None, signature_type=None):
245268
function = _function_registry.get_user_function(source, source_module, target)
246269
signature_type = _function_registry.get_func_signature_type(target, signature_type)
247270

271+
return _create_asgi_app_with_function(function, signature_type, enable_id_logging)
272+
273+
274+
def _create_asgi_app_with_function(function, signature_type, enable_id_logging):
275+
"""Create an ASGI app with the given function and signature type."""
248276
is_async = inspect.iscoroutinefunction(function)
249277
routes = []
250278
if signature_type == _function_registry.HTTP_SIGNATURE_TYPE:

0 commit comments

Comments
 (0)