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

Returning dataclass with pydantic-compatible fields (e.g. datetime) fails for FastAPI >= 0.67 #3608

Closed
9 tasks done
himbeles opened this issue Jul 26, 2021 · 6 comments
Closed
9 tasks done
Labels
answered bug Something isn't working reviewed

Comments

@himbeles
Copy link
Contributor

himbeles commented Jul 26, 2021

First check

  • I added a very descriptive title to this issue.
  • I used the GitHub search to find a similar issue and didn't find it.
  • I searched the FastAPI documentation, with the integrated search.
  • I already searched in Google "How to X in FastAPI" and didn't find any information.
  • I already read and followed all the tutorial in the docs and didn't find an answer.
  • I already checked if it is not related to FastAPI but to Pydantic.
  • I already checked if it is not related to FastAPI but to Swagger UI.
  • I already checked if it is not related to FastAPI but to ReDoc.
  • After submitting this, I commit to one of:
    • Read open issues with questions until I find 2 issues where I can help someone and add a comment to help there.
    • I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.
    • Implement a Pull Request for a confirmed bug.

Example & Description

I have encountered an issue when returning dataclass responses with some pydantic-compatible (but incompatible with json encoder of Python standard library) fields, e.g. datetime objects or pathlib file paths.

Here's a self-contained, minimal, reproducible, example where I have added a datetime field to the Item class in tests/test_serialize_response_dataclass.py:

from datetime import datetime
from fastapi import FastAPI
from fastapi.testclient import TestClient
from dataclasses import dataclass

app = FastAPI()


@dataclass
class Item:
    name: str
    date: datetime


@app.get("/items/no-response-model/object")
def get_no_response_model_object():
    return Item(name="object", date=datetime(2021, 7, 26))


client = TestClient(app)


def test_no_response_model_object():
    response = client.get("/items/no-response-model/object")
    response.raise_for_status()
    assert response.json() == {
        "name": "object",
        "date": datetime(2021, 7, 26).isoformat(),
    }

I would

  • Run the test code snippet with Pydantic 0.67
  • I would expect it pass because datetime objects are compatible with Pydantic models
  • It fails with the following stack trace:
Fail: TypeError: Object of type datetime is not JSON serializablepytest(./tests/test_serialize_response_dataclass_minimal_failing.py::test_no_response_model_object)
test_serialize_response_dataclass_minimal_failing.py(23, 5): test_no_response_model_object
test_serialize_response_dataclass_minimal_failing.py(24, 5): response = client.get("/items/no-response-model/object")
sessions.py(555, 9): return self.request('GET', url, **kwargs)
testclient.py(415, 9): return super().request(
sessions.py(542, 9): resp = self.send(prep, **send_kwargs)
sessions.py(655, 9): r = adapter.send(request, **kwargs)
testclient.py(243, 17): raise exc from None
testclient.py(240, 13): loop.run_until_complete(self.app(scope, receive, send))
base_events.py(642, 9): return future.result()
applications.py(199, 17): await super().__call__(scope, receive, send)
applications.py(112, 9): await self.middleware_stack(scope, receive, send)
errors.py(181, 13): raise exc from None
errors.py(159, 13): await self.app(scope, receive, _send)
exceptions.py(82, 17): raise exc from None
exceptions.py(71, 13): await self.app(scope, receive, sender)
routing.py(580, 17): await route.handle(scope, receive, send)
routing.py(241, 13): await self.app(scope, receive, send)
routing.py(52, 13): response = await func(request)
routing.py(243, 13): response = actual_response_class(response_data, **response_args)
responses.py(53, 9): self.body = self.render(content)
responses.py(161, 9): return json.dumps(
__init__.py(234, 5): return cls(
encoder.py(199, 9): chunks = self.iterencode(o, _one_shot=True)
encoder.py(257, 9): return _iterencode(o, 0)
encoder.py(179, 9): raise TypeError(f'Object of type {o.__class__.__name__} '

I have created a PR #3607 with an attempt to fix the issue and extend the existing tests in tests/test_serialize_response_dataclass.py.
Without this PR, all tests in this updated file fail.

Note that the example above and the extended tests also pass with a pre 0.67 version of FastApi (before official dataclass response support).

Environment

  • OS: [e.g. Linux / Windows / macOS]: macOS
  • FastAPI Version [e.g. 0.3.0]: 0.67.0
  • Python version: 3.9.6
@himbeles himbeles added the question Question or problem label Jul 26, 2021
@himbeles himbeles changed the title Returning dataclass responses with pydantic-compatible fields (e.g. datetime) fails Returning dataclass with pydantic-compatible fields (e.g. datetime) fails for FastAPI >= 0.67 Aug 3, 2021
@himbeles
Copy link
Contributor Author

himbeles commented Aug 3, 2021

It also fails, when specifying the response model in the route:

from datetime import datetime
from fastapi import FastAPI
from fastapi.testclient import TestClient
from dataclasses import dataclass

app = FastAPI()

@dataclass
class Item:
    name: str
    date: datetime


@app.get("/item", response_model=Item)
def get_object():
    return Item(name="object", date=datetime(2021, 7, 26))


client = TestClient(app)

def test_object():
    response = client.get("/item")
    response.raise_for_status()
    assert response.json() == {
        "name": "object",
        "date": datetime(2021, 7, 26).isoformat(),
    }

@stapetro
Copy link

Hello,

I've added 1 more test case in another issue for proving the change introduced in 0.67 version broke the contract of jsonable_encoder.

@gandhis1
Copy link

This is a pretty important bug for me, but the fix seems to not have any traction.

@dragoon
Copy link

dragoon commented Aug 12, 2022

This is also important for me, and the PR already exists, let me know if you need any help on this.

@torrell8
Copy link

This is very important to me aswell

@tiangolo tiangolo added bug Something isn't working and removed question Question or problem labels Aug 26, 2022
@tiangolo
Copy link
Owner

Thanks for the complete report! And thanks for the fix @himbeles in #3607

This will be available in the next release, FastAPI 0.81.0 🚀 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
answered bug Something isn't working reviewed
Projects
None yet
Development

No branches or pull requests

6 participants