marshmallow-code / marshmallow-code/apispec
Incorrect handling of Union types
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 1.2k
- Forks
- 202
- Avg merge
- 3h 38m
- Merged PRs (30d)
- 3
Description
It seems that the API spec is not properly generated when using Union typehints.
The below snippet is on python 3.10 and 3.11
from typing import Literal
from apispec import APISpec
from apispec.ext.marshmallow import MarshmallowPlugin
from marshmallow_dataclass import dataclass
class BaseItemMixin:
name: str
@dataclass
class BaseItem(BaseItemMixin):
pass
@dataclass
class Food(BaseItem):
label: str
name: Literal["food"] = "food"
@dataclass
class Drink(BaseItem):
size: int
name: Literal["drink"] = "drink"
@dataclass
class MainSchema:
id: int
name: str
items: list[Food | Drink]
spec = APISpec(
title="API",
version="1.0.0",
openapi_version="3.0.2",
plugins=[(MarshmallowPlugin())],
)
spec.path(
"/endpoint",
operations={
"post": {
"parameters": [
{
"name": "Args",
"schema": MainSchema.Schema(),
"in": "body",
}
],
"responses": {
"200": {
"content": {"application/json": {}},
"description": "OK",
},
},
}
},
)
if __name__ == "__main__":
print(spec.to_yaml())
The outputted yaml contains the schema for Main as:
components:
schemas:
Main:
type: object
properties:
name:
type: string
id:
type: integer
items:
type: array
items: {}
required:
- id
- items
- name
Items is correctly marked as an array, but the items is not correct. I'd expect oneOf being used here (although i'm not 100%).
The same happens using item: Food | Drink.
I believe the issue is within this library, and not marshmallow_dataclass, as i'm able to deserialise serialise with the schemas without issue
data = MainSchema(
id=1,
name="test",
items=[
Food(name="food", label="test"),
Drink(name="drink", size=1),
],
)
data_as_dict = MainSchema.Schema().dumps(data)
loaded_data = MainSchema.Schema().loads(data_as_dict)
print("original", data)
print("deserialised", loaded_data)
print("serialised", data_as_dict)
# original MainSchema(id=1, name='test', items=[Food(label='test', name='food'), Drink(size=1, name='drink')])
# deserialised MainSchema(id=1, name='test', items=[Food(label='test', name='food'), Drink(size=1, name='drink')])
# serialised {"id": 1, "items": [{"label": "test", "name": "food"}, {"size": 1, "name": "drink"}], "name": "test"}
Note: I've also tried without the inheritance involved, and the result is the same.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the APISpec and MarshmallowPlugin entry points in the reproduction, focusing on how MainSchema.Schema() is converted for the Union-typed items field. Run the provided Python example and add coverage for both list[Food | Drink] and Food | Drink; done means the generated OpenAPI schema represents the alternatives instead of leaving items empty.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- openapi, python
- Domain
- api
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100