functions-framework 3.8.2__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.
Files changed (57) hide show
  1. {functions_framework-3.8.2 → functions_framework-3.9.0}/PKG-INFO +21 -18
  2. {functions_framework-3.8.2 → functions_framework-3.9.0}/README.md +4 -11
  3. functions_framework-3.9.0/pyproject.toml +69 -0
  4. {functions_framework-3.8.2 → functions_framework-3.9.0}/setup.py +4 -1
  5. {functions_framework-3.8.2 → functions_framework-3.9.0}/src/functions_framework/__init__.py +28 -0
  6. {functions_framework-3.8.2 → functions_framework-3.9.0}/src/functions_framework/_cli.py +14 -3
  7. {functions_framework-3.8.2 → functions_framework-3.9.0}/src/functions_framework/_function_registry.py +4 -0
  8. functions_framework-3.9.0/src/functions_framework/_http/__init__.py +59 -0
  9. functions_framework-3.9.0/src/functions_framework/_http/asgi.py +43 -0
  10. {functions_framework-3.8.2 → functions_framework-3.9.0}/src/functions_framework/_http/gunicorn.py +25 -0
  11. functions_framework-3.9.0/src/functions_framework/aio/__init__.py +352 -0
  12. {functions_framework-3.8.2 → functions_framework-3.9.0}/src/functions_framework/execution_id.py +115 -10
  13. {functions_framework-3.8.2 → functions_framework-3.9.0}/src/functions_framework.egg-info/PKG-INFO +21 -18
  14. {functions_framework-3.8.2 → functions_framework-3.9.0}/src/functions_framework.egg-info/SOURCES.txt +6 -0
  15. functions_framework-3.9.0/src/functions_framework.egg-info/requires.txt +14 -0
  16. functions_framework-3.9.0/tests/test_aio.py +171 -0
  17. functions_framework-3.9.0/tests/test_asgi.py +117 -0
  18. {functions_framework-3.8.2 → functions_framework-3.9.0}/tests/test_cli.py +60 -0
  19. {functions_framework-3.8.2 → functions_framework-3.9.0}/tests/test_cloud_event_functions.py +45 -26
  20. functions_framework-3.9.0/tests/test_decorator_functions.py +177 -0
  21. {functions_framework-3.8.2 → functions_framework-3.9.0}/tests/test_execution_id.py +1 -0
  22. functions_framework-3.9.0/tests/test_execution_id_async.py +365 -0
  23. {functions_framework-3.8.2 → functions_framework-3.9.0}/tests/test_function_registry.py +16 -0
  24. {functions_framework-3.8.2 → functions_framework-3.9.0}/tests/test_functions.py +190 -133
  25. {functions_framework-3.8.2 → functions_framework-3.9.0}/tests/test_http.py +118 -1
  26. {functions_framework-3.8.2 → functions_framework-3.9.0}/tests/test_typing.py +12 -0
  27. functions_framework-3.8.2/src/functions_framework/_http/__init__.py +0 -42
  28. functions_framework-3.8.2/src/functions_framework.egg-info/requires.txt +0 -8
  29. functions_framework-3.8.2/tests/test_decorator_functions.py +0 -69
  30. {functions_framework-3.8.2 → functions_framework-3.9.0}/LICENSE +0 -0
  31. {functions_framework-3.8.2 → functions_framework-3.9.0}/setup.cfg +0 -0
  32. {functions_framework-3.8.2 → functions_framework-3.9.0}/src/functions_framework/__main__.py +0 -0
  33. {functions_framework-3.8.2 → functions_framework-3.9.0}/src/functions_framework/_http/flask.py +0 -0
  34. {functions_framework-3.8.2 → functions_framework-3.9.0}/src/functions_framework/_typed_event.py +0 -0
  35. {functions_framework-3.8.2 → functions_framework-3.9.0}/src/functions_framework/background_event.py +0 -0
  36. {functions_framework-3.8.2 → functions_framework-3.9.0}/src/functions_framework/event_conversion.py +0 -0
  37. {functions_framework-3.8.2 → functions_framework-3.9.0}/src/functions_framework/exceptions.py +0 -0
  38. {functions_framework-3.8.2 → functions_framework-3.9.0}/src/functions_framework/py.typed +0 -0
  39. {functions_framework-3.8.2 → functions_framework-3.9.0}/src/functions_framework/request_timeout.py +0 -0
  40. {functions_framework-3.8.2 → functions_framework-3.9.0}/src/functions_framework.egg-info/dependency_links.txt +0 -0
  41. {functions_framework-3.8.2 → functions_framework-3.9.0}/src/functions_framework.egg-info/entry_points.txt +0 -0
  42. {functions_framework-3.8.2 → functions_framework-3.9.0}/src/functions_framework.egg-info/namespace_packages.txt +0 -0
  43. {functions_framework-3.8.2 → functions_framework-3.9.0}/src/functions_framework.egg-info/top_level.txt +0 -0
  44. {functions_framework-3.8.2 → functions_framework-3.9.0}/src/google/__init__.py +0 -0
  45. {functions_framework-3.8.2 → functions_framework-3.9.0}/src/google/cloud/__init__.py +0 -0
  46. {functions_framework-3.8.2 → functions_framework-3.9.0}/src/google/cloud/functions/__init__.py +0 -0
  47. {functions_framework-3.8.2 → functions_framework-3.9.0}/src/google/cloud/functions/context.py +0 -0
  48. {functions_framework-3.8.2 → functions_framework-3.9.0}/src/google/cloud/functions_v1/__init__.py +0 -0
  49. {functions_framework-3.8.2 → functions_framework-3.9.0}/src/google/cloud/functions_v1/context.py +0 -0
  50. {functions_framework-3.8.2 → functions_framework-3.9.0}/src/google/cloud/functions_v1beta2/__init__.py +0 -0
  51. {functions_framework-3.8.2 → functions_framework-3.9.0}/src/google/cloud/functions_v1beta2/context.py +0 -0
  52. {functions_framework-3.8.2 → functions_framework-3.9.0}/tests/test_convert.py +0 -0
  53. {functions_framework-3.8.2 → functions_framework-3.9.0}/tests/test_main.py +0 -0
  54. {functions_framework-3.8.2 → functions_framework-3.9.0}/tests/test_samples.py +0 -0
  55. {functions_framework-3.8.2 → functions_framework-3.9.0}/tests/test_timeouts.py +0 -0
  56. {functions_framework-3.8.2 → functions_framework-3.9.0}/tests/test_typed_event_functions.py +0 -0
  57. {functions_framework-3.8.2 → functions_framework-3.9.0}/tests/test_view_functions.py +0 -0
