from_dict raises when nullable ref is set to None
Describe the bug
from_dict when we set a nullable field ref to None. This behavior:
- differs from class constructor which accepts
Noneref fields - differs from others fields :
from_dictacceptnullablestring,object,array, etc to be set to None
To Reproduce Steps to reproduce the behavior:
- 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
- generate the client from this swagger
- 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
- 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)
After a little digging, wild guess would be that this happens because of this line (note the parent=None), leading to this if block being skipped, while this is the one that sets the nullable attribute of the property
On a side topic - it looks like nullable is a deprecated param? I don't see it in the OpenAPI latest spec?
I read there seems to be ambiguity when you specify something like
type: object
nullable: true
which allows for passing a null, but that's not a valid object as null is not an object.
I run in the same troubles with nullalable
Yes this is deprecated in 3.1 https://jane.readthedocs.io/en/latest/tips/nullable.html but 3.0 is heavy used and will be for years.
@fabiog1901 your argument is correct but
myString:
title: MyString
type: string
nullable: true
null is not a string. By big model and big nested objects is it a pain to find out where you have an error because you get just AttributeError: 'NoneType' object has no attribute 'copy' of top object.
Hello,
Having the same issue (version 0.15.1) Is there a fix planned or does anyone know a workaround?
Thanks