Describe the bug
If the api response contains fields of type pathlib.Path, an error is raised:
ValueError: [KeyError(<class 'pathlib.PosixPath'>,), TypeError("'PosixPath' object is not iterable",), TypeError('vars() argument must have __dict__ attribute',)]
To Reproduce
I wrote a test for this bug which fails with the above ^ error.
#tests/test_serialize_path.py
from pathlib import Path
from pydantic import BaseModel
from starlette.testclient import TestClient
from fastapi import FastAPI
class ContainsPathProperty(BaseModel):
path: Path
app = FastAPI()
@app.get("/", response_model=ContainsPathProperty)
async def get_main():
obj = ContainsPathProperty(path=Path('/some/random/path'))
return obj
client = TestClient(app)
def test_serialize_path():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {'path': '/some/random/path'}
Expected behavior
The test should pass, which means a 200 response is returned and the response is:
{
"path": "/some/random/path"
}
Environment:
-
OS: Linux / Windows
-
FastAPI Version 0.38.1
-
Python version 3.6.9
Additional context
Solution 1
A possible fix would be importing and using the encoder from pydantic which knows to encode Path objects:
# pydantic/json.py
def pydantic_encoder(obj: Any) -> Any:
......
elif isinstance(obj, Path):
return str(obj)
......
The Fastapi's jsonable_encoder(....) would be changed from this:
# fastapi/encoder.py
....
from pydantic.json import ENCODERS_BY_TYPE
....
def jsonable_encoder(....) -> Any:
.......
try:
if custom_encoder and type(obj) in custom_encoder:
encoder = custom_encoder[type(obj)]
else:
encoder = ENCODERS_BY_TYPE[type(obj)]
return encoder(obj)
except KeyError as e:
.....
......
to this:
# fastapi/encoder.py
....
from pydantic.json import pydantic_encoder
....
def jsonable_encoder(....) -> Any:
.......
try:
if custom_encoder and type(obj) in custom_encoder:
encoder = custom_encoder[type(obj)]
else:
encoder = pydantic_encoder
return encoder(obj)
except KeyError as e:
.....
......
The test passes but pydantic_encoder() has a downside: the string obtained by str(Path(...)) is not the same on Windows (eg: \some\random\path) vs Linux (/some/random/path).
** Solution 2 **
As I think it is very important that a Fastapi app return the same responses no matter the platform on which it's deployed, another solution is handling the pathlib.Path encoding directly in Fastapi's jsonable_encoder(), using Path(...).as_posix() instead of str(Path(...)) like:
# fastapi/encoders.py
def jsonable_encoder(.....) -> Any:
......
if isinstance(obj, Path):
return obj.as_posix()
.......
What do you guys think?
Describe the bug
If the api response contains fields of type
pathlib.Path, an error is raised:To Reproduce
I wrote a test for this bug which fails with the above ^ error.
Expected behavior
The test should pass, which means a 200 response is returned and the response is:
{ "path": "/some/random/path" }Environment:
OS: Linux / Windows
FastAPI Version 0.38.1
Python version 3.6.9
Additional context
Solution 1
A possible fix would be importing and using the encoder from
pydanticwhich knows to encodePathobjects:The Fastapi's
jsonable_encoder(....)would be changed from this:to this:
The test passes but
pydantic_encoder()has a downside: the string obtained bystr(Path(...))is not the same on Windows (eg:\some\random\path) vs Linux (/some/random/path).** Solution 2 **
As I think it is very important that a Fastapi app return the same responses no matter the platform on which it's deployed, another solution is handling the
pathlib.Pathencoding directly in Fastapi'sjsonable_encoder(), usingPath(...).as_posix()instead ofstr(Path(...))like:What do you guys think?