Skip to content

Commit 1e995b7

Browse files
committed
fix default computation for merged properties
1 parent 2f185fb commit 1e995b7

File tree

6 files changed

+74
-12
lines changed

6 files changed

+74
-12
lines changed

.changeset/improved_property_merging.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,6 @@ When using `allOf` to extend a base object type, `openapi-python-client` is now
1515

1616
> [!NOTE]
1717
> `pattern` and `max_length` are no longer fields on `StringProperty`, which may impact custom templates.
18+
19+
This also fixes a bug where properties of inline objects (as opposed to references) were not using the
20+
merge logic, but were simply overwriting previous definitions of the same property.

end_to_end_tests/golden-record/my_test_api_client/models/model_with_merged_properties.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@ class ModelWithMergedProperties:
1616
"""
1717
Attributes:
1818
simple_string (Union[Unset, str]): extended simpleString description Default: 'new default'.
19-
string_to_enum (Union[Unset, ModelWithMergedPropertiesStringToEnum]): Default: 'a'.
19+
string_to_enum (Union[Unset, ModelWithMergedPropertiesStringToEnum]): Default:
20+
ModelWithMergedPropertiesStringToEnum.A.
2021
string_to_date (Union[Unset, datetime.date]):
2122
number_to_int (Union[Unset, int]):
2223
any_to_string (Union[Unset, str]): Default: 'x'.
2324
"""
2425

2526
simple_string: Union[Unset, str] = "new default"
26-
string_to_enum: Union[Unset, ModelWithMergedPropertiesStringToEnum] = "a"
27+
string_to_enum: Union[Unset, ModelWithMergedPropertiesStringToEnum] = ModelWithMergedPropertiesStringToEnum.A
2728
string_to_date: Union[Unset, datetime.date] = UNSET
2829
number_to_int: Union[Unset, int] = UNSET
2930
any_to_string: Union[Unset, str] = "x"

openapi_python_client/parser/properties/merge_properties.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,7 @@ def _merge_common_attributes(base: PropertyT, *extend_with: PropertyProtocol) ->
148148
"""
149149
current = base
150150
for override in extend_with:
151-
if isinstance(override, EnumProperty):
152-
override_default = current.convert_value(override.default_to_raw())
153-
else:
154-
override_default = current.convert_value(override.default)
151+
override_default = current.convert_value(override.default_to_raw())
155152
if isinstance(override_default, PropertyError):
156153
return override_default
157154
current = evolve(

openapi_python_client/parser/properties/protocol.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,3 +177,17 @@ def is_base_type(self) -> bool:
177177
ListProperty.__name__,
178178
UnionProperty.__name__,
179179
}
180+
181+
def default_to_raw(self) -> Any | None:
182+
d = self.default
183+
if not isinstance(d, Value):
184+
return d
185+
if d.startswith('"') or d.startswith("'"):
186+
return d[1:-1]
187+
if d == "true":
188+
return True
189+
if d == "false":
190+
return False
191+
if "." in d:
192+
return float(d)
193+
return int(d)

tests/test_parser/test_properties/test_init.py

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
StringProperty,
1212
UnionProperty,
1313
)
14-
from openapi_python_client.parser.properties.protocol import ModelProperty
14+
from openapi_python_client.parser.properties.protocol import ModelProperty, Value
1515
from openapi_python_client.schema import DataType
1616
from openapi_python_client.utils import ClassName, PythonIdentifier
1717

@@ -23,11 +23,11 @@ def test_is_base_type(self, string_property_factory):
2323
assert string_property_factory().is_base_type is True
2424

2525
@pytest.mark.parametrize(
26-
"required, expected",
27-
(
26+
"required,expected",
27+
[
2828
(True, "str"),
2929
(False, "Union[Unset, str]"),
30-
),
30+
],
3131
)
3232
def test_get_type_string(self, string_property_factory, required, expected):
3333
p = string_property_factory(required=required)
@@ -374,6 +374,24 @@ def test_values_from_list_duplicate(self):
374374

375375

376376
class TestPropertyFromData:
377+
@pytest.mark.parametrize(
378+
"default_value, default_repr",
379+
["a", "'a'"],
380+
["'a'", "\\'a\\'"],
381+
['"a"', "'a'"],
382+
)
383+
def test_property_from_data_str_defaults(self, default_value, default_repr, config):
384+
from openapi_python_client.parser.properties import Schemas, property_from_data
385+
from openapi_python_client.schema import Schema
386+
387+
name = "my_string"
388+
data = Schema(type="string", default=default_value)
389+
prop, _ = property_from_data(name=name, data=data, schemas=Schemas(), config=config)
390+
assert isinstance(prop, StringProperty)
391+
assert isinstance(prop, Value)
392+
assert str(prop) == default_repr
393+
assert prop.default_to_raw == default_value
394+
377395
def test_property_from_data_str_enum(self, enum_property_factory, config):
378396
from openapi_python_client.parser.properties import Class, Schemas, property_from_data
379397
from openapi_python_client.schema import Schema
@@ -747,13 +765,15 @@ def test_no_format(self, string_property_factory, required, config):
747765
from openapi_python_client.parser.properties import property_from_data
748766

749767
name = "some_prop"
750-
data = oai.Schema.model_construct(type="string", default='"hello world"')
768+
data = oai.Schema.model_construct(type="string", default="hello world")
751769

752770
p, _ = property_from_data(
753771
name=name, required=required, data=data, parent_name=None, config=config, schemas=Schemas()
754772
)
755773

756-
assert p == string_property_factory(name=name, required=required, default="'\\\\\"hello world\\\\\"'")
774+
assert p == string_property_factory(
775+
name=name, required=required, default=StringProperty.convert_value("hello world")
776+
)
757777

758778
def test_datetime_format(self, date_time_property_factory, config):
759779
from openapi_python_client.parser.properties import property_from_data

tests/test_parser/test_properties/test_model_property.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,33 @@ def test_conflicting_property_names(self, config):
642642
result = _process_properties(data=data, schemas=schemas, class_name="", config=config, roots={"root"})
643643
assert isinstance(result, PropertyError)
644644

645+
def test_merge_inline_objects(self, model_property_factory, enum_property_factory, config):
646+
data = oai.Schema.model_construct(
647+
allOf=[
648+
oai.Schema.model_construct(
649+
type="object",
650+
properties={
651+
"prop1": oai.Schema.model_construct(type="string", default="a"),
652+
},
653+
),
654+
oai.Schema.model_construct(
655+
type="object",
656+
properties={
657+
"prop1": oai.Schema.model_construct(type="string", description="desc"),
658+
},
659+
),
660+
]
661+
)
662+
schemas = Schemas()
663+
664+
result = _process_properties(data=data, schemas=schemas, class_name="", config=config, roots={"root"})
665+
assert not isinstance(result, PropertyError)
666+
assert len(result.optional_props) == 1
667+
prop1 = result.optional_props[0]
668+
assert isinstance(prop1, StringProperty)
669+
assert prop1.description == "desc"
670+
assert prop1.default == StringProperty.convert_value("a")
671+
645672

646673
class TestProcessModel:
647674
def test_process_model_error(self, mocker, model_property_factory, config):

0 commit comments

Comments
 (0)