|
31 | 31 | JsonSchemaVersion, |
32 | 32 | ReadOnlyWriteOnlyModelType, |
33 | 33 | SchemaParseError, |
| 34 | + VersionMode, |
34 | 35 | YamlValue, |
35 | 36 | load_data, |
36 | 37 | load_data_from_path, |
@@ -779,8 +780,6 @@ def schema_paths(self) -> list[tuple[str, list[str]]]: |
779 | 780 | the primary path, with fallback to the alternative in Lenient mode. |
780 | 781 | OpenAPI subclass uses its own SCHEMA_PATHS (#/components/schemas). |
781 | 782 | """ |
782 | | - from datamodel_code_generator.enums import VersionMode # noqa: PLC0415 |
783 | | - |
784 | 783 | # OpenAPI and other subclasses use their own SCHEMA_PATHS |
785 | 784 | if self.SCHEMA_PATHS != ["#/definitions", "#/$defs"]: |
786 | 785 | return [(s, s.lstrip("#/").split("/")) for s in self.SCHEMA_PATHS] |
@@ -2935,6 +2934,9 @@ def parse_array_fields( # noqa: PLR0912 |
2935 | 2934 | singular_name: bool = True, # noqa: FBT001, FBT002 |
2936 | 2935 | ) -> DataModelFieldBase: |
2937 | 2936 | """Parse array schema into a data model field with list type.""" |
| 2937 | + # Strict mode: check for version-specific array features |
| 2938 | + self._check_array_version_features(obj, path) |
| 2939 | + |
2938 | 2940 | if self.force_optional_for_required_fields: |
2939 | 2941 | required: bool = False |
2940 | 2942 | nullable: Optional[bool] = None # noqa: UP045 |
@@ -3645,9 +3647,111 @@ def parse_raw_obj( |
3645 | 3647 | if isinstance(raw, dict) and "x-python-import" in raw: |
3646 | 3648 | self._handle_python_import(name, path) |
3647 | 3649 | return |
| 3650 | + |
| 3651 | + # Strict mode: check for version-specific features before validation |
| 3652 | + self._check_version_specific_features(raw, path) |
| 3653 | + |
3648 | 3654 | obj = self._validate_schema_object(raw, path) |
3649 | 3655 | self.parse_obj(name, obj, path) |
3650 | 3656 |
|
| 3657 | + def _check_version_specific_features( |
| 3658 | + self, |
| 3659 | + raw: dict[str, YamlValue] | YamlValue, |
| 3660 | + path: list[str], |
| 3661 | + ) -> None: |
| 3662 | + """Check for version-specific features and warn in Strict mode. |
| 3663 | +
|
| 3664 | + This method checks the raw schema data before Pydantic validation |
| 3665 | + to detect features that may not be valid for the declared version. |
| 3666 | + """ |
| 3667 | + version_mode = getattr(self.config, "schema_version_mode", None) |
| 3668 | + if version_mode != VersionMode.Strict: |
| 3669 | + return |
| 3670 | + |
| 3671 | + # Check boolean schemas (Draft 6+) |
| 3672 | + if isinstance(raw, bool): |
| 3673 | + if not self.schema_features.boolean_schemas: |
| 3674 | + version_name = "Draft 4" if self.schema_features.id_field == "id" else "this version" |
| 3675 | + warn( |
| 3676 | + f"Boolean schemas are not supported in {version_name}. Schema path: {'/'.join(path)}", |
| 3677 | + stacklevel=3, |
| 3678 | + ) |
| 3679 | + return |
| 3680 | + |
| 3681 | + if not isinstance(raw, dict): |
| 3682 | + return |
| 3683 | + |
| 3684 | + # Check null in type array (Draft 2020-12 / OpenAPI 3.1+) |
| 3685 | + type_value = raw.get("type") |
| 3686 | + if isinstance(type_value, list) and "null" in type_value and not self.schema_features.null_in_type_array: |
| 3687 | + warn( |
| 3688 | + 'null in type array (e.g., type: ["string", "null"]) is not supported ' |
| 3689 | + f"in this schema version. Use nullable: true instead. Schema path: {'/'.join(path)}", |
| 3690 | + stacklevel=3, |
| 3691 | + ) |
| 3692 | + |
| 3693 | + # Check exclusive min/max format (Draft 4 uses boolean, Draft 6+ uses number) |
| 3694 | + exclusive_min = raw.get("exclusiveMinimum") |
| 3695 | + exclusive_max = raw.get("exclusiveMaximum") |
| 3696 | + if self.schema_features.exclusive_as_number: |
| 3697 | + # Draft 6+: should be numeric, not boolean |
| 3698 | + if isinstance(exclusive_min, bool): |
| 3699 | + warn( |
| 3700 | + f"exclusiveMinimum as boolean is Draft 4 style, but schema version uses numeric style. " |
| 3701 | + f"Schema path: {'/'.join(path)}", |
| 3702 | + stacklevel=3, |
| 3703 | + ) |
| 3704 | + if isinstance(exclusive_max, bool): |
| 3705 | + warn( |
| 3706 | + f"exclusiveMaximum as boolean is Draft 4 style, but schema version uses numeric style. " |
| 3707 | + f"Schema path: {'/'.join(path)}", |
| 3708 | + stacklevel=3, |
| 3709 | + ) |
| 3710 | + else: |
| 3711 | + # Draft 4: should be boolean, not numeric |
| 3712 | + if exclusive_min is not None and not isinstance(exclusive_min, bool): |
| 3713 | + warn( |
| 3714 | + f"exclusiveMinimum as number is Draft 6+ style, but schema version is Draft 4. " |
| 3715 | + f"Schema path: {'/'.join(path)}", |
| 3716 | + stacklevel=3, |
| 3717 | + ) |
| 3718 | + if exclusive_max is not None and not isinstance(exclusive_max, bool): |
| 3719 | + warn( |
| 3720 | + f"exclusiveMaximum as number is Draft 6+ style, but schema version is Draft 4. " |
| 3721 | + f"Schema path: {'/'.join(path)}", |
| 3722 | + stacklevel=3, |
| 3723 | + ) |
| 3724 | + |
| 3725 | + def _check_array_version_features( |
| 3726 | + self, |
| 3727 | + obj: JsonSchemaObject, |
| 3728 | + path: list[str], |
| 3729 | + ) -> None: |
| 3730 | + """Check for version-specific array features and warn in Strict mode. |
| 3731 | +
|
| 3732 | + Warns when prefixItems is used in versions that don't support it, |
| 3733 | + or when items as array (tuple style) is used in Draft 2020-12+. |
| 3734 | + """ |
| 3735 | + version_mode = getattr(self.config, "schema_version_mode", None) |
| 3736 | + if version_mode != VersionMode.Strict: |
| 3737 | + return |
| 3738 | + |
| 3739 | + # Check prefixItems usage (Draft 2020-12+ only) |
| 3740 | + if obj.prefixItems is not None and not self.schema_features.prefix_items: |
| 3741 | + warn( |
| 3742 | + f"prefixItems is not supported in this schema version. " |
| 3743 | + f"Use items as array for tuple validation. Schema path: {'/'.join(path)}", |
| 3744 | + stacklevel=4, |
| 3745 | + ) |
| 3746 | + |
| 3747 | + # Check items as array usage (deprecated in Draft 2020-12) |
| 3748 | + if isinstance(obj.items, list) and self.schema_features.prefix_items: |
| 3749 | + warn( |
| 3750 | + f"items as array (tuple validation) is deprecated in Draft 2020-12. " |
| 3751 | + f"Use prefixItems instead. Schema path: {'/'.join(path)}", |
| 3752 | + stacklevel=4, |
| 3753 | + ) |
| 3754 | + |
3651 | 3755 | def _handle_python_import( |
3652 | 3756 | self, |
3653 | 3757 | name: str, |
|
0 commit comments