Skip to content

feat: Support strongly typed functions signature - #208

Merged
kappratiksha merged 23 commits into
GoogleCloudPlatform:masterfrom
kappratiksha:master
Dec 12, 2022
Merged

feat: Support strongly typed functions signature#208
kappratiksha merged 23 commits into
GoogleCloudPlatform:masterfrom
kappratiksha:master

Conversation

@kappratiksha

@kappratikshakappratiksha commented Nov 21, 2022

Copy link
Copy Markdown
Contributor

Add a new signature in functions framework that will support strongly typed objects. This signature will take a strong type T as an input and can return a strong type T, built in types supported by flask or None as an output.

Sample declarations:

@functions_framework.typed(T1)
def my_function(x:T1) -> T2:
@functions_framework.typed
def my_function(x:T1) -> T2:
@functions_framework.typed
def my_function(x:T1) -> None:
@functions_framework.typed
def my_function(x:T1) -> string:

The new signature will only support types abiding by the below contract to convert a python object to and from json:

  • Implement from_dict() method. This method converts a dict to the strongly typed python object.
  • Implement to_dict() method. This method converts the python object to a dict.

Here is sample python object:

class MyMessage:
message: str
retry_limit: int
def __init__(self, message: str, retry_limit: int) -> None:
self.message = message
self.retry_limit = retry_limit
@staticmethod
def from_dict(obj: dict) -> 'MyMessage':
assert isinstance(obj, dict)
message = from_str(obj.get("message"))
retry_limit = from_int(obj.get("retryLimit"))
return MyMessage(message, retry_limit)
def to_dict(self) -> dict:
result: dict = {}
result["message"] = from_str(self.message)
result["retryLimit"] = from_int(self.retry_limit)
return result

@kappratiksha
kappratiksha marked this pull request as ready for review November 29, 2022 19:36
@anniefu

Copy link
Copy Markdown
Contributor

Could you add a description to the PR explaining the new feature and a code snippet sample of how to use it? I would define the typed class with the to_dict and from_dict functions in the code snippet to for illustrative purposes.

@anniefuanniefu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whew, pretty big one! Nice work getting this done!

I think I reviewed everything except tests. But gonna get these comments out for now.

Comment threadsrc/functions_framework/typed_event.py Outdated
Comment threadtests/test_functions/typed_events/missing_from_dict.py Outdated
Comment threadsrc/functions_framework/event_conversion.py Outdated
Comment threadsrc/functions_framework/__init__.py
Comment threadsrc/functions_framework/__init__.py Outdated
Comment threadsrc/functions_framework/__init__.py Outdated
Comment threadsrc/functions_framework/_function_registry.py Outdated
Comment threadsrc/functions_framework/_function_registry.py Outdated
Comment threadsrc/functions_framework/__init__.py Outdated
Comment threadsrc/functions_framework/typed_event.py Outdated
Comment threadsrc/functions_framework/__init__.py Outdated
Comment threadsrc/functions_framework/_typed_event.py Outdated
Comment threadsrc/functions_framework/_typed_event.py Outdated
Comment threadtests/test_functions/typed_events/typed_event.py Outdated
Comment threadtests/test_functions/typed_events/typed_event.py Outdated
Comment threadtests/test_functions/typed_events/typed_event.py
Comment threadtests/test_typed_event_functions.py Outdated
Comment threadsrc/functions_framework/__init__.py
Comment threadtests/test_typed_event_functions.py
Comment threadsrc/functions_framework/__init__.py Outdated
@anniefu

Copy link
Copy Markdown
Contributor

Oh btw, for the lint check, you can copy the commands executed here to reproduce locally: https://github.com/GoogleCloudPlatform/functions-framework-python/blob/master/.github/workflows/lint.yml

@kappratikshakappratiksha changed the title Support strongly typed functions signaturefeat: Support strongly typed functions signatureDec 9, 2022

@anniefuanniefu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just nits and a existential-ish question

Comment threadsrc/functions_framework/__init__.py Outdated
Comment threadsrc/functions_framework/__init__.py
Comment threadsrc/functions_framework/__init__.py Outdated
return wrapper


def typed(*args):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I've been pondering for a while whether there's a way for us to provide typing here where like someone could hover over the definition of @typed and see that it's supposed to just take one, optional input type parameter instead of this kind of ambiguous *args situation.

I'm not sure it's possible though unless we enforce a named parameter like @typed(input_type=MyType)...

Can you think of anything?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that is the only way to get type hints for a function. Also, it's not very useful for generics also.

if I do something like this-def typed(input_type=T) , the type hints are input_type: Any
We can leave it as is right now, I added documentation for this decorator so it should be a little better.

@kappratiksha
kappratiksha merged commit aa59a6b into GoogleCloudPlatform:masterDec 12, 2022
@release-pleaserelease-pleaseBot mentioned this pull request Dec 12, 2022
@haizaar

Copy link
Copy Markdown

Good day,
Is this functionality ready for use? (Couldn't find any docs on that)

Does it supersede functions_framework.http?
I.e. does

@functions_framework.typed
def get(m: MyModel):
...

essentially replace

@functions_framework.http
def get(request: flask.Request):
m = MyModel.from_dict(request.get_json)
...

?

Will it work on Google Cloud Functions?

@haizaar

haizaar commented Nov 30, 2023

Copy link
Copy Markdown

Answering to myself:

  1. It work
  2. On Google Cloud Functions too
  3. Example:
from __future__ importannotationsfrompydanticimportBaseModelclassHelloRequest(BaseModel):
name: str@classmethoddeffrom_dict(cls, d: dict[str, Any]) ->HelloRequest:
returncls(**d)
defto_dict(self) ->dict[str, Any]:
returnself.model_dump()
@functions_framework.typeddefhello(hr: HelloRequest):
returnf"Hello, {hr.name}"

Well done guys!

@HKWinterhalter

Copy link
Copy Markdown
Contributor

@haizaar Thanks for trying it out and sharing an example! As you may have found, 'typed' is in additional feature and does not deprecate 'http'.

@red8888

Copy link
Copy Markdown

@haizaar Thanks for trying it out and sharing an example! As you may have found, 'typed' is in additional feature and does not deprecate 'http'.

Please explain what this means. How is it an "additional feature"?

Maybe I can rephrase, "If I want type hints do I just use @functions_framework.typed instead of @functions_framework.http and it will work the exact same?"

Because its not working for me:
image

Been years and this is still totally undocumented.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@kappratiksha@anniefu@haizaar@HKWinterhalter@red8888@jinglundong