sanic-org / sanic-ext

Extended Sanic functionality
https://sanic.dev/en/plugins/sanic-ext/getting-started.html
MIT License
50 stars 34 forks source link

[Bug] Can't use `pydantic>=2.0` model in OpenAPI definition #231

Open liamcoatman opened 11 months ago

liamcoatman commented 11 months ago

Minimal example:

from pydantic import BaseModel
from sanic import Sanic
from sanic.response import text
from sanic_ext import openapi

app = Sanic("MyHelloWorldApp")

class RequestSchema(BaseModel):
    foo: str

@app.get("/")
@openapi.definition(
    body=openapi.definitions.RequestBody({"application/json": RequestSchema}),
)
async def hello_world(request):
    return text("Hello, world.")

if __name__ == "__main__":
    app.run(host="0.0.0.0", debug=True)

This works as expected with pydantic==1.10.12 but after upgrading to pydantic==2.3.0 I get

/opt/python/lib/python3.10/site-packages/sanic_ext/extensions/openapi/types.py:333: PydanticDeprecatedSince20: The `schema` method is deprecated; use `model_json_schema` instead. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.3/migration/
  extra = value.schema()["properties"]
Traceback (most recent call last):
  File "/workspace/wsgi.py", line 17, in <module>
    async def hello_world(request):
  File "/opt/python/lib/python3.10/site-packages/sanic_ext/extensions/openapi/openapi.py", line 423, in inner
    func = glbl["body"](**kwargs)(func)
  File "/opt/python/lib/python3.10/site-packages/sanic_ext/extensions/openapi/openapi.py", line 238, in inner
    OperationStore()[handler].body(body_content, **params)
  File "/opt/python/lib/python3.10/site-packages/sanic_ext/extensions/openapi/builders.py", line 93, in body
    self.requestBody = RequestBody.make(content, **kwargs)
  File "/opt/python/lib/python3.10/site-packages/sanic_ext/extensions/openapi/definitions.py", line 175, in make
    return RequestBody(MediaType.all(content), **kwargs)
  File "/opt/python/lib/python3.10/site-packages/sanic_ext/extensions/openapi/definitions.py", line 111, in all
    return {x: MediaType.make(v) for x, v in media_types.items()}
  File "/opt/python/lib/python3.10/site-packages/sanic_ext/extensions/openapi/definitions.py", line 111, in <dictcomp>
    return {x: MediaType.make(v) for x, v in media_types.items()}
  File "/opt/python/lib/python3.10/site-packages/sanic_ext/extensions/openapi/definitions.py", line 103, in make
    return MediaType(Schema.make(value))
  File "/opt/python/lib/python3.10/site-packages/sanic_ext/extensions/openapi/types.py", line 227, in make
    return Object.make(value, **kwargs)
  File "/opt/python/lib/python3.10/site-packages/sanic_ext/extensions/openapi/types.py", line 367, in make
    {
  File "/opt/python/lib/python3.10/site-packages/sanic_ext/extensions/openapi/types.py", line 368, in <dictcomp>
    k: Schema.make(v, **extra.get(k, {}))
  File "/opt/python/lib/python3.10/site-packages/sanic_ext/extensions/openapi/types.py", line 205, in make
    return Object.make(value, **kwargs)
  File "/opt/python/lib/python3.10/site-packages/sanic_ext/extensions/openapi/types.py", line 367, in make
    {
  File "/opt/python/lib/python3.10/site-packages/sanic_ext/extensions/openapi/types.py", line 368, in <dictcomp>
    k: Schema.make(v, **extra.get(k, {}))
  File "/opt/python/lib/python3.10/site-packages/sanic_ext/extensions/openapi/types.py", line 227, in make
    return Object.make(value, **kwargs)
  File "/opt/python/lib/python3.10/site-packages/sanic_ext/extensions/openapi/types.py", line 367, in make
    {
  File "/opt/python/lib/python3.10/site-packages/sanic_ext/extensions/openapi/types.py", line 368, in <dictcomp>
    k: Schema.make(v, **extra.get(k, {}))
  File "/opt/python/lib/python3.10/site-packages/sanic_ext/extensions/openapi/types.py", line 142, in make
    return Schema.make(filtered[0], **kwargs)
  File "/opt/python/lib/python3.10/site-packages/sanic_ext/extensions/openapi/types.py", line 227, in make
    return Object.make(value, **kwargs)
  File "/opt/python/lib/python3.10/site-packages/sanic_ext/extensions/openapi/types.py", line 328, in make
    if is_pydantic(value):
  File "/opt/python/lib/python3.10/site-packages/sanic_ext/utils/typing.py", line 49, in is_pydantic
    issubclass(model, BaseModel) or hasattr(model, "__pydantic_model__")
  File "/opt/python/lib/python3.10/abc.py", line 123, in __subclasscheck__
    return _abc_subclasscheck(cls, subclass)
TypeError: issubclass() arg 1 must be a class
ahopkins commented 11 months ago

Correct, they made some breaking changes in their api. 👎

If you're interested in helping out, would be happy for you to make a PR. 😎

shaoerkuai commented 3 days ago

@ahopkins Any instructions? I want to try it.

ahopkins commented 1 day ago

Sure thing. I want to get this in for release at the end of this month.