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

Freeze time doesn't work with FastAPI + pydantic V2 #551

Open
Vivanov98 opened this issue Jul 1, 2024 · 2 comments
Open

Freeze time doesn't work with FastAPI + pydantic V2 #551

Vivanov98 opened this issue Jul 1, 2024 · 2 comments

Comments

@Vivanov98
Copy link

Vivanov98 commented Jul 1, 2024

It seems that since the latest FastAPI releases (when pydantic V2 became supported, which is fastapi==0.100 onwards), freezegun has stopped working with certain FastAPI features.

Upon creating a FastAPI endpoint with pydantic models which use datetime and possibly other related date types, where freezegun has been activated - the application fails due to a schema generation failure. The failure can also occur if you instantiate the app first, then activate freezegun, and make a request after (e.g. when running a test suite with an ASGI client)

There is a somewhat related discussion over in Pydantic about freezegun and pydantic being incompatible, though the fix mentioned doesn't seem to work for a FastAPI pytest case (the author has fixed it for a django pytest suite). I have also tried to patch the associated pydantic schema methods to no avail.

I thought it would be best to put the issue here, to determine how best to work around pydantic v2 with freezegun, as it seems that the patching that freezegun is doing may be incompatible with Pydantic.

Example

This is a simple script example to demonstrate the failure (a more realistic example would be a pytest test which is wrapped in a freezegun context and is calling fastAPI via some test ASGI client)

import datetime
import freezegun

from pydantic import BaseModel, ConfigDict


class Model(BaseModel):
    field_1: datetime.datetime

    model_config = ConfigDict(arbitrary_types_allowed=True)


with freezegun.freeze_time("2021-05-04T03:04:17"):

    from fastapi import FastAPI, Depends

    app = FastAPI()


    @app.get("/data")
    async def get(mm: Model = Depends(Model)) -> None:
        print(3)
        return None

Running this script causes the following failure:

Traceback (most recent call last):
  File "/home/vivanov/exp/apps/test_api/schema_2.py", line 20, in <module>
     @app.get("/data")
      ^^^^^^^^^^^^^^^^
  File "/home/vivanov/.virtualenvs/venv-test/lib/python3.11/site-packages/fastapi/routing.py", line 944, in decorator
    self.add_api_route(
  File "/home/vivanov/.virtualenvs/venv-test/lib/python3.11/site-packages/fastapi/routing.py", line 883, in add_api_route
     route = route_class(
            ^^^^^^^^^^^^
  File "/home/vivanov/.virtualenvs/venv-test/lib/python3.11/site-packages/fastapi/routing.py", line 513, in __init__
    self.dependant = get_dependant(path=self.path_format, call=self.endpoint)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/vivanov/.virtualenvs/venv-test/lib/python3.11/site-packages/fastapi/dependencies/utils.py", line 268, in get_dependant
    sub_dependant = get_param_sub_dependant(
                   ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/vivanov/.virtualenvs/venv-test/lib/python3.11/site-packages/fastapi/dependencies/utils.py", line 111, in get_param_sub_dependant
    return get_sub_dependant(
          ^^^^^^^^^^^^^^^^^^
 File "/home/vivanov/.virtualenvs/venv-test/lib/python3.11/site-packages/fastapi/dependencies/utils.py", line 147, in get_sub_dependant
    sub_dependant = get_dependant(
                    ^^^^^^^^^^^^^^
 File "/home/vivanov/.virtualenvs/venv-test/lib/python3.11/site-packages/fastapi/dependencies/utils.py", line 261, in get_dependant
    type_annotation, depends, param_field = analyze_param(
                                            ^^^^^^^^^^^^^^
  File "/home/vivanov/.virtualenvs/venv-test/lib/python3.11/site-packages/fastapi/dependencies/utils.py", line 444, in analyze_param
    field = create_response_field(
            ^^^^^^^^^^^^^^^^^^^^^^
  File "/home/vivanov/.virtualenvs/venv-test/lib/python3.11/site-packages/fastapi/utils.py", line 101, in create_response_field
    raise fastapi.exceptions.FastAPIError(
fastapi.exceptions.FastAPIError: Invalid args for response field! Hint: check that <class 'datetime.datetime'> is a valid Pydantic field type.
If you are using a return type annotation that is not a valid Pydantic field (e.g. Union[Response, dict, None]) you can disable generating the response model from the type annotation with the path operation decorator parameter response_model=None.
Read more: https://fastapi.tiangolo.com/tutorial/response-model/

The fastapi.exceptions.FastAPIError is raised from an underlying Pydantic exception which is more illuminating:

PydanticSchemaGenerationError("Unable to generate pydantic-core schema for <class 'datetime.datetime'>.
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.\n\n
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.")

Dev environment

To replicate the above failure:

Cpython: 3.11.8
Ubuntu: 22.04.4 LTS
Kernel: 6.5.0-41-generic

Python reqs:

pydantic==2.7.4
pydantic-settings==2.2.1
pydantic_core==2.18.4
freezegun==1.5.1  # latest
fastapi==0.111.0

(NOTE
Can confirm that it works with the last fastAPI version prior to pydantic v2 support - 0.99.0

fastapi==0.99.0
pydantic==1.10.17

)

@nfarahmand
Copy link

I'm running into this problem too. @Vivanov98 did you find a solution by chance?

@takanoria0612
Copy link

I've run into a similar issue. It seems like Pydantic does not work well with the datetime type.

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

No branches or pull requests

3 participants