Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug: APIGatewayRestResolver with enable_validation=True and custom Response class type hint fails #5038

Closed
RynoM opened this issue Aug 21, 2024 · 4 comments
Assignees
Labels
documentation Improvements or additions to documentation event_handlers not-a-bug openapi-schema

Comments

@RynoM
Copy link

RynoM commented Aug 21, 2024

Expected Behaviour

I expect the type hint to not have any impact on the code running.

Current Behaviour

Adding a type hint to one of the routes results in an error.

Code snippet

from aws_lambda_powertools.event_handler import APIGatewayRestResolver, Response


app = APIGatewayRestResolver(enable_validation=True, debug=True)


@app.get("/")
def get_result() -> Response:
    print("We did some work!")
    # return {}
    return Response(status_code=200, body={"message": "Hello, World!"})


def event_handler(event: dict, context) -> dict:
    return app.resolve(event, context)


print(event_handler({"resource": "/", "path": "/", "httpMethod": "GET", "body": "Hello from Lambda!"}, None))

Possible Solution

Further description of the error is here in the pydantic docs: https://docs.pydantic.dev/latest/concepts/dataclasses/#use-custom-types

Steps to Reproduce

Adding return type annotation to get_result will break it, removing it works.

Powertools for AWS Lambda (Python) version

latest

AWS Lambda function runtime

3.9

Packaging format used

Lambda Layers

Debugging logs

Traceback (most recent call last):
  File "/opt/python/lib/python3.9/site-packages/pydantic/type_adapter.py", line 277, in _init_core_attrs
    self._core_schema = _getattr_no_parents(self._type, '__pydantic_core_schema__')
  File "/opt/python/lib/python3.9/site-packages/pydantic/type_adapter.py", line 119, in _getattr_no_parents
    raise AttributeError(attribute)
