From fe178134bab796e37a9720463cd2a9190b8cd5fc Mon Sep 17 00:00:00 2001 From: Jonathan Ballet Date: Tue, 30 May 2023 19:56:40 +0200 Subject: [PATCH 1/5] Initial typing of the public API This adds type annotations for the 2 main decorators of the library. Closes: https://github.com/GoogleCloudPlatform/functions-framework-python/issues/190 --- README.md | 5 +++-- setup.py | 2 ++ src/functions_framework/__init__.py | 9 ++++++--- src/functions_framework/py.typed | 0 tests/test_typing.py | 14 ++++++++++++++ tox.ini | 2 ++ 6 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 src/functions_framework/py.typed create mode 100644 tests/test_typing.py diff --git a/README.md b/README.md index 9dffc60c..7ca0b908 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ Create an `main.py` file with the following contents: import functions_framework @functions_framework.http -def hello(request): +def hello(request: flask.Request) -> flask.typing.ResponseReturnValue: return "Hello world!" ``` @@ -98,9 +98,10 @@ Create an `main.py` file with the following contents: ```python import functions_framework +from cloudevents.http.event import CloudEvent @functions_framework.cloud_event -def hello_cloud_event(cloud_event): +def hello_cloud_event(cloud_event: CloudEvent) -> None: print(f"Received event with ID: {cloud_event['id']} and data {cloud_event.data}") ``` diff --git a/setup.py b/setup.py index 9e8aedab..e6427cd3 100644 --- a/setup.py +++ b/setup.py @@ -46,6 +46,8 @@ ], keywords="functions-framework", packages=find_packages(where="src"), + package_data={"functions_framework": ["py.typed"]}, + zip_safe=False, namespace_packages=["google", "google.cloud"], package_dir={"": "src"}, python_requires=">=3.5, <4", diff --git a/src/functions_framework/__init__.py b/src/functions_framework/__init__.py index d4575b57..df2b32ae 100644 --- a/src/functions_framework/__init__.py +++ b/src/functions_framework/__init__.py @@ -23,13 +23,14 @@ import types from inspect import signature -from typing import Type +from typing import Callable, Type import cloudevents.exceptions as cloud_exceptions import flask import werkzeug from cloudevents.http import from_http, is_binary +from cloudevents.http.event import CloudEvent from functions_framework import _function_registry, _typed_event, event_conversion from functions_framework.background_event import BackgroundEvent @@ -45,6 +46,8 @@ _CLOUDEVENT_MIME_TYPE = "application/cloudevents+json" +CloudEventFunction = Callable[[CloudEvent], None] +HTTPFunction = Callable[[flask.Request], flask.typing.ResponseReturnValue] class _LoggingHandler(io.TextIOWrapper): """Logging replacement for stdout and stderr in GCF Python 3.7.""" @@ -59,7 +62,7 @@ def write(self, out): return self.stderr.write(json.dumps(payload) + "\n") -def cloud_event(func): +def cloud_event(func: CloudEventFunction) -> CloudEventFunction: """Decorator that registers cloudevent as user function signature type.""" _function_registry.REGISTRY_MAP[ func.__name__ @@ -99,7 +102,7 @@ def wrapper(*args, **kwargs): return _typed -def http(func): +def http(func: HTTPFunction) -> HTTPFunction: """Decorator that registers http as user function signature type.""" _function_registry.REGISTRY_MAP[ func.__name__ diff --git a/src/functions_framework/py.typed b/src/functions_framework/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/tests/test_typing.py b/tests/test_typing.py new file mode 100644 index 00000000..f4ddb7af --- /dev/null +++ b/tests/test_typing.py @@ -0,0 +1,14 @@ +import typing + +if typing.TYPE_CHECKING: # pragma: no cover + import flask + import functions_framework + from cloudevents.http.event import CloudEvent + + @functions_framework.http + def hello(request: flask.Request) -> flask.typing.ResponseReturnValue: + return "Hello world!" + + @functions_framework.cloud_event + def hello_cloud_event(cloud_event: CloudEvent) -> None: + print(f"Received event: id={cloud_event['id']} and data={cloud_event.data}") diff --git a/tox.ini b/tox.ini index 0fe3dba6..e8c555b5 100644 --- a/tox.ini +++ b/tox.ini @@ -19,8 +19,10 @@ deps = black twine isort + mypy commands = black --check src tests setup.py conftest.py --exclude tests/test_functions/background_load_error/main.py isort -c src tests setup.py conftest.py + mypy tests/test_typing.py python setup.py --quiet sdist bdist_wheel twine check dist/* From 13bb93335b7adc3071e1e756a6e57dc7b860bd24 Mon Sep 17 00:00:00 2001 From: Jonathan Ballet Date: Tue, 30 May 2023 20:05:19 +0200 Subject: [PATCH 2/5] fix typing --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7ca0b908..e65ddbb8 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ functions-framework==3.* Create an `main.py` file with the following contents: ```python +import flask import functions_framework @functions_framework.http From cf75b83122bfd534529f0946158f4582c0a31ee0 Mon Sep 17 00:00:00 2001 From: Jonathan Ballet Date: Tue, 15 Aug 2023 09:05:34 +0200 Subject: [PATCH 3/5] Remove zip_safe flag, as it's not needed anymore. --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index e6427cd3..0a98faef 100644 --- a/setup.py +++ b/setup.py @@ -47,7 +47,6 @@ keywords="functions-framework", packages=find_packages(where="src"), package_data={"functions_framework": ["py.typed"]}, - zip_safe=False, namespace_packages=["google", "google.cloud"], package_dir={"": "src"}, python_requires=">=3.5, <4", From c7be816f3d8b0272a99c94969949552c2d9ca302 Mon Sep 17 00:00:00 2001 From: Jonathan Ballet Date: Tue, 15 Aug 2023 20:43:37 +0200 Subject: [PATCH 4/5] run black --- src/functions_framework/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/functions_framework/__init__.py b/src/functions_framework/__init__.py index df2b32ae..8d47670c 100644 --- a/src/functions_framework/__init__.py +++ b/src/functions_framework/__init__.py @@ -49,6 +49,7 @@ CloudEventFunction = Callable[[CloudEvent], None] HTTPFunction = Callable[[flask.Request], flask.typing.ResponseReturnValue] + class _LoggingHandler(io.TextIOWrapper): """Logging replacement for stdout and stderr in GCF Python 3.7.""" From 977cf967ac5bf1f683a83ff8ea358f771ccb5454 Mon Sep 17 00:00:00 2001 From: Jonathan Ballet Date: Mon, 28 Aug 2023 22:28:35 +0200 Subject: [PATCH 5/5] fix import order --- tests/test_typing.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_typing.py b/tests/test_typing.py index f4ddb7af..279cd636 100644 --- a/tests/test_typing.py +++ b/tests/test_typing.py @@ -2,9 +2,11 @@ if typing.TYPE_CHECKING: # pragma: no cover import flask - import functions_framework + from cloudevents.http.event import CloudEvent + import functions_framework + @functions_framework.http def hello(request: flask.Request) -> flask.typing.ResponseReturnValue: return "Hello world!"