functions-framework 3.4.0__tar.gz → 3.6.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 (44) hide show
  1. {functions-framework-3.4.0 → functions_framework-3.6.0}/PKG-INFO +17 -11
  2. {functions-framework-3.4.0 → functions_framework-3.6.0}/README.md +8 -6
  3. {functions-framework-3.4.0 → functions_framework-3.6.0}/setup.py +7 -7
  4. {functions-framework-3.4.0 → functions_framework-3.6.0}/src/functions_framework/__init__.py +34 -11
  5. {functions-framework-3.4.0 → functions_framework-3.6.0}/src/functions_framework/_http/gunicorn.py +6 -3
  6. {functions-framework-3.4.0 → functions_framework-3.6.0}/src/functions_framework/_typed_event.py +3 -3
  7. {functions-framework-3.4.0 → functions_framework-3.6.0}/src/functions_framework/event_conversion.py +1 -1
  8. functions_framework-3.6.0/src/functions_framework/py.typed +0 -0
  9. {functions-framework-3.4.0 → functions_framework-3.6.0}/src/functions_framework.egg-info/PKG-INFO +17 -11
  10. {functions-framework-3.4.0 → functions_framework-3.6.0}/src/functions_framework.egg-info/SOURCES.txt +2 -0
  11. {functions-framework-3.4.0 → functions_framework-3.6.0}/src/functions_framework.egg-info/requires.txt +2 -2
  12. {functions-framework-3.4.0 → functions_framework-3.6.0}/tests/test_functions.py +13 -0
  13. {functions-framework-3.4.0 → functions_framework-3.6.0}/tests/test_http.py +7 -6
  14. {functions-framework-3.4.0 → functions_framework-3.6.0}/tests/test_samples.py +1 -1
  15. functions_framework-3.6.0/tests/test_typing.py +16 -0
  16. {functions-framework-3.4.0 → functions_framework-3.6.0}/LICENSE +0 -0
  17. {functions-framework-3.4.0 → functions_framework-3.6.0}/setup.cfg +0 -0
  18. {functions-framework-3.4.0 → functions_framework-3.6.0}/src/functions_framework/__main__.py +0 -0
  19. {functions-framework-3.4.0 → functions_framework-3.6.0}/src/functions_framework/_cli.py +0 -0
  20. {functions-framework-3.4.0 → functions_framework-3.6.0}/src/functions_framework/_function_registry.py +0 -0
  21. {functions-framework-3.4.0 → functions_framework-3.6.0}/src/functions_framework/_http/__init__.py +0 -0
  22. {functions-framework-3.4.0 → functions_framework-3.6.0}/src/functions_framework/_http/flask.py +0 -0
  23. {functions-framework-3.4.0 → functions_framework-3.6.0}/src/functions_framework/background_event.py +0 -0
  24. {functions-framework-3.4.0 → functions_framework-3.6.0}/src/functions_framework/exceptions.py +0 -0
  25. {functions-framework-3.4.0 → functions_framework-3.6.0}/src/functions_framework.egg-info/dependency_links.txt +0 -0
  26. {functions-framework-3.4.0 → functions_framework-3.6.0}/src/functions_framework.egg-info/entry_points.txt +0 -0
  27. {functions-framework-3.4.0 → functions_framework-3.6.0}/src/functions_framework.egg-info/namespace_packages.txt +0 -0
  28. {functions-framework-3.4.0 → functions_framework-3.6.0}/src/functions_framework.egg-info/top_level.txt +0 -0
  29. {functions-framework-3.4.0 → functions_framework-3.6.0}/src/google/__init__.py +0 -0
  30. {functions-framework-3.4.0 → functions_framework-3.6.0}/src/google/cloud/__init__.py +0 -0
  31. {functions-framework-3.4.0 → functions_framework-3.6.0}/src/google/cloud/functions/__init__.py +0 -0
  32. {functions-framework-3.4.0 → functions_framework-3.6.0}/src/google/cloud/functions/context.py +0 -0
  33. {functions-framework-3.4.0 → functions_framework-3.6.0}/src/google/cloud/functions_v1/__init__.py +0 -0
  34. {functions-framework-3.4.0 → functions_framework-3.6.0}/src/google/cloud/functions_v1/context.py +0 -0
  35. {functions-framework-3.4.0 → functions_framework-3.6.0}/src/google/cloud/functions_v1beta2/__init__.py +0 -0
  36. {functions-framework-3.4.0 → functions_framework-3.6.0}/src/google/cloud/functions_v1beta2/context.py +0 -0
  37. {functions-framework-3.4.0 → functions_framework-3.6.0}/tests/test_cli.py +0 -0
  38. {functions-framework-3.4.0 → functions_framework-3.6.0}/tests/test_cloud_event_functions.py +0 -0
  39. {functions-framework-3.4.0 → functions_framework-3.6.0}/tests/test_convert.py +0 -0
  40. {functions-framework-3.4.0 → functions_framework-3.6.0}/tests/test_decorator_functions.py +0 -0
  41. {functions-framework-3.4.0 → functions_framework-3.6.0}/tests/test_function_registry.py +0 -0
  42. {functions-framework-3.4.0 → functions_framework-3.6.0}/tests/test_main.py +0 -0
  43. {functions-framework-3.4.0 → functions_framework-3.6.0}/tests/test_typed_event_functions.py +0 -0
  44. {functions-framework-3.4.0 → functions_framework-3.6.0}/tests/test_view_functions.py +0 -0
