Bug Report Checklist
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).
Bug Report Checklist
Description
I have generated an openapi python client with the
pythontemplate 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:This however resulted in a validation error
this happens, because the method
to_dictcan return a primitive type, if the openapi model wrapping it is only used for validation (as in my case for the oneOf), but the methodsanitize_for_serializationassumes it to always be a dict and calls.items()on itopenapi-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
python -m venv .venvsource .venv/bin/activatepip install openapi-generator-bug/pythonRelated 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:
Tried this solution locally and it works (for my use-case).