functions-framework 3.8.3__tar.gz → 3.9.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {functions_framework-3.8.3 → functions_framework-3.9.0}/PKG-INFO +12 -3
- {functions_framework-3.8.3 → functions_framework-3.9.0}/pyproject.toml +20 -8
- functions_framework-3.9.0/setup.py +72 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/src/functions_framework/__init__.py +28 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/src/functions_framework/_cli.py +14 -3
- {functions_framework-3.8.3 → functions_framework-3.9.0}/src/functions_framework/_function_registry.py +4 -0
- functions_framework-3.9.0/src/functions_framework/_http/__init__.py +59 -0
- functions_framework-3.9.0/src/functions_framework/_http/asgi.py +43 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/src/functions_framework/_http/gunicorn.py +25 -0
- functions_framework-3.9.0/src/functions_framework/aio/__init__.py +352 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/src/functions_framework/execution_id.py +115 -10
- {functions_framework-3.8.3 → functions_framework-3.9.0}/src/functions_framework.egg-info/PKG-INFO +12 -3
- {functions_framework-3.8.3 → functions_framework-3.9.0}/src/functions_framework.egg-info/SOURCES.txt +7 -0
- functions_framework-3.9.0/src/functions_framework.egg-info/namespace_packages.txt +2 -0
- functions_framework-3.9.0/src/functions_framework.egg-info/requires.txt +14 -0
- functions_framework-3.9.0/tests/test_aio.py +171 -0
- functions_framework-3.9.0/tests/test_asgi.py +117 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/tests/test_cli.py +60 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/tests/test_cloud_event_functions.py +45 -26
- functions_framework-3.9.0/tests/test_decorator_functions.py +177 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/tests/test_execution_id.py +1 -0
- functions_framework-3.9.0/tests/test_execution_id_async.py +365 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/tests/test_function_registry.py +16 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/tests/test_functions.py +190 -133
- {functions_framework-3.8.3 → functions_framework-3.9.0}/tests/test_http.py +118 -1
- {functions_framework-3.8.3 → functions_framework-3.9.0}/tests/test_typing.py +12 -0
- functions_framework-3.8.3/src/functions_framework/_http/__init__.py +0 -42
- functions_framework-3.8.3/src/functions_framework.egg-info/requires.txt +0 -8
- functions_framework-3.8.3/tests/test_decorator_functions.py +0 -69
- {functions_framework-3.8.3 → functions_framework-3.9.0}/LICENSE +0 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/README.md +0 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/setup.cfg +0 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/src/functions_framework/__main__.py +0 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/src/functions_framework/_http/flask.py +0 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/src/functions_framework/_typed_event.py +0 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/src/functions_framework/background_event.py +0 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/src/functions_framework/event_conversion.py +0 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/src/functions_framework/exceptions.py +0 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/src/functions_framework/py.typed +0 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/src/functions_framework/request_timeout.py +0 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/src/functions_framework.egg-info/dependency_links.txt +0 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/src/functions_framework.egg-info/entry_points.txt +0 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/src/functions_framework.egg-info/top_level.txt +0 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/src/google/__init__.py +0 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/src/google/cloud/__init__.py +0 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/src/google/cloud/functions/__init__.py +0 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/src/google/cloud/functions/context.py +0 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/src/google/cloud/functions_v1/__init__.py +0 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/src/google/cloud/functions_v1/context.py +0 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/src/google/cloud/functions_v1beta2/__init__.py +0 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/src/google/cloud/functions_v1beta2/context.py +0 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/tests/test_convert.py +0 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/tests/test_main.py +0 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/tests/test_samples.py +0 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/tests/test_timeouts.py +0 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/tests/test_typed_event_functions.py +0 -0
- {functions_framework-3.8.3 → functions_framework-3.9.0}/tests/test_view_functions.py +0 -0
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: functions-framework
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.9.0
|
|
4
4
|
Summary: An open source FaaS (Function as a service) framework for writing portable Python functions -- brought to you by the Google Cloud Functions team.
|
|
5
|
+
Home-page: https://github.com/googlecloudplatform/functions-framework-python
|
|
6
|
+
Author: Google LLC
|
|
5
7
|
Author-email: Google LLC <googleapis-packages@google.com>
|
|
6
8
|
Maintainer-email: Google LLC <googleapis-packages@google.com>
|
|
7
9
|
License: Apache-2.0
|
|
@@ -15,16 +17,23 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
15
17
|
Classifier: Programming Language :: Python :: 3.10
|
|
16
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
-
Requires-Python:
|
|
20
|
+
Requires-Python: >=3.5, <4
|
|
19
21
|
Description-Content-Type: text/markdown
|
|
20
22
|
License-File: LICENSE
|
|
21
23
|
Requires-Dist: flask<4.0,>=2.0
|
|
22
24
|
Requires-Dist: click<9.0,>=7.0
|
|
23
25
|
Requires-Dist: watchdog>=1.0.0
|
|
24
26
|
Requires-Dist: gunicorn>=22.0.0; platform_system != "Windows"
|
|
25
|
-
Requires-Dist: cloudevents
|
|
27
|
+
Requires-Dist: cloudevents<=1.11.0,>=1.2.0
|
|
26
28
|
Requires-Dist: Werkzeug<4.0.0,>=0.14
|
|
29
|
+
Requires-Dist: httpx>=0.24.1
|
|
30
|
+
Requires-Dist: starlette<1.0.0,>=0.37.0; python_version >= "3.8"
|
|
31
|
+
Requires-Dist: uvicorn<1.0.0,>=0.18.0; python_version >= "3.8"
|
|
32
|
+
Requires-Dist: uvicorn-worker<1.0.0,>=0.2.0; python_version >= "3.8"
|
|
33
|
+
Dynamic: author
|
|
34
|
+
Dynamic: home-page
|
|
27
35
|
Dynamic: license-file
|
|
36
|
+
Dynamic: requires-python
|
|
28
37
|
|
|
29
38
|
# Functions Framework for Python
|
|
30
39
|
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "functions-framework"
|
|
3
|
-
version = "3.
|
|
3
|
+
version = "3.9.0"
|
|
4
4
|
description = "An open source FaaS (Function as a service) framework for writing portable Python functions -- brought to you by the Google Cloud Functions team."
|
|
5
5
|
readme = "README.md"
|
|
6
|
-
requires-python = ">=3.
|
|
6
|
+
requires-python = ">=3.7, <4"
|
|
7
7
|
# Once we drop support for Python 3.7 and 3.8, this can become
|
|
8
8
|
# license = "Apache-2.0"
|
|
9
|
-
license = {text = "Apache-2.0"}
|
|
10
|
-
authors = [
|
|
11
|
-
{ name = "Google LLC", email = "googleapis-packages@google.com" }
|
|
12
|
-
]
|
|
9
|
+
license = { text = "Apache-2.0" }
|
|
10
|
+
authors = [{ name = "Google LLC", email = "googleapis-packages@google.com" }]
|
|
13
11
|
maintainers = [
|
|
14
|
-
{ name = "Google LLC", email = "googleapis-packages@google.com" }
|
|
12
|
+
{ name = "Google LLC", email = "googleapis-packages@google.com" },
|
|
15
13
|
]
|
|
16
14
|
keywords = ["functions-framework"]
|
|
17
15
|
classifiers = [
|
|
@@ -29,8 +27,12 @@ dependencies = [
|
|
|
29
27
|
"click>=7.0,<9.0",
|
|
30
28
|
"watchdog>=1.0.0",
|
|
31
29
|
"gunicorn>=22.0.0; platform_system!='Windows'",
|
|
32
|
-
"cloudevents>=1.2.0
|
|
30
|
+
"cloudevents>=1.2.0,<=1.11.0", # Must support python 3.7
|
|
33
31
|
"Werkzeug>=0.14,<4.0.0",
|
|
32
|
+
"httpx>=0.24.1",
|
|
33
|
+
"starlette>=0.37.0,<1.0.0; python_version>='3.8'",
|
|
34
|
+
"uvicorn>=0.18.0,<1.0.0; python_version>='3.8'",
|
|
35
|
+
"uvicorn-worker>=0.2.0,<1.0.0; python_version>='3.8'",
|
|
34
36
|
]
|
|
35
37
|
|
|
36
38
|
[project.urls]
|
|
@@ -55,3 +57,13 @@ functions_framework = ["py.typed"]
|
|
|
55
57
|
|
|
56
58
|
[tool.setuptools.package-dir]
|
|
57
59
|
"" = "src"
|
|
60
|
+
|
|
61
|
+
[dependency-groups]
|
|
62
|
+
dev = [
|
|
63
|
+
"black>=23.3.0",
|
|
64
|
+
"build>=1.1.1",
|
|
65
|
+
"isort>=5.11.5",
|
|
66
|
+
"pretend>=1.0.9",
|
|
67
|
+
"pytest>=7.4.4",
|
|
68
|
+
"pytest-asyncio>=0.21.2",
|
|
69
|
+
]
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Copyright 2020 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from io import open
|
|
16
|
+
from os import path
|
|
17
|
+
|
|
18
|
+
from setuptools import find_packages, setup
|
|
19
|
+
|
|
20
|
+
here = path.abspath(path.dirname(__file__))
|
|
21
|
+
|
|
22
|
+
# Get the long description from the README file
|
|
23
|
+
with open(path.join(here, "README.md"), encoding="utf-8") as f:
|
|
24
|
+
long_description = f.read()
|
|
25
|
+
|
|
26
|
+
setup(
|
|
27
|
+
name="functions-framework",
|
|
28
|
+
version="3.9.0",
|
|
29
|
+
description="An open source FaaS (Function as a service) framework for writing portable Python functions -- brought to you by the Google Cloud Functions team.",
|
|
30
|
+
long_description=long_description,
|
|
31
|
+
long_description_content_type="text/markdown",
|
|
32
|
+
url="https://github.com/googlecloudplatform/functions-framework-python",
|
|
33
|
+
author="Google LLC",
|
|
34
|
+
author_email="googleapis-packages@google.com",
|
|
35
|
+
classifiers=[
|
|
36
|
+
"Development Status :: 5 - Production/Stable ",
|
|
37
|
+
"Intended Audience :: Developers",
|
|
38
|
+
"License :: OSI Approved :: Apache Software License",
|
|
39
|
+
"Programming Language :: Python :: 3.7",
|
|
40
|
+
"Programming Language :: Python :: 3.8",
|
|
41
|
+
"Programming Language :: Python :: 3.9",
|
|
42
|
+
"Programming Language :: Python :: 3.10",
|
|
43
|
+
"Programming Language :: Python :: 3.11",
|
|
44
|
+
"Programming Language :: Python :: 3.12",
|
|
45
|
+
],
|
|
46
|
+
keywords="functions-framework",
|
|
47
|
+
packages=find_packages(where="src"),
|
|
48
|
+
package_data={"functions_framework": ["py.typed"]},
|
|
49
|
+
namespace_packages=["google", "google.cloud"],
|
|
50
|
+
package_dir={"": "src"},
|
|
51
|
+
python_requires=">=3.5, <4",
|
|
52
|
+
install_requires=[
|
|
53
|
+
"flask>=1.0,<4.0",
|
|
54
|
+
"click>=7.0,<9.0",
|
|
55
|
+
"watchdog>=1.0.0",
|
|
56
|
+
"gunicorn>=22.0.0; platform_system!='Windows'",
|
|
57
|
+
"cloudevents>=1.2.0,<2.0.0",
|
|
58
|
+
"Werkzeug>=0.14,<4.0.0",
|
|
59
|
+
],
|
|
60
|
+
extras_require={
|
|
61
|
+
"async": ["starlette>=0.37.0,<1.0.0"],
|
|
62
|
+
},
|
|
63
|
+
entry_points={
|
|
64
|
+
"console_scripts": [
|
|
65
|
+
"ff=functions_framework._cli:_cli",
|
|
66
|
+
"functions-framework=functions_framework._cli:_cli",
|
|
67
|
+
"functions_framework=functions_framework._cli:_cli",
|
|
68
|
+
"functions-framework-python=functions_framework._cli:_cli",
|
|
69
|
+
"functions_framework_python=functions_framework._cli:_cli",
|
|
70
|
+
]
|
|
71
|
+
},
|
|
72
|
+
)
|
|
@@ -327,6 +327,16 @@ def crash_handler(e):
|
|
|
327
327
|
|
|
328
328
|
|
|
329
329
|
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
|
+
"""
|
|
330
340
|
target = _function_registry.get_function_target(target)
|
|
331
341
|
source = _function_registry.get_function_source(source)
|
|
332
342
|
|
|
@@ -370,6 +380,7 @@ def create_app(target=None, source=None, signature_type=None):
|
|
|
370
380
|
setup_logging()
|
|
371
381
|
|
|
372
382
|
_app.wsgi_app = execution_id.WsgiMiddleware(_app.wsgi_app)
|
|
383
|
+
|
|
373
384
|
# Execute the module, within the application context
|
|
374
385
|
with _app.app_context():
|
|
375
386
|
try:
|
|
@@ -394,6 +405,23 @@ def create_app(target=None, source=None, signature_type=None):
|
|
|
394
405
|
# command fails.
|
|
395
406
|
raise e from None
|
|
396
407
|
|
|
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
|
+
|
|
397
425
|
# Get the configured function signature type
|
|
398
426
|
signature_type = _function_registry.get_func_signature_type(target, signature_type)
|
|
399
427
|
|
|
@@ -16,7 +16,7 @@ import os
|
|
|
16
16
|
|
|
17
17
|
import click
|
|
18
18
|
|
|
19
|
-
from functions_framework import create_app
|
|
19
|
+
from functions_framework import _function_registry, create_app
|
|
20
20
|
from functions_framework._http import create_server
|
|
21
21
|
|
|
22
22
|
|
|
@@ -32,6 +32,17 @@ from functions_framework._http import create_server
|
|
|
32
32
|
@click.option("--host", envvar="HOST", type=click.STRING, default="0.0.0.0")
|
|
33
33
|
@click.option("--port", envvar="PORT", type=click.INT, default=8080)
|
|
34
34
|
@click.option("--debug", envvar="DEBUG", is_flag=True)
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
@click.option(
|
|
36
|
+
"--asgi",
|
|
37
|
+
envvar="FUNCTION_USE_ASGI",
|
|
38
|
+
is_flag=True,
|
|
39
|
+
help="Use ASGI server for function execution",
|
|
40
|
+
)
|
|
41
|
+
def _cli(target, source, signature_type, host, port, debug, asgi):
|
|
42
|
+
if asgi:
|
|
43
|
+
from functions_framework.aio import create_asgi_app
|
|
44
|
+
|
|
45
|
+
app = create_asgi_app(target, source, signature_type)
|
|
46
|
+
else:
|
|
47
|
+
app = create_app(target, source, signature_type)
|
|
37
48
|
create_server(app, debug).run(host, port)
|
|
@@ -40,6 +40,10 @@ REGISTRY_MAP = {}
|
|
|
40
40
|
# Keys are the user function name, values are the type of the function input
|
|
41
41
|
INPUT_TYPE_MAP = {}
|
|
42
42
|
|
|
43
|
+
# ASGI_FUNCTIONS stores function names that require ASGI mode.
|
|
44
|
+
# Functions decorated with @aio.http or @aio.cloud_event are added here.
|
|
45
|
+
ASGI_FUNCTIONS = set()
|
|
46
|
+
|
|
43
47
|
|
|
44
48
|
def get_user_function(source, source_module, target):
|
|
45
49
|
"""Returns user function, raises exception for invalid function."""
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Copyright 2020 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from flask import Flask
|
|
16
|
+
|
|
17
|
+
from functions_framework._http.flask import FlaskApplication
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class HTTPServer:
|
|
21
|
+
def __init__(self, app, debug, **options):
|
|
22
|
+
self.app = app
|
|
23
|
+
self.debug = debug
|
|
24
|
+
self.options = options
|
|
25
|
+
|
|
26
|
+
if isinstance(app, Flask):
|
|
27
|
+
if self.debug:
|
|
28
|
+
self.server_class = FlaskApplication
|
|
29
|
+
else:
|
|
30
|
+
try:
|
|
31
|
+
from functions_framework._http.gunicorn import GunicornApplication
|
|
32
|
+
|
|
33
|
+
self.server_class = GunicornApplication
|
|
34
|
+
except ImportError as e:
|
|
35
|
+
self.server_class = FlaskApplication
|
|
36
|
+
else: # pragma: no cover
|
|
37
|
+
if self.debug:
|
|
38
|
+
from functions_framework._http.asgi import StarletteApplication
|
|
39
|
+
|
|
40
|
+
self.server_class = StarletteApplication
|
|
41
|
+
else:
|
|
42
|
+
try:
|
|
43
|
+
from functions_framework._http.gunicorn import UvicornApplication
|
|
44
|
+
|
|
45
|
+
self.server_class = UvicornApplication
|
|
46
|
+
except ImportError as e:
|
|
47
|
+
from functions_framework._http.asgi import StarletteApplication
|
|
48
|
+
|
|
49
|
+
self.server_class = StarletteApplication
|
|
50
|
+
|
|
51
|
+
def run(self, host, port):
|
|
52
|
+
http_server = self.server_class(
|
|
53
|
+
self.app, host, port, self.debug, **self.options
|
|
54
|
+
)
|
|
55
|
+
http_server.run()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def create_server(app, debug, **options):
|
|
59
|
+
return HTTPServer(app, debug, **options)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Copyright 2025 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
import uvicorn
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class StarletteApplication:
|
|
19
|
+
"""A Starlette application that uses Uvicorn for direct serving (development mode)."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, app, host, port, debug, **options):
|
|
22
|
+
"""Initialize the Starlette application.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
app: The ASGI application to serve
|
|
26
|
+
host: The host to bind to
|
|
27
|
+
port: The port to bind to
|
|
28
|
+
debug: Whether to run in debug mode
|
|
29
|
+
**options: Additional options to pass to Uvicorn
|
|
30
|
+
"""
|
|
31
|
+
self.app = app
|
|
32
|
+
self.host = host
|
|
33
|
+
self.port = port
|
|
34
|
+
self.debug = debug
|
|
35
|
+
|
|
36
|
+
self.options = {
|
|
37
|
+
"log_level": "debug" if debug else "error",
|
|
38
|
+
}
|
|
39
|
+
self.options.update(options)
|
|
40
|
+
|
|
41
|
+
def run(self):
|
|
42
|
+
"""Run the Uvicorn server directly."""
|
|
43
|
+
uvicorn.run(self.app, host=self.host, port=int(self.port), **self.options)
|
{functions_framework-3.8.3 → functions_framework-3.9.0}/src/functions_framework/_http/gunicorn.py
RENAMED
|
@@ -70,3 +70,28 @@ class GThreadWorkerWithTimeoutSupport(ThreadWorker): # pragma: no cover
|
|
|
70
70
|
def handle_request(self, req, conn):
|
|
71
71
|
with ThreadingTimeout(TIMEOUT_SECONDS):
|
|
72
72
|
super(GThreadWorkerWithTimeoutSupport, self).handle_request(req, conn)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class UvicornApplication(gunicorn.app.base.BaseApplication):
|
|
76
|
+
"""Gunicorn application for ASGI apps using Uvicorn workers."""
|
|
77
|
+
|
|
78
|
+
def __init__(self, app, host, port, debug, **options):
|
|
79
|
+
self.options = {
|
|
80
|
+
"bind": "%s:%s" % (host, port),
|
|
81
|
+
"workers": int(os.environ.get("WORKERS", 1)),
|
|
82
|
+
"worker_class": "uvicorn_worker.UvicornWorker",
|
|
83
|
+
"timeout": int(os.environ.get("CLOUD_RUN_TIMEOUT_SECONDS", 0)),
|
|
84
|
+
"loglevel": os.environ.get("GUNICORN_LOG_LEVEL", "error"),
|
|
85
|
+
"limit_request_line": 0,
|
|
86
|
+
}
|
|
87
|
+
self.options.update(options)
|
|
88
|
+
self.app = app
|
|
89
|
+
|
|
90
|
+
super().__init__()
|
|
91
|
+
|
|
92
|
+
def load_config(self):
|
|
93
|
+
for key, value in self.options.items():
|
|
94
|
+
self.cfg.set(key, value)
|
|
95
|
+
|
|
96
|
+
def load(self):
|
|
97
|
+
return self.app
|