Skip to content

Commit 2718f3a

Browse files
committed
fix: merge an allOf that narrows a value instead of building a model
An allOf whose parts are all values, such as a reference to an enum plus `enum: [ONE_VALUE]`, was built as a model and failed with "Cannot take allOf a non-object". Merge its parts with merge_properties, as allOf already does for model properties.
1 parent fc4b81c commit 2718f3a

1 file changed

Lines changed: 77 additions & 0 deletions

File tree

‎openapi_python_client/parser/properties/__init__.py‎

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,16 @@ def property_from_data( # noqa: PLR0911, PLR0912
288288
process_properties=process_properties,
289289
roots=roots,
290290
)
291+
if data.allOf and _all_of_is_values(data.allOf, schemas):
292+
return _property_from_values_all_of(
293+
name=name,
294+
required=required,
295+
data=data,
296+
schemas=schemas,
297+
parent_name=parent_name,
298+
config=config,
299+
roots=roots,
300+
)
291301
if data.type == oai.DataType.OBJECT or data.allOf or (data.type is None and data.properties):
292302
return ModelProperty.build(
293303
data=data,
@@ -472,3 +482,70 @@ def build_parameters(
472482

473483
parameters.errors.extend(errors)
474484
return parameters
485+
486+
487+
def _all_of_is_values(parts: list[oai.Reference | oai.Schema], schemas: Schemas) -> bool:
488+
"""Whether every part of an allOf describes a value (an enum, string, number...) rather than an object.
489+
490+
Such an allOf narrows a value, for example a reference to an enum plus `enum: [ONE_VALUE]`. It is merged
491+
property by property instead of being built as a model.
492+
"""
493+
for part in parts:
494+
if isinstance(part, oai.Reference):
495+
ref_path = parse_reference_path(part.ref)
496+
if isinstance(ref_path, ParseError):
497+
return False
498+
existing = schemas.classes_by_reference.get(ref_path)
499+
if existing is None or isinstance(existing, ModelProperty):
500+
return False
501+
continue
502+
if part.type == oai.DataType.OBJECT or part.properties or part.allOf or part.anyOf or part.oneOf:
503+
return False
504+
return True
505+
506+
507+
def _property_from_values_all_of(
508+
*,
509+
name: UntrustedString,
510+
required: bool,
511+
data: oai.Schema,
512+
schemas: Schemas,
513+
parent_name: str,
514+
config: Config,
515+
roots: set[ReferencePath | strings.ClassName],
516+
) -> tuple[Property | PropertyError, Schemas]:
517+
"""Merge the parts of a value allOf into one property, using the same rules as merging model properties."""
518+
from .merge_properties import merge_properties # noqa: PLC0415
519+
520+
merged: Property | None = None
521+
for part in data.allOf:
522+
prop, schemas = property_from_data(
523+
name=name,
524+
required=required,
525+
data=part,
526+
schemas=schemas,
527+
parent_name=parent_name,
528+
config=config,
529+
roots=roots,
530+
)
531+
if isinstance(prop, PropertyError):
532+
return prop, schemas
533+
if merged is None:
534+
merged = prop
535+
continue
536+
result = merge_properties(merged, prop)
537+
if isinstance(result, PropertyError):
538+
result.data = data
539+
return result, schemas
540+
merged = result
541+
assert merged is not None
542+
return (
543+
evolve(
544+
merged,
545+
required=required,
546+
name=name,
547+
python_name=strings.PythonIdentifier(value=name, prefix=config.field_prefix),
548+
description=data.description or merged.description,
549+
),
550+
schemas,
551+
)

0 commit comments

Comments
 (0)