Skip to content

fix: preserve user function type through http and cloud_event decorators#426

Open
64johnlee wants to merge 1 commit into
GoogleCloudPlatform:mainfrom
64johnlee:fix/decorator-type-preservation
Open

fix: preserve user function type through http and cloud_event decorators#426
64johnlee wants to merge 1 commit into
GoogleCloudPlatform:mainfrom
64johnlee:fix/decorator-type-preservation

Conversation

@64johnlee
Copy link
Copy Markdown

Problem

The @functions_framework.http and @functions_framework.cloud_event decorators had fixed signatures that erased the user function's type:

def http(func: HTTPFunction) -> HTTPFunction: ...

After decoration, mypy and pyright saw the function as the generic HTTPFunction = Callable[[flask.Request], ResponseReturnValue] alias, losing all specific type information (custom return types, subclassed request types, etc.).

Example that failed type checking before this fix:

@functions_framework.http
def handle(request: flask.Request) -> flask.Response:
    return flask.Response("ok")

reveal_type(handle)
# Before: HTTPFunction (generic alias, return type lost)
# After:  (request: flask.Request) -> flask.Response (preserved)

Fix

Introduce bounded TypeVars for each decorator so the user's exact callable type flows through unchanged:

_HTTPFunctionT = TypeVar("_HTTPFunctionT", bound=HTTPFunction)

def http(func: _HTTPFunctionT) -> _HTTPFunctionT: ...

The # type: ignore[return-value] on the return is the standard pattern for typed decorator implementations — mypy cannot verify that the inner wrapper(*args, **kwargs) satisfies the TypeVar bound, but functools.wraps guarantees the runtime behaviour is correct.

Changes

  • src/functions_framework/__init__.py
    • Add TypeVar to typing imports
    • Add _CloudEventFunctionT and _HTTPFunctionT TypeVars
    • Update cloud_event() and http() signatures to use TypeVars

Backward Compatibility

Purely additive from a runtime perspective — no behaviour changes. Type checkers now get more precise information rather than less.

Fixes #361

🤖 Generated with Claude Code

The http() and cloud_event() decorators previously had fixed signatures
that erased the user function's specific type, replacing it with the
generic HTTPFunction / CloudEventFunction alias. This caused type
checkers (mypy, pyright) to lose the decorated function's type, breaking
call-site validation for functions with type annotations.

Fix: introduce _HTTPFunctionT and _CloudEventFunctionT TypeVars bound to
the respective aliases. The decorators now return the same TypeVar they
receive, so the user's original callable type flows through unmodified.
The # type: ignore[return-value] suppresses the unavoidable mismatch
between the inner wrapper(*args, **kwargs) and the TypeVar -- this is
the standard pattern for typed decorator implementations.

Before:
  @functions_framework.http
  def handle(request: flask.Request) -> str:
      ...
  # type of `handle` was HTTPFunction, losing return type annotation

After:
  # type of `handle` is (request: flask.Request) -> str -- preserved

Fixes GoogleCloudPlatform#361

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@google-cla
Copy link
Copy Markdown

google-cla Bot commented Jun 6, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

Sign up for free to 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.

Functions defined with @functions_framework.http or @functions_framework.typed do not support type hints

1 participant