Skip to content

Commit 67da5bb

Browse files
authored
[python] Fixes additional_properties_type for models (#8802)
* Fixes additionalProperties values for models, updates docs, adds tag test of it, fixes frit and gmfruit tests * Moves this.setDisallowAdditionalPropertiesIfNotPresent higher * Makes setting additional_properties_model_instances contingent on the presence of addprosp in schema, updates sample spec composed schemas to remove addprops False form two * Fixes oneOf anyOf allOf instantiation logic * Removes Address from Cat definition * Adds required vars for apple and banana, removes required vars from composed schema init sig * Updates composed schema vars to be set on self and all composed instances * Removes get_unused_args, get_var_name_to_model_instances, and get_additional_properties_model_instances * Fixes fruit + deserilization tests, creates ComposedSchemaWithPropsAndNoAddProps * Fixes FruitReq tests * Fixes GmFruit tests * Fixes discard_unknown_keys tests * Samples updated * Removes additionalproperties False in Child * Samples updated * Improves handling of v2 and v3 specs for isFreeFormObject, v2 sample spec updated with link to bug * Adds cli option disallowAdditionalPropertiesIfNotPresent to python * Adds getAdditionalProperties method so the value for addProps will be correct * Reverts file * Reverts file * Updates python doc * Reverted anytype_3 definition * Updates test_deserialize_lizard * Updates test_deserialize_dict_str_dog * Updates testDog * Updates testChild * Adds v2 python_composition sample * Adds needed files for python testing * Adds existing tests into the new python sample * Fixes test_dog * Removes addProps false form Dog * Fixes testChild * Updates how additionalProperties are set * Fixes empty_map type * Type generation fixed for v2 and v3 specs * Refactors getTypeString, updates artifactids in pom.xml files * Adds new python sample to CI testing I think * Fixes artifactId collision, regenrates docs
1 parent 22c5935 commit 67da5bb

File tree

5 files changed

+164
-166
lines changed

5 files changed

+164
-166
lines changed

.generator/templates/model_doc.mustache

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ Name | Type | Description | Notes
2828
{{#optionalVars}}
2929
**{{name}}** | {{^complexType}}**{{dataType}}**{{/complexType}}{{#complexType}}[**{{dataType}}**]({{complexType}}.md){{/complexType}} | {{description}} | [optional] {{#isReadOnly}}[readonly] {{/isReadOnly}}{{#defaultValue}} if omitted the server will use the default value of {{{.}}}{{/defaultValue}}
3030
{{/optionalVars}}
31-
{{#additionalPropertiesType}}
32-
**any string name** | **{{additionalPropertiesType}}** | any string name can be used but the value must be the correct type | [optional]
33-
{{/additionalPropertiesType}}
31+
{{#additionalProperties}}
32+
**any string name** | {{^complexType}}**{{dataType}}**{{/complexType}}{{#complexType}}[**{{dataType}}**]({{complexType}}.md){{/complexType}} | any string name can be used but the value must be the correct type | [optional]
33+
{{/additionalProperties}}
3434

3535
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
3636

.generator/templates/model_templates/classvars.mustache

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
{{/optionalVars}}
6161
}
6262

63-
{{#additionalPropertiesType}}
63+
{{#additionalProperties}}
6464
@cached_property
6565
def additional_properties_type():
6666
"""
@@ -72,11 +72,11 @@
7272
lazy_import()
7373
{{/-first}}
7474
{{/imports}}
75-
return ({{{additionalPropertiesType}}},) # noqa: E501
76-
{{/additionalPropertiesType}}
77-
{{^additionalPropertiesType}}
75+
return ({{{dataType}}},) # noqa: E501
76+
{{/additionalProperties}}
77+
{{^additionalProperties}}
7878
additional_properties_type = None
79-
{{/additionalPropertiesType}}
79+
{{/additionalProperties}}
8080

8181
_nullable = {{#isNullable}}True{{/isNullable}}{{^isNullable}}False{{/isNullable}}
8282

.generator/templates/model_templates/method_init_composed.mustache

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,52 @@
1010
'_additional_properties_model_instances',
1111
])
1212

13-
{{> model_templates/method_init_shared }}
13+
@convert_js_args_to_python_args
14+
def __init__(self, *args, **kwargs): # noqa: E501
15+
"""{{classname}} - a model defined in OpenAPI
16+
17+
Keyword Args:
18+
{{#requiredVars}}
19+
{{#defaultValue}}
20+
{{name}} ({{{dataType}}}):{{#description}} {{{description}}}.{{/description}} defaults to {{{defaultValue}}}{{#allowableValues}}, must be one of [{{#enumVars}}{{{value}}}, {{/enumVars}}]{{/allowableValues}} # noqa: E501
21+
{{/defaultValue}}
22+
{{^defaultValue}}
23+
{{name}} ({{{dataType}}}):{{#description}} {{{description}}}{{/description}}
24+
{{/defaultValue}}
25+
{{/requiredVars}}
26+
{{> model_templates/docstring_init_required_kwargs }}
27+
{{#optionalVars}}
28+
{{name}} ({{{dataType}}}):{{#description}} {{{description}}}.{{/description}} [optional]{{#defaultValue}} if omitted the server will use the default value of {{{defaultValue}}}{{/defaultValue}} # noqa: E501
29+
{{/optionalVars}}
30+
"""
31+
32+
{{#requiredVars}}
33+
{{#defaultValue}}
34+
{{name}} = kwargs.get('{{name}}', {{{defaultValue}}})
35+
{{/defaultValue}}
36+
{{/requiredVars}}
37+
_check_type = kwargs.pop('_check_type', True)
38+
_spec_property_naming = kwargs.pop('_spec_property_naming', False)
39+
_path_to_item = kwargs.pop('_path_to_item', ())
40+
_configuration = kwargs.pop('_configuration', None)
41+
_visited_composed_classes = kwargs.pop('_visited_composed_classes', ())
42+
43+
if args:
44+
raise ApiTypeError(
45+
"Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % (
46+
args,
47+
self.__class__.__name__,
48+
),
49+
path_to_item=_path_to_item,
50+
valid_classes=(self.__class__,),
51+
)
52+
53+
self._data_store = {}
54+
self._check_type = _check_type
55+
self._spec_property_naming = _spec_property_naming
56+
self._path_to_item = _path_to_item
57+
self._configuration = _configuration
58+
self._visited_composed_classes = _visited_composed_classes + (self.__class__,)
1459

1560
constant_args = {
1661
'_check_type': _check_type,
@@ -19,28 +64,18 @@
1964
'_configuration': _configuration,
2065
'_visited_composed_classes': self._visited_composed_classes,
2166
}
22-
required_args = {
23-
{{#requiredVars}}
24-
'{{name}}': {{name}},
25-
{{/requiredVars}}
26-
}
27-
model_args = {}
28-
model_args.update(required_args)
29-
model_args.update(kwargs)
3067
composed_info = validate_get_composed_info(
31-
constant_args, model_args, self)
68+
constant_args, kwargs, self)
3269
self._composed_instances = composed_info[0]
3370
self._var_name_to_model_instances = composed_info[1]
3471
self._additional_properties_model_instances = composed_info[2]
35-
unused_args = composed_info[3]
72+
discarded_args = composed_info[3]
3673

37-
for var_name, var_value in required_args.items():
38-
setattr(self, var_name, var_value)
3974
for var_name, var_value in kwargs.items():
40-
if var_name in unused_args and \
75+
if var_name in discarded_args and \
4176
self._configuration is not None and \
4277
self._configuration.discard_unknown_keys and \
43-
not self._additional_properties_model_instances:
78+
self._additional_properties_model_instances:
4479
# discard variable.
4580
continue
4681
setattr(self, var_name, var_value)

.generator/templates/model_templates/methods_setattr_getattr_composed.mustache

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,43 @@
44
self.__dict__[name] = value
55
return
66

7-
# set the attribute on the correct instance
8-
model_instances = self._var_name_to_model_instances.get(
9-
name, self._additional_properties_model_instances)
10-
if model_instances:
11-
for model_instance in model_instances:
12-
if model_instance == self:
13-
self.set_attribute(name, value)
14-
else:
15-
setattr(model_instance, name, value)
16-
if name not in self._var_name_to_model_instances:
17-
# we assigned an additional property
18-
self.__dict__['_var_name_to_model_instances'][name] = (
19-
model_instance
20-
)
21-
return None
7+
"""
8+
Use cases:
9+
1. additional_properties_type is None (additionalProperties == False in spec)
10+
Check for property presence in self.openapi_types
11+
if not present then throw an error
12+
if present set in self, set attribute
13+
always set on composed schemas
14+
2. additional_properties_type exists
15+
set attribute on self
16+
always set on composed schemas
17+
"""
18+
if self.additional_properties_type is None:
19+
"""
20+
For an attribute to exist on a composed schema it must:
21+
- fulfill schema_requirements in the self composed schema not considering oneOf/anyOf/allOf schemas AND
22+
- fulfill schema_requirements in each oneOf/anyOf/allOf schemas
2223

23-
raise ApiAttributeError(
24-
"{0} has no attribute '{1}'".format(
25-
type(self).__name__, name),
26-
[e for e in [self._path_to_item, name] if e]
27-
)
24+
schema_requirements:
25+
For an attribute to exist on a schema it must:
26+
- be present in properties at the schema OR
27+
- have additionalProperties unset (defaults additionalProperties = any type) OR
28+
- have additionalProperties set
29+
"""
30+
if name not in self.openapi_types:
31+
raise ApiAttributeError(
32+
"{0} has no attribute '{1}'".format(
33+
type(self).__name__, name),
34+
[e for e in [self._path_to_item, name] if e]
35+
)
36+
# attribute must be set on self and composed instances
37+
self.set_attribute(name, value)
38+
for model_instance in self._composed_instances:
39+
setattr(model_instance, name, value)
40+
if name not in self._var_name_to_model_instances:
41+
# we assigned an additional property
42+
self.__dict__['_var_name_to_model_instances'][name] = self._composed_instances + [self]
43+
return None
2844

2945
__unset_attribute_value__ = object()
3046

@@ -34,13 +50,12 @@
3450
return self.__dict__[name]
3551

3652
# get the attribute from the correct instance
37-
model_instances = self._var_name_to_model_instances.get(
38-
name, self._additional_properties_model_instances)
53+
model_instances = self._var_name_to_model_instances.get(name)
3954
values = []
40-
# A composed model stores child (oneof/anyOf/allOf) models under
41-
# self._var_name_to_model_instances. A named property can exist in
42-
# multiple child models. If the property is present in more than one
43-
# child model, the value must be the same across all the child models.
55+
# A composed model stores self and child (oneof/anyOf/allOf) models under
56+
# self._var_name_to_model_instances.
57+
# Any property must exist in self and all model instances
58+
# The value stored in all model instances must be the same
4459
if model_instances:
4560
for model_instance in model_instances:
4661
if name in model_instance._data_store:

0 commit comments

Comments
 (0)