Skip to content

Bug: Incomplete validation of annotated parameter types (3.21.0 regression) #7600

@rtandy

Description

@rtandy

Expected Behaviour

Annotated parameter types using annotated_types should be validated by Pydantic. This used to work, but regressed starting from 3.21.0. Types are still validated, but constraint annotations are not.

I thought #7227 would have enabled using Pydantic Field annotations for this use case, and tried to switch to that, but that also doesn't work (in any Powertools version). Should I report this separately (perhaps as a feature request)?

Current Behaviour

Parameter types are validated, but constraints expressed using annotated_types are not enforced. Handler functions can be called with invalid parameter values.

Code snippet

from http import HTTPStatus
from typing import Annotated

from aws_lambda_powertools.event_handler import APIGatewayHttpResolver
from aws_lambda_powertools.event_handler.openapi.params import Body, Query

import pytest

# aws-lambda-powertools 3.20.0: ok
# aws-lambda-powertools 3.21.0 and 3.22.0: fail (value outside of range allowed)
from annotated_types import Interval
ConstrainedInt = Annotated[int, Interval(ge=0, le=100)]

# aws-lambda-powertools 3.20.0 and 3.21.0 and 3.22.0: error (Only one FieldInfo can be used per parameter)
#from pydantic.fields import Field
#ConstrainedInt = Annotated[int, Field(ge=0, le=100)]

app = APIGatewayHttpResolver(enable_validation=True)

@app.post('/body')
def handle_body(value: Annotated[ConstrainedInt, Body()]) -> None:
    assert isinstance(value, int)
    assert value >= 0
    assert value <= 100

@app.post('/query')
def handle_body(value: Annotated[ConstrainedInt, Query()]) -> None:
    assert isinstance(value, int)
    assert value >= 0
    assert value <= 100

def handler(event, context):
    return app.resolve(event, context)

params = [
    (0, HTTPStatus.OK),
    (50, HTTPStatus.OK),
    (100, HTTPStatus.OK),
    (-1, HTTPStatus.UNPROCESSABLE_ENTITY),
    (101, HTTPStatus.UNPROCESSABLE_ENTITY),
    ('not an int', HTTPStatus.UNPROCESSABLE_ENTITY),
]

@pytest.mark.parametrize('value,expected_status', params)
def test_handle_body(value, expected_status):
    event = {
        'rawPath': '/body',
        'requestContext': {
            'http': {
                'method': 'POST'
            },
            'stage': '$default'
        },
        'body': str(value)
    }
    response = handler(event, object())
    assert expected_status == response['statusCode']

@pytest.mark.parametrize('value,expected_status', params)
def test_handle_query(value, expected_status):
    event = {
        'rawPath': '/query',
        'requestContext': {
            'http': {
                'method': 'POST'
            },
            'stage': '$default'
        },
        'queryStringParameters': {
            'value': str(value)
        }
    }
    response = handler(event, object())
    assert expected_status == response['statusCode']

Possible Solution

No response

Steps to Reproduce

Run code snippet: pytest test_annotated_types.py

Powertools for AWS Lambda (Python) version

3.21.0, 3.22.0

AWS Lambda function runtime

3.11

Packaging format used

Lambda Layers

Debugging logs

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    Status

    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions