Skip to content

Commit 9e57fb9

Browse files
committed
Add version-specific schema processing using schema_features
1 parent 2813985 commit 9e57fb9

4 files changed

Lines changed: 30 additions & 19 deletions

File tree

src/datamodel_code_generator/parser/jsonschema.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,12 @@ class JSONReference(_enum.Enum):
192192

193193

194194
class Discriminator(BaseModel):
195-
"""Represent OpenAPI discriminator object."""
195+
"""Represent OpenAPI discriminator object.
196+
197+
This is an OpenAPI-specific concept for supporting polymorphism.
198+
It identifies which schema applies based on a property value.
199+
Kept in jsonschema.py to avoid circular imports with openapi.py.
200+
"""
196201

197202
propertyName: str # noqa: N815
198203
mapping: Optional[dict[str, str]] = None # noqa: UP045
@@ -517,7 +522,7 @@ def _get_type(
517522
data_formats: dict[str, dict[str, Types]] | None = None,
518523
) -> Types:
519524
"""Get the appropriate Types enum for a given JSON Schema type and format."""
520-
if data_formats is None:
525+
if data_formats is None: # pragma: no cover
521526
data_formats = json_schema_data_formats
522527
if type_ not in data_formats:
523528
return Types.any
@@ -768,8 +773,16 @@ def _get_type_with_mappings(self, type_: str, format_: str | None = None) -> Typ
768773

769774
@cached_property
770775
def schema_paths(self) -> list[tuple[str, list[str]]]:
771-
"""Get schema paths for definitions and defs."""
772-
return [(s, s.lstrip("#/").split("/")) for s in self.SCHEMA_PATHS]
776+
"""Get schema paths for definitions and defs.
777+
778+
Prioritizes the version-appropriate key based on detected schema version,
779+
but checks both for lenient compatibility with mixed-version schemas.
780+
- Draft 4-7: definitions (standard), then $defs (fallback)
781+
- Draft 2019-09+: $defs (standard), then definitions (fallback)
782+
"""
783+
primary_key = self.schema_features.definitions_key
784+
ordered_paths = ["#/$defs", "#/definitions"] if primary_key == "$defs" else ["#/definitions", "#/$defs"]
785+
return [(s, s.lstrip("#/").split("/")) for s in ordered_paths]
773786

774787
@cached_property
775788
def schema_features(self) -> JsonSchemaFeatures:
@@ -3589,9 +3602,15 @@ def parse_id(self, obj: JsonSchemaObject, path: list[str]) -> None:
35893602

35903603
@contextmanager
35913604
def root_id_context(self, root_raw: dict[str, Any]) -> Generator[None, None, None]:
3592-
"""Context manager to temporarily set the root $id during parsing."""
3605+
"""Context manager to temporarily set the root $id during parsing.
3606+
3607+
Uses schema_features.id_field to support both "id" (Draft 4) and "$id" (Draft 6+).
3608+
Falls back to checking both fields for lenient compatibility.
3609+
"""
35933610
previous_root_id = self.root_id
3594-
self.root_id = root_raw.get("$id") or None
3611+
# Try version-specific field first, then fallback to alternative for compatibility
3612+
id_field = self.schema_features.id_field
3613+
self.root_id = root_raw.get(id_field) or root_raw.get("$id") or root_raw.get("id") or None
35953614
yield
35963615
self.root_id = previous_root_id
35973616

src/datamodel_code_generator/parser/openapi.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -240,10 +240,11 @@ def get_ref_model(self, ref: str) -> dict[str, Any]:
240240

241241
def get_data_type(self, obj: JsonSchemaObject) -> DataType:
242242
"""Get data type from JSON schema object, handling OpenAPI nullable semantics."""
243-
# OpenAPI 3.0 doesn't allow `null` in the `type` field and list of types
243+
# OpenAPI 3.0 uses `nullable: true` flag for null support (nullable_keyword=True)
244+
# OpenAPI 3.1 uses `type: ["string", "null"]` instead (nullable_keyword=False)
244245
# https://swagger.io/docs/specification/data-models/data-types/#null
245-
# OpenAPI 3.1 does allow `null` in the `type` field and is equivalent to
246-
# a `nullable` flag on the property itself
246+
# When strict_nullable is enabled, convert nullable flag to type array for
247+
# consistent handling regardless of OpenAPI version
247248
if obj.nullable and self.strict_nullable and isinstance(obj.type, str):
248249
obj.type = [obj.type, "null"]
249250

src/datamodel_code_generator/parser/schema_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ def get_data_formats(*, is_openapi: bool = False) -> DataFormatMapping:
272272
for type_key, type_formats in _get_openapi_only_formats().items():
273273
if type_key in formats:
274274
formats[type_key] = {**formats[type_key], **type_formats}
275-
else:
275+
else: # pragma: no cover
276276
formats[type_key] = type_formats
277277
return formats
278278

tests/data/expected/main/openapi/same_name_objects.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,6 @@ class Pet(BaseModel):
1818
tag: str | None = None
1919

2020

21-
class Error(BaseModel):
22-
code: int
23-
message: str
24-
25-
26-
class Resolved(BaseModel):
27-
resolved: list[str] | None = None
28-
29-
3021
class PetsModel(BaseModel):
3122
__root__: list[Pet]
3223

0 commit comments

Comments
 (0)