@@ -1,30 +1,34 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: functions-framework
3
- Version: 3.4.0
3
+ Version: 3.6.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
7
  Author-email: googleapis-packages@google.com
8
8
  Keywords: functions-framework
9
- Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Development Status :: 5 - Production/Stable
10
10
  Classifier: Intended Audience :: Developers
11
11
  Classifier: License :: OSI Approved :: Apache Software License
12
- Classifier: Programming Language :: Python :: 3
13
- Classifier: Programming Language :: Python :: 3.5
14
- Classifier: Programming Language :: Python :: 3.6
15
12
  Classifier: Programming Language :: Python :: 3.7
16
13
  Classifier: Programming Language :: Python :: 3.8
17
14
  Classifier: Programming Language :: Python :: 3.9
18
15
  Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
19
18
  Requires-Python: >=3.5, <4
20
19
  Description-Content-Type: text/markdown
21
20
  License-File: LICENSE
21
+ Requires-Dist: flask<4.0,>=1.0
22
+ Requires-Dist: click<9.0,>=7.0
23
+ Requires-Dist: watchdog>=1.0.0
24
+ Requires-Dist: gunicorn>=19.2.0; platform_system != "Windows"
25
+ Requires-Dist: cloudevents<2.0.0,>=1.2.0
22
26
 
23
27
  # Functions Framework for Python
24
28
 
