OpenAPITools / OpenAPITools/openapi-generator

[BUG][Python] Constructing an object with a polymorphic base class parameter causes RecursionError

Open
#8,579 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Client: Python Issue: Bug
Dominant language
Java
Stars
26.8k
Forks
7.7k
PR merge metrics
PR metrics pending

Description

Bug Report Checklist
  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator (example)?
  • Have you tested with the latest master to confirm the issue still exists?
  • 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

Generated client code crashes with a RecursionError when trying to construct a TestRequest object with a base type parameter. The request specifies a parent type, I'm trying to set the child type as the parameter value (standard abstract base class/child class modelling).

openapi-generator version

5.0.0

OpenAPI declaration file content or url
openapi: 3.0.0
info:
 description: The Bug schema
 title: recursion bug
 version: 0.1.11

paths: {}

components:
 schemas:
   TestRequest:
     type: object
     required:
       - data
       - parentType
     properties:
       data:
         type: string
       parentType:
         $ref: '#/components/schemas/ParentType'
   ParentType:
     type: object
     required:
       - objectType
     properties:
       objectType:
         type: string
     discriminator:
       propertyName: objectType
       mapping:
         child1: '#/components/schemas/ChildType1'
         child2: '#/components/schemas/ChildType2'
   ChildType1:
     allOf:
       - $ref: '#/components/schemas/ParentType'
     properties:
       objectType:
         type: string
         default: child1
       attribute1:
         type: string
         format: byte
   ChildType2:
     allOf:
       - $ref: '#/components/schemas/ParentType'
     type: object
     properties:
       objectType:
         type: string
         default: child2
Generation Details
java -jar /home/bherweyer/dev/spa/spa-openapi/openapi-generator-cli-5.0.0.jar  generate -i /home/bherweyer/dev/spa/spa-openapi/specification/bug.yaml -g python -o /home/bherweyer/dev/spa/spa-openapi/target/client/python/bug --package-name bug

Steps to reproduce
from bug.models import ChildType1, TestRequest

child = ChildType1(object_type="child1", attr="foobar")

request = TestRequest(parent_type=child, data="")

Running this throws the following exception:

Traceback (most recent call last):
 File "test_construct.py", line 8, in <module>
   request = TestRequest(parent_type=child, data="")
 File "/home/bherweyer/dev/spa/spa-openapi/target/client/python/bug/bug/model_utils.py", line 1566, in wrapped_init
   return fn(self, *args, **kwargs)
 File "/home/bherweyer/dev/spa/spa-openapi/target/client/python/bug/bug/model/test_request.py", line 172, in __init__
   self.parent_type = parent_type
 File "/home/bherweyer/dev/spa/spa-openapi/target/client/python/bug/bug/model_utils.py", line 167, in __setattr__
   self[attr] = value
 File "/home/bherweyer/dev/spa/spa-openapi/target/client/python/bug/bug/model_utils.py", line 352, in __setitem__
   self.set_attribute(name, value)
 File "/home/bherweyer/dev/spa/spa-openapi/target/client/python/bug/bug/model_utils.py", line 141, in set_attribute
   self._check_type, configuration=self._configuration)
 File "/home/bherweyer/dev/spa/spa-openapi/target/client/python/bug/bug/model_utils.py", line 1386, in validate_and_convert_types
   results = get_required_type_classes(required_types_mixed, spec_property_naming)
 File "/home/bherweyer/dev/spa/spa-openapi/target/client/python/bug/bug/model_utils.py", line 1017, in get_required_type_classes
   valid_classes.extend(get_possible_classes(required_type, spec_property_naming))
 File "/home/bherweyer/dev/spa/spa-openapi/target/client/python/bug/bug/model_utils.py", line 977, in get_possible_classes
   possible_classes.extend(get_discriminated_classes(cls))
Suggest a fix

get_discriminated_classes seems to assume it can recurse on a tree structure to get a list of all possible classes using the discriminator property on the models, but inspecting the generated discriminator properties shows that all possible classes are already listed in the discriminator property, which produces a complete graph. The get_discriminated_classes then recurses in a loop around that complete graph.

Manually editing the generated child classes to set the discriminator property to None makes the get_discriminated_classes method return, and it constructs the TestRequest object successfully. I have test code which is using similarly manually edited model classes without issue.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Regenerate the Python client from the provided YAML and reproduce the failure while reading the generated model_utils.py entry points get_possible_classes and get_discriminated_classes. Trace how the discriminator mappings recurse through ParentType and its children; done means constructing TestRequest with a ChildType1 value no longer raises RecursionError.

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
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.