question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[BUG][Python] Client generated allOf model broken since 5.2.0

See original GitHub issue

Bug 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:closed
  • Created 2 years ago
  • Reactions:1
  • Comments:10 (4 by maintainers)

github_iconTop GitHub Comments

2reactions
the-akhil-naircommented, Apr 25, 2022

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 in self._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:

    for prop_name in model_args:
        if prop_name not in discarded_args:
            var_name_to_model_instances[prop_name] = [self] + composed_instances

Now as we can see that the var_name_to_model_instances is populated with self 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:

    for prop_name in model_args:
        if prop_name not in discarded_args:
           var_name_to_model_instances[prop_name] = [self] + list(
                filter(
                    lambda x: prop_name in x.openapi_types, composed_instances))

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.

1reaction
MAKOMOcommented, Apr 25, 2022

This works for me. Splendid!

Read more comments on GitHub >

github_iconTop Results From Across the Web

OpenAPI generator inheritance in version 5 - Stack Overflow
allOf : - $ref: 'BaseField'. And it was generated as inheritance. Schema extends BaseField. After version moved to 5. it becomes generated as ......
Read more >
https://mail.python.org/pipermail/python-bugs-list...
Since I have a newer version of autotools, running "autoreconf" generates a lot ... is actually matplotlib's bug: Python is returning the blob...
Read more >
Openapi Generator Versions - Open Source Agenda
OpenAPI Generator allows generation of API client libraries (SDK generation), ... not working for Client generated allOf model and broken since 5.2.0 #12239 ......
Read more >
Documentation for the java Generator
generator name, java, pass this to the generate command after -g ... Annotate Model and Api using the Swagger Annotations 1.x library.
Read more >
OpenAPI Generator allows generation of API client libraries ...
The OpenAPI Specification has undergone 3 revisions since initial ... working for Client generated allOf model and broken since 5.2.0 #12239 ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found