diff --git a/fastapi/encoders.py b/fastapi/encoders.py index cb94a91a7..ae4794bae 100644 --- a/fastapi/encoders.py +++ b/fastapi/encoders.py @@ -1,4 +1,5 @@ from enum import Enum +from pathlib import PurePath from types import GeneratorType from typing import Any, Callable, Dict, List, Set, Tuple, Union @@ -73,6 +74,8 @@ def jsonable_encoder( ) if isinstance(obj, Enum): return obj.value + if isinstance(obj, PurePath): + return str(obj) if isinstance(obj, (str, int, float, type(None))): return obj if isinstance(obj, dict): diff --git a/tests/test_jsonable_encoder.py b/tests/test_jsonable_encoder.py index 97fbf6e6d..f94771aae 100644 --- a/tests/test_jsonable_encoder.py +++ b/tests/test_jsonable_encoder.py @@ -1,9 +1,10 @@ from datetime import datetime, timezone from enum import Enum +from pathlib import PurePath, PurePosixPath, PureWindowsPath import pytest from fastapi.encoders import jsonable_encoder -from pydantic import BaseModel, ValidationError +from pydantic import BaseModel, ValidationError, create_model try: from pydantic import Field @@ -69,6 +70,19 @@ class ModelWithAlias(BaseModel): foo: str = Field(..., alias="Foo") +@pytest.fixture( + name="model_with_path", params=[PurePath, PurePosixPath, PureWindowsPath] +) +def fixture_model_with_path(request): + class Config: + arbitrary_types_allowed = True + + ModelWithPath = create_model( + "ModelWithPath", path=(request.param, ...), __config__=Config + ) + return ModelWithPath(path=request.param("/foo", "bar")) + + def test_encode_class(): person = Person(name="Foo") pet = Pet(owner=person, name="Firulais") @@ -120,3 +134,11 @@ def test_custom_encoders(): instance, custom_encoder={safe_datetime: lambda o: o.isoformat()} ) assert encoded_instance["dt_field"] == instance.dt_field.isoformat() + + +def test_encode_model_with_path(model_with_path): + if isinstance(model_with_path.path, PureWindowsPath): + expected = "\\foo\\bar" + else: + expected = "/foo/bar" + assert jsonable_encoder(model_with_path) == {"path": expected}