AttributeError: __pydantic_core_schema__

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/opt/python/lib/python3.9/site-packages/aws_lambda_powertools/event_handler/api_gateway.py", line 2316, in _call_route
    route(router_middlewares=self._router_middlewares, app=self, route_arguments=route_arguments),
  File "/opt/python/lib/python3.9/site-packages/aws_lambda_powertools/event_handler/api_gateway.py", line 450, in __call__
    return self._middleware_stack(app)
  File "/opt/python/lib/python3.9/site-packages/aws_lambda_powertools/event_handler/api_gateway.py", line 1436, in __call__
    return self.current_middleware(app, self.next_middleware)
  File "/opt/python/lib/python3.9/site-packages/aws_lambda_powertools/event_handler/middlewares/base.py", line 121, in __call__
    return self.handler(app, next_middleware)
  File "/opt/python/lib/python3.9/site-packages/aws_lambda_powertools/event_handler/middlewares/openapi_validation.py", line 80, in handler
    route.dependant.path_params,
  File "/opt/python/lib/python3.9/site-packages/aws_lambda_powertools/event_handler/api_gateway.py", line 499, in dependant
    self._dependant = get_dependant(path=self.openapi_path, call=self.func, responses=self.responses)
  File "/opt/python/lib/python3.9/site-packages/aws_lambda_powertools/event_handler/openapi/dependant.py", line 207, in get_dependant
    _add_return_annotation(dependant, endpoint_signature)
  File "/opt/python/lib/python3.9/site-packages/aws_lambda_powertools/event_handler/openapi/dependant.py", line 238, in _add_return_annotation
    param_field = analyze_param(
  File "/opt/python/lib/python3.9/site-packages/aws_lambda_powertools/event_handler/openapi/params.py", line 965, in analyze_param
    field = _create_model_field(field_info, type_annotation, param_name, is_path_param)
  File "/opt/python/lib/python3.9/site-packages/aws_lambda_powertools/event_handler/openapi/params.py", line 1098, in _create_model_field
    return create_response_field(
  File "/opt/python/lib/python3.9/site-packages/aws_lambda_powertools/event_handler/openapi/params.py", line 1067, in create_response_field
    return ModelField(**kwargs)  # type: ignore[arg-type]
  File "<string>", line 6, in __init__
  File "/opt/python/lib/python3.9/site-packages/aws_lambda_powertools/event_handler/openapi/compat.py", line 86, in __post_init__
    self._type_adapter: TypeAdapter[Any] = TypeAdapter(
  File "/opt/python/lib/python3.9/site-packages/pydantic/type_adapter.py", line 264, in __init__
    self._init_core_attrs(rebuild_mocks=False)
  File "/opt/python/lib/python3.9/site-packages/pydantic/type_adapter.py", line 142, in wrapped
    return func(self, *args, **kwargs)
  File "/opt/python/lib/python3.9/site-packages/pydantic/type_adapter.py", line 284, in _init_core_attrs
    self._core_schema = _get_schema(self._type, config_wrapper, parent_depth=self._parent_depth)
  File "/opt/python/lib/python3.9/site-packages/pydantic/type_adapter.py", line 102, in _get_schema
    schema = gen.generate_schema(type_)
  File "/opt/python/lib/python3.9/site-packages/pydantic/_internal/_generate_schema.py", line 512, in generate_schema
    schema = self._generate_schema_inner(obj)
  File "/opt/python/lib/python3.9/site-packages/pydantic/_internal/_generate_schema.py", line 768, in _generate_schema_inner
    return self._annotated_schema(obj)
  File "/opt/python/lib/python3.9/site-packages/pydantic/_internal/_generate_schema.py", line 1822, in _annotated_schema
    schema = self._apply_annotations(source_type, annotations)
  File "/opt/python/lib/python3.9/site-packages/pydantic/_internal/_generate_schema.py", line 1890, in _apply_annotations
    schema = get_inner_schema(source_type)
  File "/opt/python/lib/python3.9/site-packages/pydantic/_internal/_schema_generation_shared.py", line 83, in __call__
    schema = self._handler(source_type)
  File "/opt/python/lib/python3.9/site-packages/pydantic/_internal/_generate_schema.py", line 1972, in new_handler
    schema = metadata_get_schema(source, get_inner_schema)
  File "/opt/python/lib/python3.9/site-packages/pydantic/_internal/_generate_schema.py", line 1968, in <lambda>
    lambda source, handler: handler(source)
  File "/opt/python/lib/python3.9/site-packages/pydantic/_internal/_schema_generation_shared.py", line 83, in __call__
    schema = self._handler(source_type)
  File "/opt/python/lib/python3.9/site-packages/pydantic/_internal/_generate_schema.py", line 1871, in inner_handler
    schema = self._generate_schema_inner(obj)
  File "/opt/python/lib/python3.9/site-packages/pydantic/_internal/_generate_schema.py", line 789, in _generate_schema_inner
    return self.match_type(obj)
  File "/opt/python/lib/python3.9/site-packages/pydantic/_internal/_generate_schema.py", line 875, in match_type
    return self._unknown_type_schema(obj)
  File "/opt/python/lib/python3.9/site-packages/pydantic/_internal/_generate_schema.py", line 415, in _unknown_type_schema
    raise PydanticSchemaGenerationError(
pydantic.errors.PydanticSchemaGenerationError: Unable to generate pydantic-core schema for <class 'aws_lambda_powertools.event_handler.api_gateway.Response'>. Set `arbitrary_types_allowed=True` in the model_config to ignore this error or implement `__get_pydantic_core_schema__` on your type to fully support it.

If you got this error by calling handler(<some type>) within `__get_pydantic_core_schema__` then you likely need to call `handler.generate_schema(<some type>)` since we do not call `__get_pydantic_core_schema__` on `<some type>` otherwise to avoid infinite recursion.

For further information visit https://errors.pydantic.dev/2.8/u/schema-for-unknown-type
@RynoM RynoM added bug Something isn't working triage Pending triage from maintainers labels Aug 21, 2024
Copy link

boring-cyborg bot commented Aug 21, 2024

Thanks for opening your first issue here! We'll come back to you as soon as we can.
In the meantime, check out the #python channel on our Powertools for AWS Lambda Discord: Invite link

@leandrodamascena
Copy link
Contributor

Hi @RynoM! Thanks for reporting this.

Actually, this is not a bug, it's just the way Pydantic works. Pydantic can't use TypeAdapter with generic Python classes, and that's right, it really shouldn't. Response is a generic class to make it easier to create responses for the proxy-in-the-middle (API Gateway API, ALB, Lambda URL and others).

In this case, what matters in the Response class is the body, which you are defining as json, so you need to annotate the function with a Response-specific type. And try adding the content_type in the Response object, so that we can serialize the expected content type accordingly.

from aws_lambda_powertools.event_handler import APIGatewayRestResolver, Response
from typing import Dict

app = APIGatewayRestResolver(enable_validation=True, debug=True)

@app.get("/")
def get_result() -> Response[Dict]:
    print("We did some work!")
    # return {}
    return Response(status_code=200, content_type="application/json", body={"message": "Hello, World!"})


def lambda_handler(event: dict, context) -> dict:
    return app.resolve(event, context)

Please try this and let me know it this worked.

@leandrodamascena leandrodamascena added event_handlers not-a-bug openapi-schema and removed bug Something isn't working triage Pending triage from maintainers labels Aug 21, 2024
@leandrodamascena leandrodamascena moved this from Triage to Pending customer in Powertools for AWS Lambda (Python) Aug 21, 2024
@leandrodamascena leandrodamascena added the documentation Improvements or additions to documentation label Aug 21, 2024
@RynoM
Copy link
Author

RynoM commented Aug 21, 2024

Thank you for the fast and clear response! This works indeed. Of course after asking the question, re-reading the docs I find the small collapsed note section "Using Response with data validation?".

@RynoM RynoM closed this as completed Aug 21, 2024
@github-project-automation github-project-automation bot moved this from Pending customer to Coming soon in Powertools for AWS Lambda (Python) Aug 21, 2024
Copy link
Contributor

⚠️COMMENT VISIBILITY WARNING⚠️

This issue is now closed. Please be mindful that future comments are hard for our team to see.

If you need more assistance, please either tag a team member or open a new issue that references this one.

If you wish to keep having a conversation with other community members under this issue feel free to do so.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation event_handlers not-a-bug openapi-schema
Projects
Status: Coming soon
Development

No branches or pull requests

2 participants