@@ -1,14 +1,16 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: functions-framework
3
- Version: 3.8.2
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
5
  Home-page: https://github.com/googlecloudplatform/functions-framework-python
6
6
  Author: Google LLC
7
- Author-email: googleapis-packages@google.com
7
+ Author-email: Google LLC <googleapis-packages@google.com>
8
+ Maintainer-email: Google LLC <googleapis-packages@google.com>
9
+ License: Apache-2.0
10
+ Project-URL: Homepage, https://github.com/googlecloudplatform/functions-framework-python
8
11
  Keywords: functions-framework
9
- Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Development Status :: 5 - Production/Stable
10
13
  Classifier: Intended Audience :: Developers
11
- Classifier: License :: OSI Approved :: Apache Software License
12
14
  Classifier: Programming Language :: Python :: 3.7
13
15
  Classifier: Programming Language :: Python :: 3.8
14
16
  Classifier: Programming Language :: Python :: 3.9
@@ -18,12 +20,20 @@ Classifier: Programming Language :: Python :: 3.12
18
20
  Requires-Python: >=3.5, <4
19
21
  Description-Content-Type: text/markdown
20
22
  License-File: LICENSE
21
- Requires-Dist: flask<4.0,>=1.0
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<2.0.0,>=1.2.0
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
35
+ Dynamic: license-file
36
+ Dynamic: requires-python
27
37
 
28
38
  # Functions Framework for Python
29
39
 
@@ -37,9 +47,8 @@ Python functions -- brought to you by the Google Cloud Functions team.
37
47
  The Functions Framework lets you write lightweight functions that run in many
38
48
  different environments, including:
39
49
 