25
29
  [![PyPI version](https://badge.fury.io/py/functions-framework.svg)](https://badge.fury.io/py/functions-framework)
26
30
 
27
- [![Python unit CI][ff_python_unit_img]][ff_python_unit_link] [![Python lint CI][ff_python_lint_img]][ff_python_lint_link] [![Python conformace CI][ff_python_conformance_img]][ff_python_conformance_link]
31
+ [![Python unit CI][ff_python_unit_img]][ff_python_unit_link] [![Python lint CI][ff_python_lint_img]][ff_python_lint_link] [![Python conformace CI][ff_python_conformance_img]][ff_python_conformance_link] ![Security Scorecard](https://api.securityscorecards.dev/projects/github.com/GoogleCloudPlatform/functions-framework-python/badge)
28
32
 
29
33
  An open source FaaS (Function as a service) framework for writing portable
30
34
  Python functions -- brought to you by the Google Cloud Functions team.
@@ -81,14 +85,15 @@ functions-framework==3.*
81
85
  Create an `main.py` file with the following contents:
82
86
 
83
87
  ```python
88
+ import flask
84
89
  import functions_framework
85
90
 
86
91
  @functions_framework.http
87
- def hello(request):
92
+ def hello(request: flask.Request) -> flask.typing.ResponseReturnValue:
88
93
  return "Hello world!"
89
94
  ```
90
95
 
91
- > Your function is passed a single parameter, `(request)`, which is a Flask [`Request`](http://flask.pocoo.org/docs/1.0/api/#flask.Request) object.
96
+ > Your function is passed a single parameter, `(request)`, which is a Flask [`Request`](https://flask.palletsprojects.com/en/3.0.x/api/#flask.Request) object.
92
97
 
93
98
  Run the following command:
94
99
 
@@ -120,13 +125,14 @@ Create an `main.py` file with the following contents:
120
125
 
121
126
  ```python
122
127
  import functions_framework
128
+ from cloudevents.http.event import CloudEvent
123
129
 
124
130
  @functions_framework.cloud_event
125
- def hello_cloud_event(cloud_event):
131
+ def hello_cloud_event(cloud_event: CloudEvent) -> None:
126
132
  print(f"Received event with ID: {cloud_event['id']} and data {cloud_event.data}")
127
133
  ```
128
134
 
129
- > Your function is passed a single [CloudEvent](https://github.com/cloudevents/sdk-python/blob/master/cloudevents/sdk/event/v1.py) parameter.
135
+ > Your function is passed a single [CloudEvent](https://github.com/cloudevents/sdk-python/blob/main/cloudevents/sdk/event/v1.py) parameter.
130
136
 
131
137
  Run the following command to run `hello_cloud_event` target locally:
132
138
 
@@ -324,7 +330,7 @@ After you've written your function, you can simply deploy it from your local mac
324
330
 
325
331
  ### Cloud Run/Cloud Run on GKE
326
332
 
327
- 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/master/examples/cloud_run_http)
333
+ 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)
328
334
 
329
335
  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).
330
336
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![PyPI version](https://badge.fury.io/py/functions-framework.svg)](https://badge.fury.io/py/functions-framework)
4
4
 
5
- [![Python unit CI][ff_python_unit_img]][ff_python_unit_link] [![Python lint CI][ff_python_lint_img]][ff_python_lint_link] [![Python conformace CI][ff_python_conformance_img]][ff_python_conformance_link]
5
+ [![Python unit CI][ff_python_unit_img]][ff_python_unit_link] [![Python lint CI][ff_python_lint_img]][ff_python_lint_link] [![Python conformace CI][ff_python_conformance_img]][ff_python_conformance_link] ![Security Scorecard](https://api.securityscorecards.dev/projects/github.com/GoogleCloudPlatform/functions-framework-python/badge)
6
6
 
7
7
  An open source FaaS (Function as a service) framework for writing portable
8
8
  Python functions -- brought to you by the Google Cloud Functions team.
@@ -59,14 +59,15 @@ functions-framework==3.*
59
59
  Create an `main.py` file with the following contents:
60
60
 
61
61
  ```python
62
+ import flask
62
63
  import functions_framework
63
64
 
64
65
  @functions_framework.http
65
- def hello(request):
66
+ def hello(request: flask.Request) -> flask.typing.ResponseReturnValue:
66
67
  return "Hello world!"
67
68
  ```
68
69
 
69
- > Your function is passed a single parameter, `(request)`, which is a Flask [`Request`](http://flask.pocoo.org/docs/1.0/api/#flask.Request) object.
70
+ > Your function is passed a single parameter, `(request)`, which is a Flask [`Request`](https://flask.palletsprojects.com/en/3.0.x/api/#flask.Request) object.
70
71
 
71
72
  Run the following command:
72
73
 
@@ -98,13 +99,14 @@ Create an `main.py` file with the following contents:
98
99
 
99
100
  ```python
100
101
  import functions_framework
102
+ from cloudevents.http.event import CloudEvent
101
103
 
102
104
  @functions_framework.cloud_event
103
- def hello_cloud_event(cloud_event):
105
+ def hello_cloud_event(cloud_event: CloudEvent) -> None:
104
106
  print(f"Received event with ID: {cloud_event['id']} and data {cloud_event.data}")
105
107
  ```
106
108
 
107
- > Your function is passed a single [CloudEvent](https://github.com/cloudevents/sdk-python/blob/master/cloudevents/sdk/event/v1.py) parameter.
109
+ > Your function is passed a single [CloudEvent](https://github.com/cloudevents/sdk-python/blob/main/cloudevents/sdk/event/v1.py) parameter.
108
110
 
109
111
  Run the following command to run `hello_cloud_event` target locally:
110
112
 
@@ -302,7 +304,7 @@ After you've written your function, you can simply deploy it from your local mac
302
304
 
303
305
  ### Cloud Run/Cloud Run on GKE
304
306
 
305
- 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/master/examples/cloud_run_http)
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)
306
308
 
307
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).
308
310
 
@@ -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.4.0",
28
+ version="3.6.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",
@@ -33,27 +33,27 @@ setup(
33
33
  author="Google LLC",
34
34
  author_email="googleapis-packages@google.com",
35
35
  classifiers=[
36
- "Development Status :: 3 - Alpha",
36
+ "Development Status :: 5 - Production/Stable ",
37
37
  "Intended Audience :: Developers",
38
38
  "License :: OSI Approved :: Apache Software License",
39
- "Programming Language :: Python :: 3",
40
- "Programming Language :: Python :: 3.5",
41
- "Programming Language :: Python :: 3.6",
42
39
  "Programming Language :: Python :: 3.7",
43
40
  "Programming Language :: Python :: 3.8",
44
41
  "Programming Language :: Python :: 3.9",
45
42
  "Programming Language :: Python :: 3.10",
43
+ "Programming Language :: Python :: 3.11",
44
+ "Programming Language :: Python :: 3.12",
46
45
  ],
47
46
  keywords="functions-framework",
48
47
  packages=find_packages(where="src"),
48
+ package_data={"functions_framework": ["py.typed"]},
49
49
  namespace_packages=["google", "google.cloud"],
50
50
  package_dir={"": "src"},
51
51
  python_requires=">=3.5, <4",
52
52
  install_requires=[
53
- "flask>=1.0,<3.0",
53
+ "flask>=1.0,<4.0",
54
54
  "click>=7.0,<9.0",
55
55
  "watchdog>=1.0.0",
56
- "gunicorn>=19.2.0,<21.0; platform_system!='Windows'",
56
+ "gunicorn>=19.2.0; platform_system!='Windows'",
57
57
  "cloudevents>=1.2.0,<2.0.0",
58
58
  ],
59
59
  entry_points={
@@ -23,13 +23,14 @@ import sys
23
23
  import types
24
24
 
25
25
  from inspect import signature
26
- from typing import Type
26
+ from typing import Callable, Type
27
27
 
28
28
  import cloudevents.exceptions as cloud_exceptions
29
29
  import flask
30
30
  import werkzeug
31
31
 
32
32
  from cloudevents.http import from_http, is_binary
33
+ from cloudevents.http.event import CloudEvent
33
34
 
34
35
  from functions_framework import _function_registry, _typed_event, event_conversion
35
36
  from functions_framework.background_event import BackgroundEvent
@@ -45,6 +46,9 @@ _CRASH = "crash"
45
46
 
46
47
  _CLOUDEVENT_MIME_TYPE = "application/cloudevents+json"
47
48
 
49
+ CloudEventFunction = Callable[[CloudEvent], None]
50
+ HTTPFunction = Callable[[flask.Request], flask.typing.ResponseReturnValue]
51
+
48
52
 
49
53
  class _LoggingHandler(io.TextIOWrapper):
50
54
  """Logging replacement for stdout and stderr in GCF Python 3.7."""
@@ -59,11 +63,11 @@ class _LoggingHandler(io.TextIOWrapper):
59
63
  return self.stderr.write(json.dumps(payload) + "\n")
60
64
 
61
65
 
62
- def cloud_event(func):
66
+ def cloud_event(func: CloudEventFunction) -> CloudEventFunction:
63
67
  """Decorator that registers cloudevent as user function signature type."""
64
- _function_registry.REGISTRY_MAP[
65
- func.__name__
66
- ] = _function_registry.CLOUDEVENT_SIGNATURE_TYPE
68
+ _function_registry.REGISTRY_MAP[func.__name__] = (
69
+ _function_registry.CLOUDEVENT_SIGNATURE_TYPE
70
+ )
67
71
 
68
72
  @functools.wraps(func)
69
73
  def wrapper(*args, **kwargs):
@@ -99,11 +103,11 @@ def typed(*args):
99
103
  return _typed
100
104
 
101
105
 
102
- def http(func):
106
+ def http(func: HTTPFunction) -> HTTPFunction:
103
107
  """Decorator that registers http as user function signature type."""
104
- _function_registry.REGISTRY_MAP[
105
- func.__name__
106
- ] = _function_registry.HTTP_SIGNATURE_TYPE
108
+ _function_registry.REGISTRY_MAP[func.__name__] = (
109
+ _function_registry.HTTP_SIGNATURE_TYPE
110
+ )
107
111
 
108
112
  @functools.wraps(func)
109
113
  def wrapper(*args, **kwargs):
@@ -353,11 +357,30 @@ def create_app(target=None, source=None, signature_type=None):
353
357
 
354
358
  # Execute the module, within the application context
355
359
  with _app.app_context():
356
- spec.loader.exec_module(source_module)
360
+ try:
361
+ spec.loader.exec_module(source_module)
362
+ function = _function_registry.get_user_function(
363
+ source, source_module, target
364
+ )
365
+ except Exception as e:
366
+ if werkzeug.serving.is_running_from_reloader():
367
+ # When reloading, print out the error immediately, but raise
368
+ # it later so the debugger or server can handle it.
369
+ import traceback
370
+
371
+ traceback.print_exc()
372
+ err = e
373
+
374
+ def function(*_args, **_kwargs):
375
+ raise err from None
376
+
377
+ else:
378
+ # When not reloading, raise the error immediately so the
379
+ # command fails.
380
+ raise e from None
357
381
 
358
382
  # Get the configured function signature type
359
383
  signature_type = _function_registry.get_func_signature_type(target, signature_type)
360
- function = _function_registry.get_user_function(source, source_module, target)
361
384
 
362
385
  _configure_app(_app, function, signature_type)
363
386
 
@@ -12,6 +12,8 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ import os
16
+
15
17
  import gunicorn.app.base
16
18
 
17
19
 
@@ -19,14 +21,15 @@ class GunicornApplication(gunicorn.app.base.BaseApplication):
19
21
  def __init__(self, app, host, port, debug, **options):
20
22
  self.options = {
21
23
  "bind": "%s:%s" % (host, port),
22
- "workers": 1,
23
- "threads": 1024,
24
- "timeout": 0,
24
+ "workers": os.environ.get("WORKERS", (os.cpu_count() or 1) * 4),
25
+ "threads": os.environ.get("THREADS", 1),
26
+ "timeout": os.environ.get("CLOUD_RUN_TIMEOUT_SECONDS", 300),
25
27
  "loglevel": "error",
26
28
  "limit_request_line": 0,
27
29
  }
28
30
  self.options.update(options)
29
31
  self.app = app
32
+
30
33
  super().__init__()
31
34
 
32
35
  def load_config(self):
@@ -48,9 +48,9 @@ def register_typed_event(decorator_type, func):
48
48
  )
49
49
 
50
50
  _function_registry.INPUT_TYPE_MAP[func.__name__] = input_type
51
- _function_registry.REGISTRY_MAP[
52
- func.__name__
53
- ] = _function_registry.TYPED_SIGNATURE_TYPE
51
+ _function_registry.REGISTRY_MAP[func.__name__] = (
52
+ _function_registry.TYPED_SIGNATURE_TYPE
53
+ )
54
54
 
55
55
 
56
56
  """ Checks whether the response type of the typed function has a to_dict method"""
@@ -27,7 +27,7 @@ _CLOUD_EVENT_SPEC_VERSION = "1.0"
27
27
 
28
28
  # Maps background/legacy event types to their equivalent CloudEvent types.
29
29
  # For more info on event mappings see
30
- # https://github.com/GoogleCloudPlatform/functions-framework-conformance/blob/master/docs/mapping.md
30
+ # https://github.com/GoogleCloudPlatform/functions-framework-conformance/blob/main/docs/mapping.md
31
31
  _BACKGROUND_TO_CE_TYPE = {
32
32
  "google.pubsub.topic.publish": "google.cloud.pubsub.topic.v1.messagePublished",
33
33
  "providers/cloud.pubsub/eventTypes/topic.publish": "google.cloud.pubsub.topic.v1.messagePublished",
@@ -1,30 +1,34 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: functions-framework
3
- Version: 3.4.0
3
+ Version: 3.6.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
7
  Author-email: googleapis-packages@google.com
8
8
  Keywords: functions-framework
9
- Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Development Status :: 5 - Production/Stable
10
10
  Classifier: Intended Audience :: Developers
11
11
  Classifier: License :: OSI Approved :: Apache Software License
12
- Classifier: Programming Language :: Python :: 3
13
- Classifier: Programming Language :: Python :: 3.5
14
- Classifier: Programming Language :: Python :: 3.6
15
12
  Classifier: Programming Language :: Python :: 3.7
16
13
  Classifier: Programming Language :: Python :: 3.8
17
14
  Classifier: Programming Language :: Python :: 3.9
18
15
  Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
19
18
  Requires-Python: >=3.5, <4
20
19
  Description-Content-Type: text/markdown
21
20
  License-File: LICENSE
21
+ Requires-Dist: flask<4.0,>=1.0
22
+ Requires-Dist: click<9.0,>=7.0
23
+ Requires-Dist: watchdog>=1.0.0
24
+ Requires-Dist: gunicorn>=19.2.0; platform_system != "Windows"
25
+ Requires-Dist: cloudevents<2.0.0,>=1.2.0
22
26
 
23
27
  # Functions Framework for Python
24
28
 
25
29
  [![PyPI version](https://badge.fury.io/py/functions-framework.svg)](https://badge.fury.io/py/functions-framework)
26
30
 
27
- [![Python unit CI][ff_python_unit_img]][ff_python_unit_link] [![Python lint CI][ff_python_lint_img]][ff_python_lint_link] [![Python conformace CI][ff_python_conformance_img]][ff_python_conformance_link]
31
+ [![Python unit CI][ff_python_unit_img]][ff_python_unit_link] [![Python lint CI][ff_python_lint_img]][ff_python_lint_link] [![Python conformace CI][ff_python_conformance_img]][ff_python_conformance_link] ![Security Scorecard](https://api.securityscorecards.dev/projects/github.com/GoogleCloudPlatform/functions-framework-python/badge)
28
32
 
29
33
  An open source FaaS (Function as a service) framework for writing portable
30
34
  Python functions -- brought to you by the Google Cloud Functions team.
@@ -81,14 +85,15 @@ functions-framework==3.*
81
85
  Create an `main.py` file with the following contents:
82
86
 
83
87
  ```python
88
+ import flask
84
89
  import functions_framework
85
90
 
86
91
  @functions_framework.http
87
- def hello(request):
92
+ def hello(request: flask.Request) -> flask.typing.ResponseReturnValue:
88
93
  return "Hello world!"
89
94
  ```
90
95
 
91
- > Your function is passed a single parameter, `(request)`, which is a Flask [`Request`](http://flask.pocoo.org/docs/1.0/api/#flask.Request) object.
96
+ > Your function is passed a single parameter, `(request)`, which is a Flask [`Request`](https://flask.palletsprojects.com/en/3.0.x/api/#flask.Request) object.
92
97
 
93
98
  Run the following command:
94
99
 
@@ -120,13 +125,14 @@ Create an `main.py` file with the following contents:
120
125
 
121
126
  ```python
122
127
  import functions_framework
128
+ from cloudevents.http.event import CloudEvent
123
129
 
124
130
  @functions_framework.cloud_event
125
- def hello_cloud_event(cloud_event):
131
+ def hello_cloud_event(cloud_event: CloudEvent) -> None:
126
132
  print(f"Received event with ID: {cloud_event['id']} and data {cloud_event.data}")
127
133
  ```
128
134
 
129
- > Your function is passed a single [CloudEvent](https://github.com/cloudevents/sdk-python/blob/master/cloudevents/sdk/event/v1.py) parameter.
135
+ > Your function is passed a single [CloudEvent](https://github.com/cloudevents/sdk-python/blob/main/cloudevents/sdk/event/v1.py) parameter.
130
136
 
131
137
  Run the following command to run `hello_cloud_event` target locally:
132
138
 
@@ -324,7 +330,7 @@ After you've written your function, you can simply deploy it from your local mac
324
330
 
325
331
  ### Cloud Run/Cloud Run on GKE
326
332
 
327
- 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/master/examples/cloud_run_http)
333
+ 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)
328
334
 
329
335
  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).
330
336
 
@@ -10,6 +10,7 @@ src/functions_framework/_typed_event.py
10
10
  src/functions_framework/background_event.py
11
11
  src/functions_framework/event_conversion.py
12
12
  src/functions_framework/exceptions.py
13
+ src/functions_framework/py.typed
13
14
  src/functions_framework.egg-info/PKG-INFO
14
15
  src/functions_framework.egg-info/SOURCES.txt
15
16
  src/functions_framework.egg-info/dependency_links.txt
@@ -38,4 +39,5 @@ tests/test_http.py
38
39
  tests/test_main.py
39
40
  tests/test_samples.py
40
41
  tests/test_typed_event_functions.py
42
+ tests/test_typing.py
41
43
  tests/test_view_functions.py
@@ -1,7 +1,7 @@
1
- flask<3.0,>=1.0
1
+ flask<4.0,>=1.0
2
2
  click<9.0,>=7.0
3
3
  watchdog>=1.0.0
4
4
  cloudevents<2.0.0,>=1.2.0
5
5
 
6
6
  [:platform_system != "Windows"]
7
- gunicorn<21.0,>=19.2.0
7
+ gunicorn>=19.2.0
@@ -323,6 +323,19 @@ def test_invalid_function_definition_function_syntax_error():
323
323
  )
324
324
 
325
325
 
326
+ def test_invalid_function_definition_function_syntax_robustness_with_debug(monkeypatch):
327
+ monkeypatch.setattr(
328
+ functions_framework.werkzeug.serving, "is_running_from_reloader", lambda: True
329
+ )
330
+ source = TEST_FUNCTIONS_DIR / "background_load_error" / "main.py"
331
+ target = "function"
332
+
333
+ client = create_app(target, source).test_client()
334
+
335
+ resp = client.get("/")
336
+ assert resp.status_code == 500
337
+
338
+
326
339
  def test_invalid_function_definition_missing_dependency():
327
340
  source = TEST_FUNCTIONS_DIR / "background_missing_dependency" / "main.py"
328
341
  target = "function"
@@ -12,6 +12,7 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ import os
15
16
  import platform
16
17
  import sys
17
18
 
@@ -96,17 +97,17 @@ def test_gunicorn_application(debug):
96
97
  assert gunicorn_app.app == app
97
98
  assert gunicorn_app.options == {
98
99
  "bind": "%s:%s" % (host, port),
99
- "workers": 1,
100
- "threads": 1024,
101
- "timeout": 0,
100
+ "workers": os.cpu_count() * 4,
101
+ "threads": 1,
102
+ "timeout": 300,
102
103
  "loglevel": "error",
103
104
  "limit_request_line": 0,
104
105
  }
105
106
 
106
107
  assert gunicorn_app.cfg.bind == ["1.2.3.4:1234"]
107
- assert gunicorn_app.cfg.workers == 1
108
- assert gunicorn_app.cfg.threads == 1024
109
- assert gunicorn_app.cfg.timeout == 0
108
+ assert gunicorn_app.cfg.workers == os.cpu_count() * 4
109
+ assert gunicorn_app.cfg.threads == 1
110
+ assert gunicorn_app.cfg.timeout == 300
110
111
  assert gunicorn_app.load() == app
111
112
 
112
113
 
@@ -24,7 +24,7 @@ class TestSamples:
24
24
  self.stop_all_containers(client)
25
25
 
26
26
  TAG = "cloud_run_http"
27
- client.images.build(path=str(EXAMPLES_DIR / "cloud_run_http"), tag={TAG})
27
+ client.images.build(path=str(EXAMPLES_DIR / "cloud_run_http"), tag=TAG)
28
28
  container = client.containers.run(image=TAG, detach=True, ports={8080: 8080})
29
29
  timeout = 10
30
30
  success = False
@@ -0,0 +1,16 @@
1
+ import typing
2
+
3
+ if typing.TYPE_CHECKING: # pragma: no cover
4
+ import flask
5
+
6
+ from cloudevents.http.event import CloudEvent
7
+
8
+ import functions_framework
9
+
10
+ @functions_framework.http
11
+ def hello(request: flask.Request) -> flask.typing.ResponseReturnValue:
12
+ return "Hello world!"
13
+
14
+ @functions_framework.cloud_event
15
+ def hello_cloud_event(cloud_event: CloudEvent) -> None:
16
+ print(f"Received event: id={cloud_event['id']} and data={cloud_event.data}")