[BUG][Python] Client generated allOf model broken since 5.2.0
See original GitHub issueBug Report Checklist
- [yes] Have you provided a full/minimal spec to reproduce the issue?
- [yes] Have you validated the input using an OpenAPI validator (example)?
- [no] Have you tested with the latest master to confirm the issue still exists?
- [yes] Have you searched for related issues/PRs?
- [yes] What’s the actual output vs expected output?
- [no] [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description
The allOf models for the python-client generator are broken since version 5.2.0
openapi-generator version
5.2.0 upwards creates broken model code
5.1.X works fine
OpenAPI declaration file content or url
Minimal example model code
openapi: 3.0.2
info:
title: Python AllOf bug demo
description: Python AllOf bug demo
version: 1.0.0
servers:
- url: /bug
tags:
- name: StreamOptions
description: StreamOptions
paths:
/StreamOptions:
$ref: '#/components/schemas/StreamOptions'
components:
schemas:
StreamOptions:
allOf:
- $ref: "#/components/schemas/EgressThresholdOptions"
- $ref: "#/components/schemas/PublishOptions"
EgressThresholdOptions:
type: object
properties:
egressThresholds:
type: object
additionalProperties:
type: number
minimum: 0
maximum: 1
example: 0.9
PublishOptions:
type: object
properties:
publish:
type : object
properties:
egressUnknownDetections:
type: boolean
example: true
filterZeroDetections:
type: boolean
example: true
Generation Details
Steps to reproduce
Use the following command to produce the model
openapi-generator generate -i api.yaml -g python -o .
Once generated, create the following python bug.py script to trigger the bug
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from copy import deepcopy
import openapi_client.models as cc
from openapi_client.configuration import Configuration
from openapi_client.model_utils import deserialize_model
def main():
data = {"egressThresholds": {"person":0.8}, "publish": {"egressUnknownDetections": False}}
model = deserialize_model(
deepcopy(data),
cc.StreamOptions,
(),
True,
Configuration(discard_unknown_keys=True),
True,
)
model.publish # breaks
if __name__ == "__main__":
main()
This will result in the following error
./bug.py
Traceback (most recent call last):
File "/Users/balzer/demo/./bug.py", line 26, in <module>
main()
File "/Users/balzer/demo/./bug.py", line 22, in main
model.publish # breaks
File "/Users/balzer/demo/openapi_client/model_utils.py", line 188, in __getattr__
return self.__getitem__(attr)
File "/Users/balzer/demo/openapi_client/model_utils.py", line 667, in __getitem__
value = self.get(name, self.__unset_attribute_value__)
File "/Users/balzer/demo/openapi_client/model_utils.py", line 658, in get
raise ApiValueError(
openapi_client.exceptions.ApiValueError: Values stored for property publish in StreamOptions differ when looking at self and self's composed instances. All values must be the same at ['publish']
Turns out that the values list in model_utils.ModelComposed.get contains two entries:
[{'egress_unknown_detections': False}, {'egressUnknownDetections': False}]
The first object is of type <class ‘openapi_client.model.publish_options_publish.PublishOptionsPublish’> and the other of type <class ‘dict’>
See for possible reason below
Related issues/PRs
Have not found any related issue so far
Suggest a fix
Debugging the code, it seems that the method _from_openapi_data of a model calls validate_get_composed_info in model_utils.py . This call get_allof_instances and which is used to fill the var_name_to_model_instances variable.
This variable contains the model data for each composed model and looks like this:
<class 'openapi_client.model.egress_threshold_options.EgressThresholdOptions'> {'egress_thresholds': {'person': 0.8},
'publish': {'egressUnknownDetections': False}}
<class 'openapi_client.model.publish_options.PublishOptions'> {'egress_thresholds': {'person': 0.8},
'publish': {'egress_unknown_detections': False}}
For some reason, the convert_js_args_to_python_args is applied to some of the data and not to the others, i.e. for the EgressThresholdOptions model the EgressThresholdOptions data is converted but not the PublishOptions model data and vice versa.
Currently trying to find out where this happens.
Issue Analytics
- State:
- Created 2 years ago
- Reactions:1
- Comments:10 (4 by maintainers)
Top GitHub Comments
I feel the issue is due to the creation of
self._var_name_to_model_instances
while doing the deserialization of the data.Earlier the Python SDK code was using
get_var_name_to_model_instances
function which was adding var name to model instances that contain it. So<class 'openapi_client.model.stream_options_all_of'>
will not part of mapping inself._var_name_to_model_instances
for variable name stream_options.Now with the latest Python SDK code following is the way through which
var_name_to_model_instances
is created:Now as we can see that the
var_name_to_model_instances
is populated withself and composed_instance
which will also contain stream_options_all_of as a composed instance and there will be no check that if stream_options is present in composed_instances or not.As there is no attribute_mapping found for stream_options in stream_options_all_of, the type for stream_options will be treated as dict for mapping stream_options_all_of as mentioned by @Chekov2k.
So what I suggest is the following code:
This way we can check if the property name is present in that composed instance or not. If it’s okay for @spacether I can raise a PR for this.
This works for me. Splendid!