40
- * [Google Cloud Functions](https://cloud.google.com/functions/)
50
+ * [Google Cloud Run Functions](https://cloud.google.com/functions/)
41
51
  * Your local development machine
42
- * [Cloud Run and Cloud Run for Anthos](https://cloud.google.com/run/)
43
52
  * [Knative](https://github.com/knative/)-based environments
44
53
 
45
54
  The framework allows you to go from:
@@ -321,7 +330,7 @@ https://cloud.google.com/functions/docs/tutorials/pubsub#functions_helloworld_pu
321
330
 
322
331
  ## Run your function on serverless platforms
323
332
 
324
- ### Google Cloud Functions
333
+ ### Google Cloud Run functions
325
334
 
326
335
  This Functions Framework is based on the [Python Runtime on Google Cloud Functions](https://cloud.google.com/functions/docs/concepts/python-runtime).
327
336
 
@@ -329,12 +338,6 @@ On Cloud Functions, using the Functions Framework is not necessary: you don't ne
329
338
 
330
339
  After you've written your function, you can simply deploy it from your local machine using the `gcloud` command-line tool. [Check out the Cloud Functions quickstart](https://cloud.google.com/functions/docs/quickstart).
331
340
 
332
- ### Cloud Run/Cloud Run on GKE
333
-
334
- Once you've written your function and added the Functions Framework to your `requirements.txt` file, all that's left is to create a container image. [Check out the Cloud Run quickstart](https://cloud.google.com/run/docs/quickstarts/build-and-deploy) for Python to create a container image and deploy it to Cloud Run. You'll write a `Dockerfile` when you build your container. This `Dockerfile` allows you to specify exactly what goes into your container (including custom binaries, a specific operating system, and more). [Here is an example `Dockerfile` that calls Functions Framework.](https://github.com/GoogleCloudPlatform/functions-framework-python/blob/main/examples/cloud_run_http)
335
-
336
- If you want even more control over the environment, you can [deploy your container image to Cloud Run on GKE](https://cloud.google.com/run/docs/quickstarts/prebuilt-deploy-gke). With Cloud Run on GKE, you can run your function on a GKE cluster, which gives you additional control over the environment (including use of GPU-based instances, longer timeouts and more).
337
-
338
341
  ### Container environments based on Knative
339
342
 
340
343
  Cloud Run and Cloud Run on GKE both implement the [Knative Serving API](https://www.knative.dev/docs/). The Functions Framework is designed to be compatible with Knative environments. Just build and deploy your container to a Knative environment.
@@ -352,10 +355,10 @@ You can configure the Functions Framework using command-line flags or environmen
352
355
  | `--source` | `FUNCTION_SOURCE` | The path to the file containing your function. Default: `main.py` (in the current working directory) |
353
356
  | `--debug` | `DEBUG` | A flag that allows to run functions-framework to run in debug mode, including live reloading. Default: `False` |
354
357
 
355
- ## Enable Google Cloud Function Events
358
+ ## Enable Google Cloud Run function Events
356
359
 
357
360
  The Functions Framework can unmarshall incoming
358
- Google Cloud Functions [event](https://cloud.google.com/functions/docs/concepts/events-triggers#events) payloads to `event` and `context` objects.
361
+ Google Cloud Run functions [event](https://cloud.google.com/functions/docs/concepts/events-triggers#events) payloads to `event` and `context` objects.
359
362
  These will be passed as arguments to your function when it receives a request.
360
363
  Note that your function must use the `event`-style function signature:
361
364
 
@@ -10,9 +10,8 @@ Python functions -- brought to you by the Google Cloud Functions team.
10
10
  The Functions Framework lets you write lightweight functions that run in many
11
11
  different environments, including:
12
12
 
13
- * [Google Cloud Functions](https://cloud.google.com/functions/)
13
+ * [Google Cloud Run Functions](https://cloud.google.com/functions/)
14
14
  * Your local development machine
15
- * [Cloud Run and Cloud Run for Anthos](https://cloud.google.com/run/)
16
15
  * [Knative](https://github.com/knative/)-based environments
17
16
 
18
17
  The framework allows you to go from:
@@ -294,7 +293,7 @@ https://cloud.google.com/functions/docs/tutorials/pubsub#functions_helloworld_pu
294
293
 
295
294
  ## Run your function on serverless platforms
296
295
 
297
- ### Google Cloud Functions
296
+ ### Google Cloud Run functions
298
297
 
299
298
  This Functions Framework is based on the [Python Runtime on Google Cloud Functions](https://cloud.google.com/functions/docs/concepts/python-runtime).
300
299
 
@@ -302,12 +301,6 @@ On Cloud Functions, using the Functions Framework is not necessary: you don't ne
302
301
 
303
302
  After you've written your function, you can simply deploy it from your local machine using the `gcloud` command-line tool. [Check out the Cloud Functions quickstart](https://cloud.google.com/functions/docs/quickstart).
304
303
 
305
- ### Cloud Run/Cloud Run on GKE
306
-
307
- Once you've written your function and added the Functions Framework to your `requirements.txt` file, all that's left is to create a container image. [Check out the Cloud Run quickstart](https://cloud.google.com/run/docs/quickstarts/build-and-deploy) for Python to create a container image and deploy it to Cloud Run. You'll write a `Dockerfile` when you build your container. This `Dockerfile` allows you to specify exactly what goes into your container (including custom binaries, a specific operating system, and more). [Here is an example `Dockerfile` that calls Functions Framework.](https://github.com/GoogleCloudPlatform/functions-framework-python/blob/main/examples/cloud_run_http)
308
-
309
- If you want even more control over the environment, you can [deploy your container image to Cloud Run on GKE](https://cloud.google.com/run/docs/quickstarts/prebuilt-deploy-gke). With Cloud Run on GKE, you can run your function on a GKE cluster, which gives you additional control over the environment (including use of GPU-based instances, longer timeouts and more).
310
-
311
304
  ### Container environments based on Knative
312
305
 
313
306
  Cloud Run and Cloud Run on GKE both implement the [Knative Serving API](https://www.knative.dev/docs/). The Functions Framework is designed to be compatible with Knative environments. Just build and deploy your container to a Knative environment.
@@ -325,10 +318,10 @@ You can configure the Functions Framework using command-line flags or environmen
325
318
  | `--source` | `FUNCTION_SOURCE` | The path to the file containing your function. Default: `main.py` (in the current working directory) |
326
319
  | `--debug` | `DEBUG` | A flag that allows to run functions-framework to run in debug mode, including live reloading. Default: `False` |
327
320
 
328
- ## Enable Google Cloud Function Events
321
+ ## Enable Google Cloud Run function Events
329
322
 
330
323
  The Functions Framework can unmarshall incoming
331
- Google Cloud Functions [event](https://cloud.google.com/functions/docs/concepts/events-triggers#events) payloads to `event` and `context` objects.
324
+ Google Cloud Run functions [event](https://cloud.google.com/functions/docs/concepts/events-triggers#events) payloads to `event` and `context` objects.
332
325
  These will be passed as arguments to your function when it receives a request.
333
326
  Note that your function must use the `event`-style function signature:
334
327
 
@@ -0,0 +1,69 @@
1
+ [project]
2
+ name = "functions-framework"
3
+ version = "3.9.0"
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
+ readme = "README.md"
6
+ requires-python = ">=3.7, <4"
7
+ # Once we drop support for Python 3.7 and 3.8, this can become
8
+ # license = "Apache-2.0"
9
+ license = { text = "Apache-2.0" }
10
+ authors = [{ name = "Google LLC", email = "googleapis-packages@google.com" }]
11
+ maintainers = [
12
+ { name = "Google LLC", email = "googleapis-packages@google.com" },
13
+ ]
14
+ keywords = ["functions-framework"]
15
+ classifiers = [
16
+ "Development Status :: 5 - Production/Stable",
17
+ "Intended Audience :: Developers",
18
+ "Programming Language :: Python :: 3.7",
19
+ "Programming Language :: Python :: 3.8",
20
+ "Programming Language :: Python :: 3.9",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ ]
25
+ dependencies = [
26
+ "flask>=2.0,<4.0",
27
+ "click>=7.0,<9.0",
28
+ "watchdog>=1.0.0",
29
+ "gunicorn>=22.0.0; platform_system!='Windows'",
30
+ "cloudevents>=1.2.0,<=1.11.0", # Must support python 3.7
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'",
36
+ ]
37
+
38
+ [project.urls]
39
+ Homepage = "https://github.com/googlecloudplatform/functions-framework-python"
40
+
41
+ [project.scripts]
42
+ ff = "functions_framework._cli:_cli"
43
+ functions-framework = "functions_framework._cli:_cli"
44
+ functions_framework = "functions_framework._cli:_cli"
45
+ functions-framework-python = "functions_framework._cli:_cli"
46
+ functions_framework_python = "functions_framework._cli:_cli"
47
+
48
+ [build-system]
49
+ requires = ["setuptools>=61.0.0"]
50
+ build-backend = "setuptools.build_meta"
51
+
52
+ [tool.setuptools.packages.find]
53
+ where = ["src"]
54
+
55
+ [tool.setuptools.package-data]
56
+ functions_framework = ["py.typed"]
57
+
58
+ [tool.setuptools.package-dir]
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
+ ]
@@ -25,7 +25,7 @@ with open(path.join(here, "README.md"), encoding="utf-8") as f:
25
25
 
26
26
  setup(
27
27
  name="functions-framework",
28
- version="3.8.2",
28
+ version="3.9.0",
29
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
30
  long_description=long_description,
31
31
  long_description_content_type="text/markdown",
@@ -57,6 +57,9 @@ setup(
57
57
  "cloudevents>=1.2.0,<2.0.0",
58
58
  "Werkzeug>=0.14,<4.0.0",
59
59
  ],
60
+ extras_require={
61
+ "async": ["starlette>=0.37.0,<1.0.0"],
62
+ },
60
63
  entry_points={
61
64
  "console_scripts": [
62
65
  "ff=functions_framework._cli:_cli",
@@ -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
- def _cli(target, source, signature_type, host, port, debug):
36
- app = create_app(target, source, signature_type)
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)
@@ -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