openapi-generators / openapi-generators/openapi-python-client

from_dict raises when nullable ref is set to None

Abierto
#679 7 comentarios 13 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

🐞bug
Lenguaje dominante
Python
Estrellas
2k
Forks
293
Merge medio
34 min
PR fusionados (30 d)
1

Descripción

Describe the bug
from_dict when we set a nullable field ref to None. This behavior:

  • differs from class constructor which accepts None ref fields
  • differs from others fields : from_dict accept nullable string, object, array, etc to be set to None

To Reproduce
Steps to reproduce the behavior:

  1. take the following swagger:
components:
  schemas:
    MyObject:
      properties:
        myId:
          title: MyId
          type: string
          nullable: true
        myArray:
          title: MyArray
          type: array
          nullable: true
          items:
            type: string
        myString:
          title: MyString
          type: string
          nullable: true
        myObject:
          title: myObject
          type: object
          nullable: true
        myRef:
          $ref: '#/components/schemas/MyChildObject'
          nullable: true
      required:
        - myId
      title: MyObject
      type: object
    MyChildObject:
      properties:
        aField:
          items:
            type: string
          nullable: true
          title: AField
          type: array
      title: MyChildObject
      type: object
info:
  title: myapp
  version: 1.9.4a5
openapi: 3.0.2
paths:
  /myroute:
    get:
      parameters:
      requestBody:
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MyChildObject'
          description: Successful Response
  1. generate the client from this swagger
  2. run the following code snippet:
from myapp_client.models import MyObject, MyChildObject
MyObject(my_id="id", my_object=None, my_array=None, my_string=None, my_ref=None) # pass
MyObject.from_dict({"myId":"id", "myObject": None, "myArray": None, "myString": None, "myRef": None}) # raise
MyObject.from_dict({"myId":"id", "myObject": None, "myArray": None, "myString": None}) # pass
  1. See error
In [1]: from myapp_client.models import MyObject, MyChildObject

In [2]: MyObject(my_id="id", my_object=None, my_array=None, my_string=None, my_ref=None)
Out[2]: MyObject(my_id='id', my_array=None, my_string=None, my_object=None, my_ref=None, additional_properties={})

In [3]: MyObject.from_dict({"myId":"id", "myObject": None, "myArray": None, "myString": None, "myRef": None})
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In [3], line 1
----> 1 MyObject.from_dict({"myId":"id", "myObject": None, "myArray": None, "myString": None, "myRef": None})

File ~/CF/amp-client/myapp-client/myapp_client/models/my_object.py:89, in MyObject.from_dict(cls, src_dict)
     87     my_ref = UNSET
     88 else:
---> 89     my_ref = MyChildObject.from_dict(_my_ref)
     91 my_object = cls(
     92     my_id=my_id,
     93     my_array=my_array,
   (...)
     96     my_ref=my_ref,
     97 )
     99 my_object.additional_properties = d

File ~/CF/amp-client/myapp-client/myapp_client/models/my_child_object.py:38, in MyChildObject.from_dict(cls, src_dict)
     36 @classmethod
     37 def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
---> 38     d = src_dict.copy()
     39     a_field = cast(List[str], d.pop("aField", UNSET))
     41     my_child_object = cls(
     42         a_field=a_field,
     43     )

AttributeError: 'NoneType' object has no attribute 'copy'

In [4]: MyObject.from_dict({"myId":"id", "myObject": None, "myArray": None, "myString": None})
Out[4]: MyObject(my_id='id', my_array=None, my_string=None, my_object=None, my_ref=<myapp_client.types.Unset object at 0x7f9f73a4f8e0>, additional_properties={})

In [5]: MyObject.from_dict({"myId":"id", "myObject": None, "myArray": None, "myString": None})

Expected behavior
We expect MyObject.from_dict({"myId":"id", "myObject": None, "myArray": None, "myString": None, "myRef": None}) to run without errors.

Some debug
having a look in the debugger, it raises there::

        _my_ref = d.pop("myRef", UNSET)
        my_ref: Union[Unset, MyChildObject]
        if isinstance(_my_ref, Unset):
            my_ref = UNSET
        else:
->          my_ref = MyChildObject.from_dict(_my_ref)

The code generated should rather look like:

        _my_ref = d.pop("myRef", UNSET)
        my_ref: Union[Unset, None, MyChildObject]
        if _my_ref is None:
            my_ref = None
        elif isinstance(_my_ref, Unset):
            my_ref = UNSET
        else:
            my_ref = MyChildObject.from_dict(_my_ref)

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Línea de trabajo

Empieza inspeccionando los archivos generados myapp_client/models/my_object.py y my_child_object.py, especialmente la ruta from_dict mostrada en el informe, y luego rastrea qué código del generador la produce. Compara el manejo de escalares anulables con el manejo de $ref anulables. Se considera terminado cuando from_dict acepta un valor None para myRef sin llamar a MyChildObject.from_dict(None), preservando al mismo tiempo el comportamiento de UNSET.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
openapi, python
Área
tooling
Tipo de issue
Error
Dificultad
4/5
Tiempo estimado
3-5 días
Estado de actividad
Estancado
Claridad
Bastante claro
Aptitud para principiantes
35/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.