-
-
Notifications
You must be signed in to change notification settings - Fork 7.5k
[BUG][PYTHON] Exception when serializing any_of with primitive types #23409
Description
Bug Report Checklist
- Have you provided a full/minimal spec to reproduce the issue?
- Have you validated the input using an OpenAPI validator? (Not needed, it is a bug in the template)
- Have you tested with the latest master to confirm the issue still exists? (Not needed, the source code is still there.)
- Have you searched for related issues/PRs?
- What's the actual output vs expected output?
- [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description
I have generated an openapi python client with the python template from my openapi definition. I have built the openapi spec with the openapi-generator-cli container. Then I have tried using the api client in a python script:
from openapi_generator_bug.api_client import ApiClient
from openapi_generator_bug.models.id import Id
from openapi_generator_bug import DefaultApi
client = ApiClient()
api = DefaultApi(client)
api.get(id=[Id(1)])This however resulted in a validation error
~/Documents/test/openapi-generator-bug
.venv 0 $ python script.py
Traceback (most recent call last):
File "/home/william/Documents/test/openapi-generator-bug/script.py", line 7, in <module>
api.get(id=[Id(1)])
~~~~~~~^^^^^^^^^^^^
File "/home/william/Documents/test/.venv/lib64/python3.14/site-packages/pydantic/_internal/_validate_call.py", line 39, in wrapper_function
return wrapper(*args, **kwargs)
File "/home/william/Documents/test/.venv/lib64/python3.14/site-packages/pydantic/_internal/_validate_call.py", line 136, in __call__
res = self.__pydantic_validator__.validate_python(pydantic_core.ArgsKwargs(args, kwargs))
File "/home/william/Documents/test/.venv/lib64/python3.14/site-packages/openapi_generator_bug/api/default_api.py", line 83, in get
_param = self._get_serialize(
id=id,
...<3 lines>...
_host_index=_host_index
)
File "/home/william/Documents/test/.venv/lib64/python3.14/site-packages/openapi_generator_bug/api/default_api.py", line 281, in _get_serialize
return self.api_client.param_serialize(
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
method='GET',
^^^^^^^^^^^^^
...<10 lines>...
_request_auth=_request_auth
^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/home/william/Documents/test/.venv/lib64/python3.14/site-packages/openapi_generator_bug/api_client.py", line 241, in param_serialize
query_params = self.sanitize_for_serialization(query_params)
File "/home/william/Documents/test/.venv/lib64/python3.14/site-packages/openapi_generator_bug/api_client.py", line 364, in sanitize_for_serialization
self.sanitize_for_serialization(sub_obj) for sub_obj in obj
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
File "/home/william/Documents/test/.venv/lib64/python3.14/site-packages/openapi_generator_bug/api_client.py", line 367, in sanitize_for_serialization
return tuple(
^^^^^
File "/home/william/Documents/test/.venv/lib64/python3.14/site-packages/openapi_generator_bug/api_client.py", line 368, in <genexpr>
self.sanitize_for_serialization(sub_obj) for sub_obj in obj
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
File "/home/william/Documents/test/.venv/lib64/python3.14/site-packages/openapi_generator_bug/api_client.py", line 364, in sanitize_for_serialization
self.sanitize_for_serialization(sub_obj) for sub_obj in obj
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
File "/home/william/Documents/test/.venv/lib64/python3.14/site-packages/openapi_generator_bug/api_client.py", line 394, in sanitize_for_serialization
for key, val in obj_dict.items()
^^^^^^^^^^^^^^
AttributeError: 'int' object has no attribute 'items'
this happens, because the method to_dict can return a primitive type, if the openapi model wrapping it is only used for validation (as in my case for the oneOf), but the method sanitize_for_serialization assumes it to always be a dict and calls .items() on it
openapi-generator version
openapi-generator-cli:v7.21.0
This seems to be a bug in the implementation.
OpenAPI declaration file content or url
{
"openapi": "3.0.0",
"info": {
"title": "openapi generator bug",
"version": "0.1.0"
},
"paths": {
"/get": {
"get": {
"operationId": "get",
"parameters": [
{
"name": "id",
"in": "query",
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Id"
}
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"Id": {
"oneOf": [
{
"type": "string",
"enum": ["none"]
},
{
"type": "integer",
"format": "uint64",
"minimum": 1.0
}
]
}
}
}
}Generation Details
podman run --rm \
-v "${PWD}":/app:z \
docker.io/openapitools/openapi-generator-cli:v7.21.0@sha256:ce308310f3c1f8761e65338b8ab87b651bf4862c6acb80de510f381fffc4510b \
generate \
-i /app/openapi-generator-bug/schema.json \
-g python \
-o /app/openapi-generator-bug/python \
--additional-properties=packageName="openapi_generator_bug", \
--additional-properties=projectName="OpenAPI Generator Bug", \
--additional-properties=packageVersion="0.1.0" \
--additional-properties=generateSourceCodeOnly="false"Steps to reproduce
- Create the spec file with the above spec
- Run the command specified above to generate the library
- Create a venv
python -m venv .venv - Activate the venv
source .venv/bin/activate - Install the library
pip install openapi-generator-bug/python - Copy and run the script from above.
Related issues/PRs
None found.
Suggest a fix
This is the problem source:
https://github.com/OpenAPITools/openapi-generator/blob/master/modules/openapi-generator/src/main/resources/python/api_client.mustache#L402
The primitive type however originates from here:
https://github.com/OpenAPITools/openapi-generator/blob/master/modules/openapi-generator/src/main/resources/python/model_oneof.mustache#L197
You can simply move some code around in the template to make this work:
elif isinstance(obj, dict):
return {
key: self.sanitize_for_serialization(val)
for key, val in obj.items()
}
# Convert model obj to dict except
# attributes `openapi_types`, `attribute_map`
# and attributes which value is not None.
# Convert attribute name to json key in
# model definition for request.
if hasattr(obj, 'to_dict') and callable(getattr(obj, 'to_dict')):
obj_dict = obj.to_dict()
else:
obj_dict = obj.__dict__
return self.sanitize_for_serialization(obj_dict)Tried this solution locally and it works (for my use-case).