Skip to content

Can't use custom field type #4189

@rafaellehmkuhl

Description

@rafaellehmkuhl

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.

Commit to Help

  • I commit to help with one of those options 👆

Example Code

from pydantic import BaseModel
from serial.tools.list_ports_linux import SysFS
from fastapi import FastAPI
from typing import List


class SysFSType(SysFS):
    @classmethod
    def __get_validators__(cls):
        yield cls.validate

    @classmethod
    def validate(cls, v):
        if not isinstance(v, SysFS):
            raise TypeError("SysFS required")
        return v

    def __repr__(self):
        return super().device


class Device(BaseModel):
    name: str
    port: SysFSType


app = FastAPI()

connected_devices = [Device(name="D1", port=SysFS("/dev/ttyACM1")), Device(name="D2", port=SysFS("/dev/ttyACM2"))]


@app.get("/devices/", response_model=List[Device])
async def get_devices():
    return connected_devices

Description

I'm trying to use the SysFS class (from pyserial) as a field type on a BaseModel class (through the SysFSType custom class, as explained here).

It all runs fine, even the GET route:

[
  {
    name: "D1",
    port: {
    device: "/dev/ttyACM1",
    name: "ttyACM1",
    description: "n/a",
    hwid: "n/a",
    vid: null,
    pid: null,
    serial_number: null,
    location: null,
    manufacturer: null,
    product: null,
    interface: null,
    usb_device_path: null,
    device_path: null,
    subsystem: null,
    usb_interface_path: null
  }
  },
  {
    name: "D2",
    port: {
    device: "/dev/ttyACM2",
    name: "ttyACM2",
    description: "n/a",
    hwid: "n/a",
    vid: null,
    pid: null,
    serial_number: null,
    location: null,
    manufacturer: null,
    product: null,
    interface: null,
    usb_device_path: null,
    device_path: null,
    subsystem: null,
    usb_interface_path: null
    }
  }
]

but as soon as I enter the swagger documentation, I get the following error:

rafael:~/rasp-home/dev/companion-docker$ uvicorn api:app --reload

INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [10227] using statreload
INFO:     Started server process [10229]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     127.0.0.1:48246 - "GET /devices/ HTTP/1.1" 200 OK
INFO:     127.0.0.1:48252 - "GET /docs HTTP/1.1" 200 OK
INFO:     127.0.0.1:48252 - "GET /openapi.json HTTP/1.1" 500 Internal Server Error
ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/dist-packages/uvicorn-0.13.4-py3.8.egg/uvicorn/protocols/http/h11_impl.py", line 396, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "/usr/local/lib/python3.8/dist-packages/uvicorn-0.13.4-py3.8.egg/uvicorn/middleware/proxy_headers.py", line 45, in __call__
    return await self.app(scope, receive, send)
  File "/usr/local/lib/python3.8/dist-packages/fastapi-0.63.0-py3.8.egg/fastapi/applications.py", line 199, in __call__
    await super().__call__(scope, receive, send)
  File "/usr/local/lib/python3.8/dist-packages/starlette-0.13.6-py3.8.egg/starlette/applications.py", line 111, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/usr/local/lib/python3.8/dist-packages/starlette-0.13.6-py3.8.egg/starlette/middleware/errors.py", line 181, in __call__
    raise exc from None
  File "/usr/local/lib/python3.8/dist-packages/starlette-0.13.6-py3.8.egg/starlette/middleware/errors.py", line 159, in __call__
    await self.app(scope, receive, _send)
  File "/usr/local/lib/python3.8/dist-packages/starlette-0.13.6-py3.8.egg/starlette/exceptions.py", line 82, in __call__
    raise exc from None
  File "/usr/local/lib/python3.8/dist-packages/starlette-0.13.6-py3.8.egg/starlette/exceptions.py", line 71, in __call__
    await self.app(scope, receive, sender)
  File "/usr/local/lib/python3.8/dist-packages/starlette-0.13.6-py3.8.egg/starlette/routing.py", line 566, in __call__
    await route.handle(scope, receive, send)
  File "/usr/local/lib/python3.8/dist-packages/starlette-0.13.6-py3.8.egg/starlette/routing.py", line 227, in handle
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.8/dist-packages/starlette-0.13.6-py3.8.egg/starlette/routing.py", line 41, in app
    response = await func(request)
  File "/usr/local/lib/python3.8/dist-packages/fastapi-0.63.0-py3.8.egg/fastapi/applications.py", line 152, in openapi
    return JSONResponse(self.openapi())
  File "/usr/local/lib/python3.8/dist-packages/fastapi-0.63.0-py3.8.egg/fastapi/applications.py", line 130, in openapi
    self.openapi_schema = get_openapi(
  File "/usr/local/lib/python3.8/dist-packages/fastapi-0.63.0-py3.8.egg/fastapi/openapi/utils.py", line 354, in get_openapi
    definitions = get_model_definitions(
  File "/usr/local/lib/python3.8/dist-packages/fastapi-0.63.0-py3.8.egg/fastapi/utils.py", line 24, in get_model_definitions
    m_schema, m_definitions, m_nested_models = model_process_schema(
  File "/usr/local/lib/python3.8/dist-packages/pydantic-1.8.2-py3.8.egg/pydantic/schema.py", line 548, in model_process_schema
    m_schema, m_definitions, nested_models = model_type_schema(
  File "/usr/local/lib/python3.8/dist-packages/pydantic-1.8.2-py3.8.egg/pydantic/schema.py", line 589, in model_type_schema
    f_schema, f_definitions, f_nested_models = field_schema(
  File "/usr/local/lib/python3.8/dist-packages/pydantic-1.8.2-py3.8.egg/pydantic/schema.py", line 241, in field_schema
    f_schema, f_definitions, f_nested_models = field_type_schema(
  File "/usr/local/lib/python3.8/dist-packages/pydantic-1.8.2-py3.8.egg/pydantic/schema.py", line 495, in field_type_schema
    f_schema, f_definitions, f_nested_models = field_singleton_schema(
  File "/usr/local/lib/python3.8/dist-packages/pydantic-1.8.2-py3.8.egg/pydantic/schema.py", line 863, in field_singleton_schema
    raise ValueError(f'Value not declarable with JSON Schema, field: {field}')
ValueError: Value not declarable with JSON Schema, field: name='port' type=SysFSType required=True

Am I using the custom field correctly?
I first assumed this was related to pydantic, but it's part of the code seems to run fine. It somehow seems to be the connection between the types and the JSON schema generated for it.

Operating System

Linux

Operating System Details

No response

FastAPI Version

0.63.0

Python Version

3.8.10

Additional Context

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions