diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index b99a1bfcc..ec8ee5ee2 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -39,8 +39,6 @@ jobs: name: py312-isort6 - tox_env: py312-isort5-parallel name: py312-isort5 - - tox_env: py312-pydantic1-parallel - name: py312-pydantic1 runs-on: ${{ matrix.os == '' && 'ubuntu-24.04' || matrix.os }} env: OS: ${{ matrix.os == '' && 'ubuntu-24.04' || matrix.os}} diff --git a/pyproject.toml b/pyproject.toml index 1880c8c23..789df4036 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -216,7 +216,6 @@ skip = '.git,*.lock,tests,docs/cli-reference,CHANGELOG.md,docs/changelog.md,docs [tool.pytest.ini_options] filterwarnings = [ "error", - "ignore:^.*Pydantic v1 runtime support is deprecated.*:DeprecationWarning", "ignore:^.*No --output-model-type specified.*:DeprecationWarning", "ignore:^.*Pydantic v2 with --use-annotated is recommended.*:DeprecationWarning", "ignore:^.*`--validation` option is deprecated.*", diff --git a/src/datamodel_code_generator/__init__.py b/src/datamodel_code_generator/__init__.py index 8417a03fb..d7138dda6 100644 --- a/src/datamodel_code_generator/__init__.py +++ b/src/datamodel_code_generator/__init__.py @@ -80,16 +80,9 @@ T = TypeVar("T") _ConfigT = TypeVar("_ConfigT", bound="ParserConfig") -# Import is_pydantic_v2 here for module-level YamlValue type definition -from datamodel_code_generator.util import is_pydantic_v2 # noqa: E402 - if not TYPE_CHECKING: # pragma: no branch YamlScalar: TypeAlias = str | int | float | bool | None - if is_pydantic_v2(): - YamlValue = TypeAliasType("YamlValue", "dict[str, YamlValue] | list[YamlValue] | YamlScalar") - else: - # Pydantic v1 cannot handle TypeAliasType, use Any for recursive parts - YamlValue: TypeAlias = dict[str, Any] | list[Any] | YamlScalar + YamlValue = TypeAliasType("YamlValue", "dict[str, YamlValue] | list[YamlValue] | YamlScalar") GeneratedModules: TypeAlias = dict[tuple[str, ...], str] @@ -471,19 +464,13 @@ def _create_parser_config( Filters GenerateConfig fields to only those expected by the parser config class, then merges with additional_options. """ - if is_pydantic_v2(): - parser_config_fields = set(config_class.model_fields.keys()) - all_options = { - k: v - for k, v in generate_config.model_dump().items() - if k in parser_config_fields and k not in additional_options - } | dict(additional_options) - return config_class.model_validate(all_options) - parser_config_fields = set(config_class.__fields__.keys()) + parser_config_fields = set(config_class.model_fields.keys()) all_options = { - k: v for k, v in generate_config.dict().items() if k in parser_config_fields and k not in additional_options + k: v + for k, v in generate_config.model_dump().items() + if k in parser_config_fields and k not in additional_options } | dict(additional_options) - return config_class.parse_obj(all_options) + return config_class.model_validate(all_options) def generate( # noqa: PLR0912, PLR0914, PLR0915 @@ -518,18 +505,11 @@ def generate( # noqa: PLR0912, PLR0914, PLR0915 raise ValueError(msg) if config is None: - if is_pydantic_v2(): - from datamodel_code_generator.model.pydantic_v2 import UnionMode # noqa: PLC0415 - from datamodel_code_generator.types import StrictTypes # noqa: PLC0415 + from datamodel_code_generator.model.pydantic_v2 import UnionMode # noqa: PLC0415 + from datamodel_code_generator.types import StrictTypes # noqa: PLC0415 - GenerateConfig.model_rebuild(_types_namespace={"StrictTypes": StrictTypes, "UnionMode": UnionMode}) - config = GenerateConfig.model_validate(options) - else: - from datamodel_code_generator.enums import UnionMode # noqa: PLC0415 - from datamodel_code_generator.types import StrictTypes # noqa: PLC0415 - - GenerateConfig.update_forward_refs(StrictTypes=StrictTypes, UnionMode=UnionMode) - config = GenerateConfig(**options) + GenerateConfig.model_rebuild(_types_namespace={"StrictTypes": StrictTypes, "UnionMode": UnionMode}) + config = GenerateConfig.model_validate(options) # Variables that may be modified during processing input_filename = config.input_filename @@ -979,13 +959,6 @@ def __getattr__(name: str) -> Any: if name in _LAZY_IMPORTS: import importlib # noqa: PLC0415 - if name == "GenerateConfig" and not is_pydantic_v2(): # pragma: no cover - msg = ( - f"'{name}' is only available in Pydantic v2 environments. " - "Use 'from datamodel_code_generator.config import GenerateConfig' instead." - ) - raise ImportError(msg) - module = importlib.import_module(_LAZY_IMPORTS[name]) return getattr(module, name) msg = f"module {__name__!r} has no attribute {name!r}" @@ -1034,5 +1007,4 @@ def __getattr__(name: str) -> Any: "generate_dynamic_models", # noqa: F822 ] -if is_pydantic_v2(): # pragma: no cover - __all__ += ["GenerateConfig"] +__all__ += ["GenerateConfig"] diff --git a/src/datamodel_code_generator/__main__.py b/src/datamodel_code_generator/__main__.py index c18d2c37d..1ab8db0a7 100644 --- a/src/datamodel_code_generator/__main__.py +++ b/src/datamodel_code_generator/__main__.py @@ -41,12 +41,12 @@ from collections import defaultdict from collections.abc import Callable, Mapping, Sequence # noqa: TC003 # pydantic needs it from enum import IntEnum -from io import TextIOBase +from io import TextIOBase # noqa: TC003 # needed for pydantic from pathlib import Path from typing import TYPE_CHECKING, Any, ClassVar, Optional, TypeAlias, Union, cast from urllib.parse import ParseResult, urlparse -from pydantic import BaseModel, ValidationError +from pydantic import BaseModel, ConfigDict, ValidationError, field_validator, model_validator from datamodel_code_generator import ( DEFAULT_SHARED_MODULE_NAME, @@ -87,13 +87,7 @@ from datamodel_code_generator.parser import LiteralType # noqa: TC001 # needed for pydantic from datamodel_code_generator.reference import is_url from datamodel_code_generator.types import StrictTypes # noqa: TC001 # needed for pydantic -from datamodel_code_generator.util import ( - ConfigDict, - field_validator, - is_pydantic_v2, - load_toml, - model_validator, -) +from datamodel_code_generator.util import load_toml from datamodel_code_generator.validators import ValidatorsConfig if TYPE_CHECKING: @@ -144,42 +138,28 @@ def sig_int_handler(_: int, __: Any) -> None: # pragma: no cover signal.signal(signal.SIGINT, sig_int_handler) -class Config(BaseModel): +class Config(BaseModel): # noqa: PLR0904 """Configuration model for code generation.""" - if is_pydantic_v2(): - model_config = ConfigDict(arbitrary_types_allowed=True) # ty: ignore - - def get(self, item: str) -> Any: # pragma: no cover - """Get attribute value by name.""" - return getattr(self, item) - - def __getitem__(self, item: str) -> Any: # pragma: no cover - """Get item by key.""" - return self.get(item) # ty: ignore + model_config = ConfigDict(arbitrary_types_allowed=True) # ty: ignore - @classmethod - def parse_obj(cls, obj: Any) -> Self: - """Parse object into Config model.""" - return cls.model_validate(obj) + def get(self, item: str) -> Any: # pragma: no cover + """Get attribute value by name.""" + return getattr(self, item) - @classmethod - def get_fields(cls) -> dict[str, Any]: - """Get model fields.""" - return cls.model_fields - - else: + def __getitem__(self, item: str) -> Any: # pragma: no cover + """Get item by key.""" + return self.get(item) # ty: ignore - class Config: - """Pydantic v1 configuration.""" + @classmethod + def parse_obj(cls, obj: Any) -> Self: + """Parse object into Config model.""" + return cls.model_validate(obj) - # Pydantic 1.5.1 doesn't support validate_assignment correctly - arbitrary_types_allowed = (TextIOBase,) - - @classmethod - def get_fields(cls) -> dict[str, Any]: - """Get model fields.""" - return cls.__fields__ + @classmethod + def get_fields(cls) -> dict[str, Any]: + """Get model fields.""" + return cls.model_fields @field_validator( "aliases", "extra_template_data", "custom_formatters_kwargs", "validators", "default_values", mode="before" @@ -363,156 +343,78 @@ def validate_external_ref_mapping(cls, values: dict[str, Any]) -> dict[str, Any] "`--all-exports-collision-strategy` can only be used with `--all-exports-scope=recursive`." ) - if is_pydantic_v2(): - - @model_validator() # ty: ignore - def validate_output_datetime_class(self: Self) -> Self: # ty: ignore - """Validate output datetime class compatibility.""" - datetime_class_type: DatetimeClassType | None = self.output_datetime_class - if ( - datetime_class_type - and datetime_class_type is not DatetimeClassType.Datetime - and self.output_model_type == DataModelType.DataclassesDataclass - ): - raise Error(self.__validate_output_datetime_class_err) - return self - - @model_validator() # ty: ignore - def validate_original_field_name_delimiter(self: Self) -> Self: # ty: ignore - """Validate original field name delimiter requires snake case.""" - if self.original_field_name_delimiter is not None and not self.snake_case_field: - raise Error(self.__validate_original_field_name_delimiter_err) - return self - - @model_validator() # ty: ignore - def validate_custom_file_header(self: Self) -> Self: # ty: ignore - """Validate custom file header options are mutually exclusive.""" - if self.custom_file_header and self.custom_file_header_path: - raise Error(self.__validate_custom_file_header_err) - return self - - @model_validator() # ty: ignore - def validate_keyword_only(self: Self) -> Self: # ty: ignore - """Validate keyword-only compatibility with target Python version.""" - output_model_type: DataModelType = self.output_model_type - python_target: PythonVersion = self.target_python_version - if ( - self.keyword_only - and output_model_type == DataModelType.DataclassesDataclass - and not python_target.has_kw_only_dataclass - ): - raise Error(self.__validate_keyword_only_err) # pragma: no cover - return self - - @model_validator() # ty: ignore - def validate_root(self: Self) -> Self: # ty: ignore - """Validate root model configuration.""" - if self.use_annotated: - self.field_constraints = True - return self - - @model_validator() # ty: ignore - def validate_all_exports_collision_strategy(self: Self) -> Self: # ty: ignore - """Validate all_exports_collision_strategy requires recursive scope.""" - if self.all_exports_collision_strategy is not None and self.all_exports_scope != AllExportsScope.Recursive: - raise Error(self.__validate_all_exports_collision_strategy_err) - return self - - from pydantic import field_validator as _field_validator # noqa: PLC0415 - - @_field_validator("input_model", mode="before") - @classmethod - def coerce_input_model_to_list(cls, v: str | list[str] | None) -> list[str] | None: # ty: ignore - """Convert string input_model to list for backwards compatibility.""" - if isinstance(v, str): - return [v] - return v - - @_field_validator("class_name_affix_scope", mode="before") - @classmethod - def validate_class_name_affix_scope(cls, v: str | ClassNameAffixScope | None) -> ClassNameAffixScope: # ty: ignore - """Convert string to ClassNameAffixScope enum.""" - if v is None: # pragma: no cover - return ClassNameAffixScope.All - if isinstance(v, str): - return ClassNameAffixScope(v) - return v # pragma: no cover - - else: - - @model_validator() # ty: ignore - def validate_output_datetime_class(cls, values: dict[str, Any]) -> dict[str, Any]: # noqa: N805 - """Validate output datetime class compatibility.""" - datetime_class_type: DatetimeClassType | None = values.get("output_datetime_class") - if ( - datetime_class_type - and datetime_class_type is not DatetimeClassType.Datetime - and values.get("output_model_type") == DataModelType.DataclassesDataclass - ): - raise Error(cls.__validate_output_datetime_class_err) - return values - - @model_validator() # ty: ignore - def validate_original_field_name_delimiter(cls, values: dict[str, Any]) -> dict[str, Any]: # noqa: N805 - """Validate original field name delimiter requires snake case.""" - if values.get("original_field_name_delimiter") is not None and not values.get("snake_case_field"): - raise Error(cls.__validate_original_field_name_delimiter_err) - return values - - @model_validator() # ty: ignore - def validate_custom_file_header(cls, values: dict[str, Any]) -> dict[str, Any]: # noqa: N805 - """Validate custom file header options are mutually exclusive.""" - if values.get("custom_file_header") and values.get("custom_file_header_path"): - raise Error(cls.__validate_custom_file_header_err) - return values - - @model_validator() # ty: ignore - def validate_keyword_only(cls, values: dict[str, Any]) -> dict[str, Any]: # noqa: N805 - """Validate keyword-only compatibility with target Python version.""" - output_model_type: DataModelType = cast("DataModelType", values.get("output_model_type")) - python_target: PythonVersion = cast("PythonVersion", values.get("target_python_version")) - if ( - values.get("keyword_only") - and output_model_type == DataModelType.DataclassesDataclass - and not python_target.has_kw_only_dataclass - ): - raise Error(cls.__validate_keyword_only_err) # pragma: no cover - return values - - @model_validator() # ty: ignore - def validate_root(cls, values: dict[str, Any]) -> dict[str, Any]: # noqa: N805 - """Validate root model configuration.""" - if values.get("use_annotated"): - values["field_constraints"] = True - return values - - @model_validator() # ty: ignore - def validate_all_exports_collision_strategy(cls, values: dict[str, Any]) -> dict[str, Any]: # noqa: N805 - """Validate all_exports_collision_strategy requires recursive scope.""" - if ( - values.get("all_exports_collision_strategy") is not None - and values.get("all_exports_scope") != AllExportsScope.Recursive - ): - raise Error(cls.__validate_all_exports_collision_strategy_err) - return values - - @field_validator("input_model", mode="before") # pragma: no cover - @classmethod - def coerce_input_model_to_list(cls, v: str | list[str] | None) -> list[str] | None: - """Convert string input_model to list for backwards compatibility.""" - if isinstance(v, str): - return [v] - return v - - @field_validator("class_name_affix_scope", mode="before") # pragma: no cover - @classmethod - def validate_class_name_affix_scope(cls, v: str | ClassNameAffixScope | None) -> ClassNameAffixScope: - """Convert string to ClassNameAffixScope enum.""" - if v is None: - return ClassNameAffixScope.All - if isinstance(v, str): - return ClassNameAffixScope(v) - return v + @model_validator(mode="after") # ty: ignore + def validate_output_datetime_class(self: Self) -> Self: # ty: ignore + """Validate output datetime class compatibility.""" + datetime_class_type: DatetimeClassType | None = self.output_datetime_class + if ( + datetime_class_type + and datetime_class_type is not DatetimeClassType.Datetime + and self.output_model_type == DataModelType.DataclassesDataclass + ): + raise Error(self.__validate_output_datetime_class_err) + return self + + @model_validator(mode="after") # ty: ignore + def validate_original_field_name_delimiter(self: Self) -> Self: # ty: ignore + """Validate original field name delimiter requires snake case.""" + if self.original_field_name_delimiter is not None and not self.snake_case_field: + raise Error(self.__validate_original_field_name_delimiter_err) + return self + + @model_validator(mode="after") # ty: ignore + def validate_custom_file_header(self: Self) -> Self: # ty: ignore + """Validate custom file header options are mutually exclusive.""" + if self.custom_file_header and self.custom_file_header_path: + raise Error(self.__validate_custom_file_header_err) + return self + + @model_validator(mode="after") # ty: ignore + def validate_keyword_only(self: Self) -> Self: # ty: ignore + """Validate keyword-only compatibility with target Python version.""" + output_model_type: DataModelType = self.output_model_type + python_target: PythonVersion = self.target_python_version + if ( + self.keyword_only + and output_model_type == DataModelType.DataclassesDataclass + and not python_target.has_kw_only_dataclass + ): + raise Error(self.__validate_keyword_only_err) # pragma: no cover + return self + + @model_validator(mode="after") # ty: ignore + def validate_root(self: Self) -> Self: # ty: ignore + """Validate root model configuration.""" + if self.use_annotated: + self.field_constraints = True + return self + + @model_validator(mode="after") # ty: ignore + def validate_all_exports_collision_strategy(self: Self) -> Self: # ty: ignore + """Validate all_exports_collision_strategy requires recursive scope.""" + if self.all_exports_collision_strategy is not None and self.all_exports_scope != AllExportsScope.Recursive: + raise Error(self.__validate_all_exports_collision_strategy_err) + return self + + from pydantic import field_validator as _field_validator # noqa: PLC0415 + + @_field_validator("input_model", mode="before") + @classmethod + def coerce_input_model_to_list(cls, v: str | list[str] | None) -> list[str] | None: # ty: ignore + """Convert string input_model to list for backwards compatibility.""" + if isinstance(v, str): + return [v] + return v + + @_field_validator("class_name_affix_scope", mode="before") + @classmethod + def validate_class_name_affix_scope(cls, v: str | ClassNameAffixScope | None) -> ClassNameAffixScope: # ty: ignore + """Convert string to ClassNameAffixScope enum.""" + if v is None: # pragma: no cover + return ClassNameAffixScope.All + if isinstance(v, str): + return ClassNameAffixScope(v) + return v # pragma: no cover input: Optional[Union[Path, str]] = None # noqa: UP007, UP045 input_model: Optional[list[str]] = None # noqa: UP045 @@ -938,9 +840,6 @@ def _load_validators_config( if file_handle is None: return None, None - if ValidatorsConfig is None: - return None, "--validators option requires Pydantic v2. Please upgrade to Pydantic v2 or remove the option." - with file_handle as data: try: raw = json.load(data) @@ -1260,14 +1159,6 @@ def main(args: Sequence[str] | None = None) -> Exit: # noqa: PLR0911, PLR0912, stacklevel=1, ) - if not is_pydantic_v2(): - warnings.warn( - "Pydantic v1 runtime support is deprecated and will be removed in a future version. " - "Please upgrade to Pydantic v2.", - DeprecationWarning, - stacklevel=1, - ) - if config.reuse_scope == ReuseScope.Tree and not config.reuse_model: print( # noqa: T201 "Warning: --reuse-scope=tree has no effect without --reuse-model", diff --git a/src/datamodel_code_generator/config.py b/src/datamodel_code_generator/config.py index bb09f49eb..ef8252faa 100644 --- a/src/datamodel_code_generator/config.py +++ b/src/datamodel_code_generator/config.py @@ -7,7 +7,7 @@ from pathlib import Path # noqa: TC003 - used at runtime by Pydantic from typing import TYPE_CHECKING, Annotated, Any -from pydantic import BaseModel, Field +from pydantic import BaseModel, ConfigDict, Field from datamodel_code_generator.enums import ( DEFAULT_SHARED_MODULE_NAME, @@ -49,7 +49,6 @@ from datamodel_code_generator.model.union import DataTypeUnion from datamodel_code_generator.parser import DefaultPutDict, LiteralType from datamodel_code_generator.types import DataTypeManager, StrictTypes # noqa: TC001 - used by Pydantic at runtime -from datamodel_code_generator.util import ConfigDict, is_pydantic_v2 from datamodel_code_generator.validators import ModelValidators # noqa: TC001 - used by Pydantic at runtime CallableSchema = Callable[[str], str] @@ -57,24 +56,14 @@ DefaultPutDictSchema = DefaultPutDict[str, str] if TYPE_CHECKING: ExtraTemplateDataType = defaultdict[str, dict[str, Any]] -elif is_pydantic_v2(): +else: ExtraTemplateDataType = defaultdict[str, Annotated[dict[str, Any], Field(default_factory=dict)]] -else: # pragma: no cover - ExtraTemplateDataType = defaultdict[str, dict[str, Any]] class GenerateConfig(BaseModel): """Configuration model for generate().""" - if is_pydantic_v2(): - model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True) - else: # pragma: no cover - - class Config: - """Pydantic v1 model config.""" - - extra = "forbid" - arbitrary_types_allowed = True + model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True) input_filename: str | None = None input_file_type: InputFileType = InputFileType.Auto @@ -214,15 +203,7 @@ class Config: class ParserConfig(BaseModel): """Configuration model for Parser.__init__().""" - if is_pydantic_v2(): - model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True) - else: # pragma: no cover - - class Config: - """Pydantic v1 model config.""" - - extra = "forbid" - arbitrary_types_allowed = True + model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True) data_model_type: type[DataModel] = pydantic_model.BaseModel data_model_root_type: type[DataModel] = pydantic_model.CustomRootType @@ -373,15 +354,7 @@ class OpenAPIParserConfig(JSONSchemaParserConfig): class ParseConfig(BaseModel): """Configuration model for Parser.parse().""" - if is_pydantic_v2(): - model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True) - else: # pragma: no cover - - class Config: - """Pydantic v1 model config.""" - - extra = "forbid" - arbitrary_types_allowed = True + model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True) with_import: bool | None = True format_: bool | None = True diff --git a/src/datamodel_code_generator/input_model.py b/src/datamodel_code_generator/input_model.py index 79af70c62..4114d5585 100644 --- a/src/datamodel_code_generator/input_model.py +++ b/src/datamodel_code_generator/input_model.py @@ -767,13 +767,6 @@ def load_model_schema( # noqa: PLR0912, PLR0914, PLR0915 msg = f"Multiple --input-model only supports Pydantic v2 BaseModel classes, got {type(obj).__name__}" raise Error(msg) - if not hasattr(obj, "model_json_schema"): - msg = ( - "Multiple --input-model with Pydantic model requires Pydantic v2 runtime. " - "Please upgrade Pydantic to v2." - ) - raise Error(msg) - model_classes.append(obj) if input_file_type not in {InputFileType.Auto, InputFileType.JsonSchema}: @@ -907,9 +900,6 @@ def _load_single_model_schema( # noqa: PLR0912, PLR0914, PLR0915 f"got '{input_file_type.value}'" ) raise Error(msg) - if not hasattr(obj, "model_json_schema"): - msg = "--input-model with Pydantic model requires Pydantic v2 runtime. Please upgrade Pydantic to v2." - raise Error(msg) _try_rebuild_model(obj) schema_generator = _get_input_model_json_schema_class() schema = obj.model_json_schema(schema_generator=schema_generator) @@ -938,22 +928,18 @@ def _load_single_model_schema( # noqa: PLR0912, PLR0914, PLR0915 f"got '{input_file_type.value}'" ) raise Error(msg) - try: - from pydantic import TypeAdapter # noqa: PLC0415 - - schema = TypeAdapter(obj).json_schema() - schema = _add_python_type_info_generic(schema, cast("type", obj)) - - if ref_strategy and ref_strategy != InputModelRefStrategy.RegenerateAll: - obj_type = cast("type", obj) - nested_models = _collect_nested_models(obj_type) - obj_name = getattr(obj, "__name__", None) - if obj_name and "$defs" in schema and obj_name in schema["$defs"]: # pragma: no cover - nested_models[obj_name] = obj_type - schema = _filter_defs_by_strategy(schema, nested_models, output_model_type, ref_strategy) - except ImportError as e: - msg = "--input-model with dataclass/TypedDict requires Pydantic v2 runtime." - raise Error(msg) from e + from pydantic import TypeAdapter # noqa: PLC0415 + + schema = TypeAdapter(obj).json_schema() + schema = _add_python_type_info_generic(schema, cast("type", obj)) + + if ref_strategy and ref_strategy != InputModelRefStrategy.RegenerateAll: + obj_type = cast("type", obj) + nested_models = _collect_nested_models(obj_type) + obj_name = getattr(obj, "__name__", None) + if obj_name and "$defs" in schema and obj_name in schema["$defs"]: # pragma: no cover + nested_models[obj_name] = obj_type + schema = _filter_defs_by_strategy(schema, nested_models, output_model_type, ref_strategy) return schema diff --git a/src/datamodel_code_generator/model/base.py b/src/datamodel_code_generator/model/base.py index d34a81cc0..9ad058c65 100644 --- a/src/datamodel_code_generator/model/base.py +++ b/src/datamodel_code_generator/model/base.py @@ -15,7 +15,7 @@ from typing import TYPE_CHECKING, Any, ClassVar, Optional, TypeVar, Union from warnings import warn -from pydantic import Field +from pydantic import ConfigDict, Field from typing_extensions import Self from datamodel_code_generator import cached_path_exists @@ -37,7 +37,6 @@ chain_as_tuple, get_optional_type, ) -from datamodel_code_generator.util import ConfigDict, is_pydantic_v2, model_copy, model_dump, model_validate __all__ = ["WrappedDefault"] @@ -103,35 +102,27 @@ class ConstraintsBase(_BaseModel): unique_items: Optional[bool] = Field(None, alias="uniqueItems") # noqa: UP045 _exclude_fields: ClassVar[set[str]] = {"has_constraints"} - if is_pydantic_v2(): - model_config = ConfigDict( # ty: ignore - arbitrary_types_allowed=True, ignored_types=(cached_property,) - ) - else: - - class Config: - """Pydantic v1 configuration for ConstraintsBase.""" - - arbitrary_types_allowed = True - keep_untouched = (cached_property,) + model_config = ConfigDict( # ty: ignore + arbitrary_types_allowed=True, ignored_types=(cached_property,) + ) @cached_property def has_constraints(self) -> bool: """Check if any constraint values are set.""" - return any(v is not None for v in model_dump(self).values()) + return any(v is not None for v in self.model_dump().values()) @staticmethod def merge_constraints(a: ConstraintsBaseT | None, b: ConstraintsBaseT | None) -> ConstraintsBaseT | None: """Merge two constraint objects, with b taking precedence over a.""" constraints_class = None if isinstance(a, ConstraintsBase): # pragma: no cover - root_type_field_constraints = {k: v for k, v in model_dump(a, by_alias=True).items() if v is not None} + root_type_field_constraints = {k: v for k, v in a.model_dump(by_alias=True).items() if v is not None} constraints_class = a.__class__ else: root_type_field_constraints = {} # pragma: no cover if isinstance(b, ConstraintsBase): # pragma: no cover - model_field_constraints = {k: v for k, v in model_dump(b, by_alias=True).items() if v is not None} + model_field_constraints = {k: v for k, v in b.model_dump(by_alias=True).items() if v is not None} constraints_class = constraints_class or b.__class__ else: model_field_constraints = {} @@ -139,8 +130,7 @@ def merge_constraints(a: ConstraintsBaseT | None, b: ConstraintsBaseT | None) -> if constraints_class is None or not issubclass(constraints_class, ConstraintsBase): # pragma: no cover return None - return model_validate( - constraints_class, + return constraints_class.model_validate( { **root_type_field_constraints, **model_field_constraints, @@ -151,17 +141,10 @@ def merge_constraints(a: ConstraintsBaseT | None, b: ConstraintsBaseT | None) -> class DataModelFieldBase(_BaseModel): """Base class for model field representation and rendering.""" - if is_pydantic_v2(): - model_config = ConfigDict( # ty: ignore - arbitrary_types_allowed=True, - defer_build=True, - ) - else: - - class Config: - """Pydantic v1 configuration for DataModelFieldBase.""" - - arbitrary_types_allowed = True + model_config = ConfigDict( # ty: ignore + arbitrary_types_allowed=True, + defer_build=True, + ) name: Optional[str] = None # noqa: UP045 default: Optional[Any] = None # noqa: UP045 @@ -195,17 +178,6 @@ class Config: use_default_factory_for_optional_nested_models: bool = False if not TYPE_CHECKING: # pragma: no branch - if not is_pydantic_v2(): - - @classmethod - def model_rebuild( - cls, - *, - _types_namespace: dict[str, type] | None = None, - ) -> None: - """Update forward references for Pydantic v1.""" - localns = _types_namespace or {} - cls.update_forward_refs(**localns) def __init__(self, **data: Any) -> None: """Initialize the field and set up parent relationships.""" @@ -445,14 +417,14 @@ def fall_back_to_nullable(self) -> bool: def copy_deep(self) -> Self: """Create a deep copy of this field to avoid mutating the original.""" - copied = model_copy(self) + copied = self.model_copy() copied.parent = None copied.extras = deepcopy(self.extras) - copied.data_type = model_copy(self.data_type) + copied.data_type = self.data_type.model_copy() if self.data_type.data_types: - copied.data_type.data_types = [model_copy(dt) for dt in self.data_type.data_types] + copied.data_type.data_types = [dt.model_copy() for dt in self.data_type.data_types] if self.data_type.dict_key: - copied.data_type.dict_key = model_copy(self.data_type.dict_key) + copied.data_type.dict_key = self.data_type.dict_key.model_copy() return copied def replace_data_type(self, new_data_type: DataType, *, clear_old_parent: bool = True) -> None: @@ -942,13 +914,7 @@ def render(self, *, class_name: str | None = None) -> str: ) -if is_pydantic_v2(): - _rebuild_namespace = {"Union": Union, "DataModelFieldBase": DataModelFieldBase, "DataType": DataType} - DataType.model_rebuild(_types_namespace=_rebuild_namespace) - BaseClassDataType.model_rebuild(_types_namespace=_rebuild_namespace) - DataModelFieldBase.model_rebuild(_types_namespace={"DataModel": DataModel}) -else: - _rebuild_namespace = {"Union": Union, "DataModelFieldBase": DataModelFieldBase, "DataType": DataType} - DataType.model_rebuild(_types_namespace=_rebuild_namespace) - BaseClassDataType.model_rebuild(_types_namespace=_rebuild_namespace) - DataModelFieldBase.model_rebuild(_types_namespace={"DataModel": DataModel}) +_rebuild_namespace = {"Union": Union, "DataModelFieldBase": DataModelFieldBase, "DataType": DataType} +DataType.model_rebuild(_types_namespace=_rebuild_namespace) +BaseClassDataType.model_rebuild(_types_namespace=_rebuild_namespace) +DataModelFieldBase.model_rebuild(_types_namespace={"DataModel": DataModel}) diff --git a/src/datamodel_code_generator/model/msgspec.py b/src/datamodel_code_generator/model/msgspec.py index ab400c13d..2119f41d9 100644 --- a/src/datamodel_code_generator/model/msgspec.py +++ b/src/datamodel_code_generator/model/msgspec.py @@ -47,7 +47,6 @@ _remove_none_from_union, chain_as_tuple, ) -from datamodel_code_generator.util import model_dump UNSET_TYPE = "UnsetType" @@ -390,7 +389,7 @@ def _get_meta_string(self) -> str | None: **data, **{ k: self._get_strict_field_constraint_value(k, v) - for k, v in model_dump(self.constraints).items() + for k, v in self.constraints.model_dump().items() if k in self._META_FIELD_KEYS }, } diff --git a/src/datamodel_code_generator/model/pydantic_v2/__init__.py b/src/datamodel_code_generator/model/pydantic_v2/__init__.py index 3bc60384e..9cc0060f1 100644 --- a/src/datamodel_code_generator/model/pydantic_v2/__init__.py +++ b/src/datamodel_code_generator/model/pydantic_v2/__init__.py @@ -47,12 +47,8 @@ class ConfigDict(_BaseModel): json_schema_extra: Optional[Dict[str, Any]] = None # noqa: UP006, UP045 def dict(self, **kwargs: Any) -> dict[str, Any]: # type: ignore[override] - """Version-compatible dict method for templates.""" - from datamodel_code_generator.util import is_pydantic_v2 # noqa: PLC0415 - - if is_pydantic_v2(): - return self.model_dump(**kwargs) - return super().dict(**kwargs) # pragma: no cover + """Return dict for templates.""" + return self.model_dump(**kwargs) __all__ = [ diff --git a/src/datamodel_code_generator/model/pydantic_v2/base_model.py b/src/datamodel_code_generator/model/pydantic_v2/base_model.py index 0d67c2882..3fb3078d3 100644 --- a/src/datamodel_code_generator/model/pydantic_v2/base_model.py +++ b/src/datamodel_code_generator/model/pydantic_v2/base_model.py @@ -10,7 +10,7 @@ from collections import defaultdict from typing import TYPE_CHECKING, Any, ClassVar, Literal, NamedTuple, Optional -from pydantic import Field +from pydantic import Field, field_validator, model_validator from datamodel_code_generator.imports import IMPORT_ANY, Import from datamodel_code_generator.model.base import ALL_MODEL, UNDEFINED, BaseClassDataType, DataModelFieldBase @@ -34,7 +34,6 @@ ) from datamodel_code_generator.reference import ModelResolver from datamodel_code_generator.types import chain_as_tuple -from datamodel_code_generator.util import field_validator, model_validate, model_validator if TYPE_CHECKING: from pathlib import Path @@ -308,7 +307,7 @@ def __init__( # noqa: PLR0913 if config_parameters: from datamodel_code_generator.model.pydantic_v2 import ConfigDict # noqa: PLC0415 - self.extra_template_data["config"] = model_validate(ConfigDict, config_parameters) # ty: ignore + self.extra_template_data["config"] = ConfigDict.model_validate(config_parameters) # ty: ignore self._additional_imports.append(IMPORT_CONFIG_DICT) self._process_validators() diff --git a/src/datamodel_code_generator/parser/base.py b/src/datamodel_code_generator/parser/base.py index fe01102b6..9fb909494 100644 --- a/src/datamodel_code_generator/parser/base.py +++ b/src/datamodel_code_generator/parser/base.py @@ -84,7 +84,7 @@ from datamodel_code_generator.parser._scc import find_circular_sccs, strongly_connected_components from datamodel_code_generator.reference import ModelResolver, ModelType, Reference from datamodel_code_generator.types import ANY, DataType, DataTypeManager -from datamodel_code_generator.util import camel_to_snake, model_copy, model_dump +from datamodel_code_generator.util import camel_to_snake if TYPE_CHECKING: from collections.abc import Iterable, Iterator, Sequence @@ -379,7 +379,7 @@ def to_hashable(item: Any) -> HashableComparable: # noqa: PLR0911 if isinstance(item, set): # pragma: no cover return frozenset(to_hashable(i) for i in item) # type: ignore[return-value] if isinstance(item, BaseModel): # pragma: no cover - return to_hashable(model_dump(item)) + return to_hashable(item.model_dump()) if item is None: return "" return item # type: ignore[return-value] @@ -780,11 +780,11 @@ def _copy_data_types(data_types: list[DataType]) -> list[DataType]: if data_type_.reference: copied_data_types.append(data_type_.__class__(reference=data_type_.reference)) elif data_type_.data_types: # pragma: no cover - copied_data_type = model_copy(data_type_) + copied_data_type = data_type_.model_copy() copied_data_type.data_types = _copy_data_types(data_type_.data_types) copied_data_types.append(copied_data_type) else: - copied_data_types.append(model_copy(data_type_)) + copied_data_types.append(data_type_.model_copy()) return copied_data_types @@ -860,29 +860,18 @@ def _create_default_config(cls, options: ParserConfigDict) -> ParserConfigT: # """ from datamodel_code_generator import types as types_module # noqa: PLC0415 from datamodel_code_generator.model import base as model_base # noqa: PLC0415 - from datamodel_code_generator.util import is_pydantic_v2 # noqa: PLC0415 config_class = cls._get_config_class() - if is_pydantic_v2(): - config_class.model_rebuild( - _types_namespace={ - "StrictTypes": types_module.StrictTypes, - "DataModel": model_base.DataModel, - "DataModelFieldBase": model_base.DataModelFieldBase, - "DataTypeManager": types_module.DataTypeManager, - } - ) - return config_class.model_validate(options) # type: ignore[return-value] - config_class.update_forward_refs( - StrictTypes=types_module.StrictTypes, - DataModel=model_base.DataModel, - DataModelFieldBase=model_base.DataModelFieldBase, - DataTypeManager=types_module.DataTypeManager, + config_class.model_rebuild( + _types_namespace={ + "StrictTypes": types_module.StrictTypes, + "DataModel": model_base.DataModel, + "DataModelFieldBase": model_base.DataModelFieldBase, + "DataTypeManager": types_module.DataTypeManager, + } ) - defaults = {name: field.default for name, field in config_class.__fields__.items()} - defaults.update(options) # ty: ignore - return config_class.construct(**defaults) # type: ignore[return-value] + return config_class.model_validate(options) # type: ignore[return-value] def __init__( # noqa: PLR0912, PLR0915 self, @@ -1001,7 +990,7 @@ def __init__( # noqa: PLR0912, PLR0915 if self.validators: for model_name, model_config in self.validators.items(): self.extra_template_data[model_name]["validators"] = [ - model_dump(v, mode="json") for v in model_config.validators + v.model_dump(mode="json") for v in model_config.validators ] self.use_generic_base_class: bool = config.use_generic_base_class @@ -1704,7 +1693,7 @@ def check_paths( @classmethod def _create_set_from_list(cls, data_type: DataType) -> DataType | None: if data_type.is_list: - new_data_type = model_copy(data_type) + new_data_type = data_type.model_copy() new_data_type.is_list = False new_data_type.is_set = True for data_type_ in new_data_type.data_types: @@ -2025,7 +2014,7 @@ def __collapse_root_models( # noqa: PLR0912, PLR0914, PLR0915 continue # set copied data_type - copied_data_type = model_copy(root_type_field.data_type) + copied_data_type = root_type_field.data_type.model_copy() if isinstance(data_type.parent, self.data_model_field_type): # for field # override empty field by root-type field @@ -2194,18 +2183,18 @@ def __override_required_field( if not original_field: # pragma: no cover model.fields.remove(model_field) continue - copied_original_field = model_copy(original_field) + copied_original_field = original_field.model_copy() if original_field.data_type.reference: data_type = self.data_type_manager.data_type( reference=original_field.data_type.reference, ) elif original_field.data_type.data_types: - data_type = model_copy(original_field.data_type) + data_type = original_field.data_type.model_copy() data_type.data_types = _copy_data_types(original_field.data_type.data_types) for data_type_ in data_type.data_types: data_type_.parent = data_type else: - data_type = model_copy(original_field.data_type) + data_type = original_field.data_type.model_copy() data_type.parent = copied_original_field copied_original_field.data_type = data_type copied_original_field.parent = model diff --git a/src/datamodel_code_generator/parser/jsonschema.py b/src/datamodel_code_generator/parser/jsonschema.py index 8cf6733f8..9fd49b894 100644 --- a/src/datamodel_code_generator/parser/jsonschema.py +++ b/src/datamodel_code_generator/parser/jsonschema.py @@ -20,7 +20,10 @@ from warnings import warn from pydantic import ( + ConfigDict, Field, + field_validator, + model_validator, ) from typing_extensions import Unpack @@ -70,19 +73,7 @@ get_subscript_args, get_type_base_name, ) -from datamodel_code_generator.util import ( - BaseModel, - field_validator, - get_fields_set, - is_pydantic_v2, - model_copy, - model_dump, - model_validate, - model_validator, -) - -if is_pydantic_v2(): - from pydantic import ConfigDict +from datamodel_code_generator.util import BaseModel if TYPE_CHECKING: from collections.abc import Callable, Generator, Iterable, Iterator @@ -208,24 +199,11 @@ class JsonSchemaObject(BaseModel): """Represent a JSON Schema object with validation and parsing capabilities.""" if not TYPE_CHECKING: # pragma: no branch - if is_pydantic_v2(): - - @classmethod - def get_fields(cls) -> dict[str, Any]: - """Get fields for Pydantic v2 models.""" - return cls.model_fields - else: - - @classmethod - def get_fields(cls) -> dict[str, Any]: - """Get fields for Pydantic v1 models.""" - return cls.__fields__ - - @classmethod - def model_rebuild(cls) -> None: - """Rebuild model by updating forward references.""" - cls.update_forward_refs() + @classmethod + def get_fields(cls) -> dict[str, Any]: + """Get fields for Pydantic v2 models.""" + return cls.model_fields __constraint_fields__: set[str] = { # noqa: RUF012 "exclusiveMinimum", @@ -388,19 +366,10 @@ def validate_null_type(cls, value: Any) -> Any: # noqa: N805 custom_base_path: str | list[str] | None = Field(default=None, alias="customBasePath") extras: dict[str, Any] = Field(alias=__extra_key__, default_factory=dict) discriminator: Optional[Union[Discriminator, str]] = None # noqa: UP007, UP045 - if is_pydantic_v2(): - model_config = ConfigDict( # ty: ignore - arbitrary_types_allowed=True, - ignored_types=(cached_property,), - ) - else: - - class Config: - """Pydantic v1 configuration for JsonSchemaObject.""" - - arbitrary_types_allowed = True - keep_untouched = (cached_property,) - smart_casts = True + model_config = ConfigDict( # ty: ignore + arbitrary_types_allowed=True, + ignored_types=(cached_property,), + ) def __init__(self, **data: Any) -> None: """Initialize JsonSchemaObject with extra fields handling.""" @@ -417,7 +386,7 @@ def __init__(self, **data: Any) -> None: if "x-propertyNames" in self.extras and self.propertyNames is None: x_prop_names = self.extras.pop("x-propertyNames") if isinstance(x_prop_names, dict): - self.propertyNames = model_validate(JsonSchemaObject, x_prop_names) + self.propertyNames = JsonSchemaObject.model_validate(x_prop_names) @cached_property def is_object(self) -> bool: @@ -445,12 +414,12 @@ def validate_items(cls, values: Any) -> Any: # noqa: N805 @cached_property def has_default(self) -> bool: """Check if the schema has a default value or default factory.""" - return "default" in get_fields_set(self) or "default_factory" in self.extras + return "default" in self.model_fields_set or "default_factory" in self.extras @cached_property def has_constraint(self) -> bool: """Check if the schema has any constraint fields set.""" - return bool(self.__constraint_fields__ & get_fields_set(self)) + return bool(self.__constraint_fields__ & self.model_fields_set) @cached_property def ref_type(self) -> JSONReference | None: @@ -490,7 +459,7 @@ def has_ref_with_schema_keywords(self) -> bool: """ if not self.ref: return False - other_fields = get_fields_set(self) - {"ref"} + other_fields = self.model_fields_set - {"ref"} schema_affecting_fields = other_fields - self.__metadata_only_fields__ - {"extras"} if self.extras: schema_affecting_extras = {k for k in self.extras if k in self.__schema_affecting_extras__} @@ -508,7 +477,7 @@ def is_ref_with_nullable_only(self) -> bool: """ if not self.ref or self.nullable is not True: return False - other_fields = get_fields_set(self) - {"ref", "nullable"} - self.__metadata_only_fields__ - {"extras"} + other_fields = self.model_fields_set - {"ref", "nullable"} - self.__metadata_only_fields__ - {"extras"} if other_fields: return False if self.extras: @@ -1236,7 +1205,7 @@ def get_object_field( # noqa: PLR0913 default_value = effective_default if effective_has_default is not None else field.default has_default = effective_has_default if effective_has_default is not None else field.has_default - constraints = model_dump(field, exclude_none=True) if self.is_constraints_field(field) else None + constraints = field.model_dump(exclude_none=True) if self.is_constraints_field(field) else None consumed = self.data_type_manager.CONSTRAINED_TYPE_CONSUMED_KEYS if constraints is not None and field_type.type in consumed: for key in consumed[field_type.type]: @@ -1317,7 +1286,7 @@ def _get_data_type(type_: str, format__: str) -> DataType: key = zero_bound_keys[0] kwargs_to_pass = {key: number_kwargs[key]} else: - kwargs_to_pass = model_dump(obj) + kwargs_to_pass = obj.model_dump() return self.data_type_manager.get_data_type( self._get_type_with_mappings(type_, format__), @@ -1667,7 +1636,7 @@ def _load_ref_schema_object(self, ref: str) -> JsonSchemaObject: pointer = [p for p in fragment.split("/") if p] target_schema = get_model_by_path(raw_doc, pointer) - return model_validate(self.SCHEMA_OBJECT_TYPE, target_schema) + return self.SCHEMA_OBJECT_TYPE.model_validate(target_schema) def _build_anchor_indexes(self, obj: JsonSchemaObject, path: list[str]) -> None: """Build $recursiveAnchor and $dynamicAnchor indexes for a schema object.""" @@ -1760,12 +1729,12 @@ def _merge_ref_with_schema(self, obj: JsonSchemaObject) -> JsonSchemaObject: return obj ref_schema = self._load_ref_schema_object(obj.ref) - ref_dict = model_dump(ref_schema, exclude_unset=True, by_alias=True) - current_dict = model_dump(obj, exclude={"ref"}, exclude_unset=True, by_alias=True) + ref_dict = ref_schema.model_dump(exclude_unset=True, by_alias=True) + current_dict = obj.model_dump(exclude={"ref"}, exclude_unset=True, by_alias=True) merged = self._deep_merge(ref_dict, current_dict) merged.pop("$ref", None) - return model_validate(self.SCHEMA_OBJECT_TYPE, merged) + return self.SCHEMA_OBJECT_TYPE.model_validate(merged) def _is_ref_circular(self, resolved_ref: str) -> bool: """Check if a resolved $ref target contains a circular reference (cached).""" @@ -1828,7 +1797,7 @@ def _merge_primitive_schemas(self, items: list[JsonSchemaObject]) -> JsonSchemaO base_dict: dict[str, Any] = {} for item in items: # pragma: no branch if item.type: # pragma: no branch - base_dict = model_dump(item, exclude_unset=True, by_alias=True) + base_dict = item.model_dump(exclude_unset=True, by_alias=True) break for item in items: @@ -1842,7 +1811,7 @@ def _merge_primitive_schemas(self, items: list[JsonSchemaObject]) -> JsonSchemaO else: base_dict[field] = JsonSchemaParser._intersect_constraint(field, base_dict[field], value) - return model_validate(self.SCHEMA_OBJECT_TYPE, base_dict) + return self.SCHEMA_OBJECT_TYPE.model_validate(base_dict) def _merge_primitive_schemas_for_allof(self, items: list[JsonSchemaObject]) -> JsonSchemaObject | None: """Merge primitive schemas for allOf, respecting allof_merge_mode setting.""" @@ -1857,15 +1826,15 @@ def _merge_primitive_schemas_for_allof(self, items: list[JsonSchemaObject]) -> J if self.allof_merge_mode != AllOfMergeMode.NoMerge: merged = self._merge_primitive_schemas(items) - merged_dict = model_dump(merged, exclude_unset=True, by_alias=True) + merged_dict = merged.model_dump(exclude_unset=True, by_alias=True) if merged_format: merged_dict["format"] = merged_format - return model_validate(self.SCHEMA_OBJECT_TYPE, merged_dict) + return self.SCHEMA_OBJECT_TYPE.model_validate(merged_dict) base_dict: dict[str, Any] = {} for item in items: if item.type: - base_dict = model_dump(item, exclude_unset=True, by_alias=True) + base_dict = item.model_dump(exclude_unset=True, by_alias=True) break for item in items: @@ -1879,7 +1848,7 @@ def _merge_primitive_schemas_for_allof(self, items: list[JsonSchemaObject]) -> J if merged_format: base_dict["format"] = merged_format - return model_validate(self.SCHEMA_OBJECT_TYPE, base_dict) + return self.SCHEMA_OBJECT_TYPE.model_validate(base_dict) @staticmethod def _intersect_constraint(field: str, val1: Any, val2: Any) -> Any: # noqa: PLR0911 @@ -2140,17 +2109,17 @@ def _merge_properties_with_parent_constraints( merged_properties[prop_name] = child_prop continue - parent_dict = model_dump(parent_prop, exclude_unset=True, by_alias=True) - child_dict = model_dump(child_prop, exclude_unset=True, by_alias=True) + parent_dict = parent_prop.model_dump(exclude_unset=True, by_alias=True) + child_dict = child_prop.model_dump(exclude_unset=True, by_alias=True) merged_dict = self._merge_property_schemas(parent_dict, child_dict) - merged_properties[prop_name] = model_validate(self.SCHEMA_OBJECT_TYPE, merged_dict) + merged_properties[prop_name] = self.SCHEMA_OBJECT_TYPE.model_validate(merged_dict) - merged_obj_dict = model_dump(child_obj, exclude_unset=True, by_alias=True) + merged_obj_dict = child_obj.model_dump(exclude_unset=True, by_alias=True) merged_obj_dict["properties"] = { - k: model_dump(v, exclude_unset=True, by_alias=True) if isinstance(v, JsonSchemaObject) else v + k: v.model_dump(exclude_unset=True, by_alias=True) if isinstance(v, JsonSchemaObject) else v for k, v in merged_properties.items() } - return model_validate(self.SCHEMA_OBJECT_TYPE, merged_obj_dict) + return self.SCHEMA_OBJECT_TYPE.model_validate(merged_obj_dict) def _get_inherited_field_type( # noqa: PLR0912 self, prop_name: str, base_classes: list[Reference], visited: frozenset[str] | None = None @@ -2204,7 +2173,7 @@ def _schema_signature(self, prop_schema: JsonSchemaObject | bool) -> str | bool: """Normalize property schema for comparison across allOf items.""" if isinstance(prop_schema, bool): return prop_schema - return json.dumps(model_dump(prop_schema, exclude_unset=True, by_alias=True), sort_keys=True, default=repr) + return json.dumps(prop_schema.model_dump(exclude_unset=True, by_alias=True), sort_keys=True, default=repr) def _is_root_model_schema(self, obj: JsonSchemaObject) -> bool: # noqa: PLR0911 """Check if schema represents a root model (primitive type with constraints). @@ -2285,9 +2254,9 @@ def _handle_allof_root_model_with_constraints( # noqa: PLR0911, PLR0912 return None if obj.description: - merged_dict = model_dump(merged_schema, exclude_unset=True, by_alias=True) + merged_dict = merged_schema.model_dump(exclude_unset=True, by_alias=True) merged_dict["description"] = obj.description - merged_schema = model_validate(self.SCHEMA_OBJECT_TYPE, merged_dict) + merged_schema = self.SCHEMA_OBJECT_TYPE.model_validate(merged_dict) return self.parse_root_type(name, merged_schema, path) @@ -2318,17 +2287,15 @@ def _merge_all_of_object(self, obj: JsonSchemaObject) -> JsonSchemaObject | None if not any(len(signatures) > 1 for signatures in property_signatures.values()): return None - merged_schema: dict[str, Any] = model_dump(obj, exclude={"allOf"}, exclude_unset=True, by_alias=True) + merged_schema: dict[str, Any] = obj.model_dump(exclude={"allOf"}, exclude_unset=True, by_alias=True) for resolved_item in resolved_items: - merged_schema = self._deep_merge( - merged_schema, model_dump(resolved_item, exclude_unset=True, by_alias=True) - ) + merged_schema = self._deep_merge(merged_schema, resolved_item.model_dump(exclude_unset=True, by_alias=True)) if "required" in merged_schema and isinstance(merged_schema["required"], list): merged_schema["required"] = list(dict.fromkeys(merged_schema["required"])) merged_schema.pop("allOf", None) - return model_validate(self.SCHEMA_OBJECT_TYPE, merged_schema) + return self.SCHEMA_OBJECT_TYPE.model_validate(merged_schema) def parse_combined_schema( self, @@ -2338,7 +2305,7 @@ def parse_combined_schema( target_attribute_name: str, ) -> list[DataType]: """Parse combined schema (anyOf, oneOf, allOf) into a list of data types.""" - base_object = model_dump(obj, exclude={target_attribute_name, "title"}, exclude_unset=True, by_alias=True) + base_object = obj.model_dump(exclude={target_attribute_name, "title"}, exclude_unset=True, by_alias=True) combined_schemas: list[JsonSchemaObject] = [] refs = [] for index, target_attribute in enumerate(getattr(obj, target_attribute_name, [])): @@ -2350,10 +2317,9 @@ def parse_combined_schema( refs.append(index) else: combined_schemas.append( - model_validate( - self.SCHEMA_OBJECT_TYPE, + self.SCHEMA_OBJECT_TYPE.model_validate( self._deep_merge( - base_object, model_dump(merged_attr, exclude_unset=True, by_alias=True) + base_object, merged_attr.model_dump(exclude_unset=True, by_alias=True) ), ) ) @@ -2362,11 +2328,10 @@ def parse_combined_schema( refs.append(index) else: combined_schemas.append( - model_validate( - self.SCHEMA_OBJECT_TYPE, + self.SCHEMA_OBJECT_TYPE.model_validate( self._deep_merge( base_object, - model_dump(target_attribute, exclude_unset=True, by_alias=True), + target_attribute.model_dump(exclude_unset=True, by_alias=True), ), ) ) @@ -2459,7 +2424,7 @@ def _parse_object_common_part( # noqa: PLR0912, PLR0913, PLR0915 if current_type and current_type.type == ANY and field_name: inherited_type = self._get_inherited_field_type(field_name, base_classes) if inherited_type is not None: - new_type = model_copy(inherited_type, deep=True) + new_type = inherited_type.model_copy(deep=True) new_type.is_optional = new_type.is_optional or current_type.is_optional new_type.is_dict = new_type.is_dict or current_type.is_dict new_type.is_list = new_type.is_list or current_type.is_list @@ -2473,7 +2438,7 @@ def _parse_object_common_part( # noqa: PLR0912, PLR0913, PLR0915 if inherited_type is None or not inherited_type.is_list or not inherited_type.data_types: continue - new_type = model_copy(inherited_type, deep=True) + new_type = inherited_type.model_copy(deep=True) # Preserve modifiers coming from the overriding schema. if current_type is not None: # pragma: no branch @@ -2492,7 +2457,7 @@ def _parse_object_common_part( # noqa: PLR0912, PLR0913, PLR0915 and current_type.data_types[0].is_list ) if is_wrapped: - wrapper = model_copy(current_type, deep=True) + wrapper = current_type.model_copy(deep=True) wrapper.data_types[0] = new_type field.data_type = wrapper continue @@ -3372,7 +3337,7 @@ def parse_array_fields( # noqa: PLR0912, PLR0915 data_types.append(self.parse_object(name, obj, get_special_path("object", path))) if obj.enum and not self.ignore_enum_constraints: data_types.append(self.parse_enum(name, obj, get_special_path("enum", path))) - constraints = model_dump(obj, exclude_none=True) + constraints = obj.model_dump(exclude_none=True) if suppress_item_constraints: constraints.pop("minItems", None) constraints.pop("maxItems", None) @@ -3536,7 +3501,7 @@ def parse_root_type( # noqa: PLR0912, PLR0914, PLR0915 reference = self.model_resolver.add(path, name, loaded=True, class_name=True) self._set_schema_metadata(reference.path, obj) self.set_schema_extensions(reference.path, obj) - constraints = model_dump(obj, exclude_none=True) if self.field_constraints else {} + constraints = obj.model_dump(exclude_none=True) if self.field_constraints else {} if self.field_constraints and obj.format == "hostname": constraints["pattern"] = self.data_type_manager.HOSTNAME_REGEX data_model_root_type = self.data_model_root_type( @@ -3598,7 +3563,7 @@ def _parse_multiple_types_with_properties( self._set_schema_metadata(reference.path, obj) self.set_schema_extensions(reference.path, obj) - constraints = model_dump(obj, exclude_none=True) if self.field_constraints else {} + constraints = obj.model_dump(exclude_none=True) if self.field_constraints else {} if self.field_constraints and obj.format == "hostname": constraints["pattern"] = self.data_type_manager.HOSTNAME_REGEX data_model_root_type = self.data_model_root_type( @@ -4027,7 +3992,7 @@ def _validate_schema_object( ) -> JsonSchemaObject: """Validate raw data as JsonSchemaObject with path context in errors.""" try: - return model_validate(self.SCHEMA_OBJECT_TYPE, raw) + return self.SCHEMA_OBJECT_TYPE.model_validate(raw) except SchemaParseError: raise except Exception as e: diff --git a/src/datamodel_code_generator/parser/openapi.py b/src/datamodel_code_generator/parser/openapi.py index 6346f3f41..2e30a46b1 100644 --- a/src/datamodel_code_generator/parser/openapi.py +++ b/src/datamodel_code_generator/parser/openapi.py @@ -39,7 +39,7 @@ DataType, EmptyDataType, ) -from datamodel_code_generator.util import BaseModel, model_dump, model_validate +from datamodel_code_generator.util import BaseModel if TYPE_CHECKING: from urllib.parse import ParseResult @@ -334,7 +334,7 @@ def resolve_object(self, obj: ReferenceObject | BaseModelT, object_type: type[Ba """Resolve a reference object to its actual type or return the object as-is.""" if isinstance(obj, ReferenceObject): ref_obj = self.get_ref_model(obj.ref) - return model_validate(object_type, ref_obj) + return object_type.model_validate(ref_obj) return obj def _parse_schema_or_ref( @@ -475,7 +475,7 @@ def parse_responses( if not detail.ref: # pragma: no cover continue ref_model = self.get_ref_model(detail.ref) - content = {k: model_validate(MediaObject, v) for k, v in ref_model.get("content", {}).items()} + content = {k: MediaObject.model_validate(v) for k, v in ref_model.get("content", {}).items()} else: content = detail.content @@ -617,7 +617,7 @@ def parse_all_parameters( # noqa: PLR0912, PLR0914, PLR0915 required=effective_required, alias=single_alias, validation_aliases=validation_aliases, - constraints=model_dump(object_schema, exclude_none=True) + constraints=object_schema.model_dump(exclude_none=True) if object_schema and self.is_constraints_field(object_schema) else None, nullable=object_schema.nullable @@ -667,7 +667,7 @@ def parse_operation( path: list[str], ) -> None: """Parse an OpenAPI operation including parameters, request body, and responses.""" - operation = model_validate(Operation, raw_operation) + operation = Operation.model_validate(raw_operation) path_name, method = path[-2:] if self.use_operation_id_as_name: if not operation.operationId: @@ -688,7 +688,7 @@ def parse_operation( if operation.requestBody: if isinstance(operation.requestBody, ReferenceObject): ref_model = self.get_ref_model(operation.requestBody.ref) - request_body = model_validate(RequestBodyObject, ref_model) + request_body = RequestBodyObject.model_validate(ref_model) else: request_body = operation.requestBody self.parse_request_body( diff --git a/src/datamodel_code_generator/reference.py b/src/datamodel_code_generator/reference.py index 189a59efc..0b50160ae 100644 --- a/src/datamodel_code_generator/reference.py +++ b/src/datamodel_code_generator/reference.py @@ -29,22 +29,19 @@ ) from urllib.parse import ParseResult, urlparse -import pydantic -from packaging import version -from pydantic import BaseModel, Field +from pydantic import BaseModel, ConfigDict, Field, model_validator from typing_extensions import TypeIs from datamodel_code_generator import Error, NamingStrategy from datamodel_code_generator.enums import ClassNameAffixScope from datamodel_code_generator.format import PythonVersion -from datamodel_code_generator.util import ConfigDict, camel_to_snake, is_pydantic_v2, model_validator +from datamodel_code_generator.util import camel_to_snake if TYPE_CHECKING: from collections.abc import Callable, Generator, Iterator, Mapping, Sequence from collections.abc import Set as AbstractSet import inflect - from pydantic.typing import DictStrAny from datamodel_code_generator.model.base import DataModel from datamodel_code_generator.types import DataType @@ -95,49 +92,25 @@ def __init__(self, **values: Any) -> None: setattr(self, pass_field_name, values[pass_field_name]) if not TYPE_CHECKING: # pragma: no branch - if is_pydantic_v2(): - - def dict( # noqa: PLR0913 # pragma: no cover - self, - *, - include: AbstractSet[int | str] | Mapping[int | str, Any] | None = None, - exclude: AbstractSet[int | str] | Mapping[int | str, Any] | None = None, - by_alias: bool = False, - exclude_unset: bool = False, - exclude_defaults: bool = False, - exclude_none: bool = False, - ) -> DictStrAny: - return self.model_dump( - include=include, # ty: ignore - exclude=set(exclude or ()) | self._exclude_fields, - by_alias=by_alias, - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, - exclude_none=exclude_none, - ) - - else: - def dict( # noqa: PLR0913 - self, - *, - include: AbstractSet[int | str] | Mapping[int | str, Any] | None = None, - exclude: AbstractSet[int | str] | Mapping[int | str, Any] | None = None, - by_alias: bool = False, - skip_defaults: bool | None = None, - exclude_unset: bool = False, - exclude_defaults: bool = False, - exclude_none: bool = False, - ) -> DictStrAny: - return super().dict( - include=include, # ty: ignore - exclude=set(exclude or ()) | self._exclude_fields, - by_alias=by_alias, - skip_defaults=skip_defaults, # ty: ignore - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, - exclude_none=exclude_none, - ) + def dict( # noqa: PLR0913 # pragma: no cover + self, + *, + include: AbstractSet[int | str] | Mapping[int | str, Any] | None = None, + exclude: AbstractSet[int | str] | Mapping[int | str, Any] | None = None, + by_alias: bool = False, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + ) -> dict[str, Any]: + return self.model_dump( + include=include, # ty: ignore + exclude=set(exclude or ()) | self._exclude_fields, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + ) class Reference(_BaseModel): @@ -167,22 +140,11 @@ def validate_original_name(cls, values: Any) -> Any: # noqa: N805 values["original_name"] = values.get("name", original_name) return values - if is_pydantic_v2(): - # TODO[pydantic]: The following keys were removed: `copy_on_model_validation`. - # Check https://docs.pydantic.dev/dev-v2/migration/#changes-to-config for more information. - model_config = ConfigDict( # ty: ignore - arbitrary_types_allowed=True, - ignored_types=(cached_property,), - revalidate_instances="never", - ) - else: - - class Config: - """Pydantic v1 configuration for Reference model.""" - - arbitrary_types_allowed = True - keep_untouched = (cached_property,) - copy_on_model_validation = False if version.parse(pydantic.VERSION) < version.parse("1.9.2") else "none" + model_config = ConfigDict( # ty: ignore + arbitrary_types_allowed=True, + ignored_types=(cached_property,), + revalidate_instances="never", + ) @property def short_name(self) -> str: diff --git a/src/datamodel_code_generator/types.py b/src/datamodel_code_generator/types.py index 5e3d0fab8..30d579a48 100644 --- a/src/datamodel_code_generator/types.py +++ b/src/datamodel_code_generator/types.py @@ -26,9 +26,8 @@ runtime_checkable, ) -import pydantic -from packaging import version -from pydantic import Field, StrictBool, StrictInt, StrictStr, create_model +from pydantic import ConfigDict, Field, GetCoreSchemaHandler, StrictBool, StrictInt, StrictStr, create_model +from pydantic_core import core_schema from typing_extensions import TypeIs from datamodel_code_generator.format import ( @@ -55,7 +54,6 @@ Import, ) from datamodel_code_generator.reference import Reference, _BaseModel -from datamodel_code_generator.util import ConfigDict, is_pydantic_v2 T = TypeVar("T") SourceT = TypeVar("SourceT") @@ -102,20 +100,13 @@ }, ) - if TYPE_CHECKING: import builtins from collections.abc import Callable, Iterable, Iterator, Sequence - from pydantic_core import core_schema - from datamodel_code_generator.enums import StrictTypes from datamodel_code_generator.model.base import DataModelFieldBase -if is_pydantic_v2(): - from pydantic import GetCoreSchemaHandler - from pydantic_core import core_schema - class UnionIntFloat: """Pydantic-compatible type that accepts both int and float values.""" @@ -124,7 +115,7 @@ def __init__(self, value: float) -> None: """Initialize with an int or float value.""" self.value: int | float = value - def __int__(self) -> int: + def __int__(self) -> int: # pragma: no cover """Convert value to int.""" return int(self.value) @@ -136,11 +127,6 @@ def __str__(self) -> str: """Convert value to string.""" return str(self.value) - @classmethod - def __get_validators__(cls) -> Iterator[Callable[[Any], Any]]: # noqa: PLW3201 - """Return Pydantic v1 validators.""" - yield cls.validate - @classmethod def __get_pydantic_core_schema__( # noqa: PLW3201 cls, _source_type: Any, _handler: GetCoreSchemaHandler @@ -164,7 +150,7 @@ def __get_pydantic_core_schema__( # noqa: PLW3201 @classmethod def validate(cls, v: Any) -> UnionIntFloat: """Validate and convert value to UnionIntFloat.""" - if isinstance(v, UnionIntFloat): + if isinstance(v, UnionIntFloat): # pragma: no cover return v if not isinstance(v, (int, float)): # pragma: no cover try: @@ -401,31 +387,10 @@ def nullable(self) -> bool: class DataType(_BaseModel): """Represents a type in generated code with imports and references.""" - if is_pydantic_v2(): - # TODO[pydantic]: The following keys were removed: `copy_on_model_validation`. - # Check https://docs.pydantic.dev/dev-v2/migration/#changes-to-config for more information. - model_config = ConfigDict( # ty: ignore - extra="forbid", - revalidate_instances="never", - ) - else: - if not TYPE_CHECKING: # pragma: no branch - - @classmethod - def model_rebuild( - cls, - *, - _types_namespace: dict[str, type] | None = None, - ) -> None: - """Update forward references for Pydantic v1.""" - localns = _types_namespace or {} - cls.update_forward_refs(**localns) - - class Config: - """Pydantic v1 model configuration.""" - - extra = "forbid" - copy_on_model_validation = False if version.parse(pydantic.VERSION) < version.parse("1.9.2") else "none" + model_config = ConfigDict( # ty: ignore + extra="forbid", + revalidate_instances="never", + ) type: Optional[str] = None # noqa: UP045 reference: Optional[Reference] = None # noqa: UP045 @@ -470,7 +435,7 @@ def __deepcopy__(self, memo: dict[int, Any] | None = None) -> DataType: return memo[obj_id] cls = self.__class__ - model_fields = getattr(cls, "model_fields" if is_pydantic_v2() else "__fields__") + model_fields = cls.model_fields shallow_kwargs: dict[str, Any] = {} for field_name in model_fields: @@ -480,8 +445,7 @@ def __deepcopy__(self, memo: dict[int, Any] | None = None) -> DataType: else: shallow_kwargs[field_name] = value - constructor = getattr(cls, "model_construct" if is_pydantic_v2() else "construct") - new_obj: DataType = constructor(**shallow_kwargs) + new_obj: DataType = cls.model_construct(**shallow_kwargs) memo[obj_id] = new_obj for field_name in model_fields: diff --git a/src/datamodel_code_generator/util.py b/src/datamodel_code_generator/util.py index 7f8042c7f..a36852bce 100644 --- a/src/datamodel_code_generator/util.py +++ b/src/datamodel_code_generator/util.py @@ -166,7 +166,7 @@ def inner(method: Callable[[Model, T], T]) -> Callable[[Model, T], T]: ... @overload def inner(method: Callable[[Model], Model]) -> Callable[[Model], Model]: ... - def inner( + def inner( # pragma: no cover method: Callable[[type[Model], T], T] | Callable[[Model, T], T] | Callable[[Model], Model], ) -> Callable[[type[Model], T], T] | Callable[[Model, T], T] | Callable[[Model], Model]: if is_pydantic_v2(): @@ -179,7 +179,7 @@ def inner( return root_validator(method, pre=mode == "before") # ty: ignore # pragma: no cover - return inner + return inner # pragma: no cover def field_validator( @@ -189,7 +189,7 @@ def field_validator( ) -> Callable[[Any], Callable[[Any, Any], Any]]: """Decorate field validators for both Pydantic v1 and v2.""" - def inner(method: Callable[[Model, Any], Any]) -> Callable[[Model, Any], Any]: + def inner(method: Callable[[Model, Any], Any]) -> Callable[[Model, Any], Any]: # pragma: no cover if is_pydantic_v2(): from pydantic import field_validator as field_validator_v2 # noqa: PLC0415 @@ -198,11 +198,11 @@ def inner(method: Callable[[Model, Any], Any]) -> Callable[[Model, Any], Any]: return validator(field_name, *fields, pre=mode == "before")(method) # ty: ignore # pragma: no cover - return inner + return inner # pragma: no cover @lru_cache(maxsize=1) -def _get_config_dict() -> type: +def _get_config_dict() -> type: # pragma: no cover """Get ConfigDict type lazily. Only used with pydantic v2.""" from pydantic import ConfigDict # noqa: PLC0415 @@ -212,7 +212,7 @@ def _get_config_dict() -> type: class _ConfigDictProxy: """Proxy for lazy ConfigDict access.""" - def __call__(self, **kwargs: Any) -> Any: + def __call__(self, **kwargs: Any) -> Any: # pragma: no cover return _get_config_dict()(**kwargs) @@ -306,14 +306,14 @@ def model_validate(cls: type[Model], obj: Any) -> Model: return cls.parse_obj(obj) # type: ignore[reportDeprecated] # pragma: no cover -def get_fields_set(obj: _BaseModel) -> set[str]: # ty: ignore +def get_fields_set(obj: _BaseModel) -> set[str]: # ty: ignore # pragma: no cover """Version-compatible access to fields set (__fields_set__/model_fields_set).""" if is_pydantic_v2(): return obj.model_fields_set # ty: ignore return obj.__fields_set__ # type: ignore[reportDeprecated] # pragma: no cover -def model_copy(obj: Model, **kwargs: Any) -> Model: +def model_copy(obj: Model, **kwargs: Any) -> Model: # pragma: no cover """Version-compatible model copy (copy/model_copy).""" if is_pydantic_v2(): return obj.model_copy(**kwargs) # ty: ignore diff --git a/src/datamodel_code_generator/validators.py b/src/datamodel_code_generator/validators.py index 4f028baf7..c2e721705 100644 --- a/src/datamodel_code_generator/validators.py +++ b/src/datamodel_code_generator/validators.py @@ -6,14 +6,8 @@ from __future__ import annotations from enum import Enum -from typing import TYPE_CHECKING -from pydantic import BaseModel - -from datamodel_code_generator.util import is_pydantic_v2 - -if TYPE_CHECKING: - from pydantic import RootModel +from pydantic import BaseModel, RootModel class ValidatorMode(str, Enum): @@ -40,12 +34,5 @@ class ModelValidators(BaseModel): validators: list[ValidatorDefinition] -if is_pydantic_v2(): - from pydantic import RootModel - - class ValidatorsConfig(RootModel[dict[str, ModelValidators]]): - """Root model for validators configuration.""" - -else: # pragma: no cover - # Pydantic v1 doesn't support RootModel, but validators feature is v2-only anyway - ValidatorsConfig = None # type: ignore[assignment,misc] +class ValidatorsConfig(RootModel[dict[str, ModelValidators]]): + """Root model for validators configuration.""" diff --git a/tests/main/jsonschema/test_main_jsonschema.py b/tests/main/jsonschema/test_main_jsonschema.py index bef6f0e12..934bd778a 100644 --- a/tests/main/jsonschema/test_main_jsonschema.py +++ b/tests/main/jsonschema/test_main_jsonschema.py @@ -44,7 +44,6 @@ from tests.main.jsonschema.conftest import EXPECTED_JSON_SCHEMA_PATH, assert_file_content PYDANTIC_V2_SKIP = pytest.mark.skipif(not is_pydantic_v2(), reason="Pydantic v2 required") -PYDANTIC_V1_ONLY = pytest.mark.skipif(is_pydantic_v2(), reason="Pydantic v1 only") if TYPE_CHECKING: from pytest_mock import MockerFixture @@ -8265,26 +8264,6 @@ def test_validators_invalid_structure(output_file: Path, tmp_path: Path, capsys: ) -@PYDANTIC_V1_ONLY -def test_validators_requires_pydantic_v2(output_file: Path, tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None: - """Test that validators option requires Pydantic v2.""" - config_file = tmp_path / "validators.json" - config_file.write_text('{"User": {"validators": []}}') - - run_main_and_assert( - input_path=JSON_SCHEMA_DATA_PATH / "field_validators.json", - output_path=output_file, - input_file_type="jsonschema", - expected_exit=Exit.ERROR, - extra_args=[ - "--validators", - str(config_file), - ], - capsys=capsys, - expected_stderr_contains="--validators option requires Pydantic v2", - ) - - def test_jsonschema_classvar_extra_pydantic_v2(output_file: Path) -> None: """Test default value handling.""" run_main_and_assert( diff --git a/tests/main/test_main_general.py b/tests/main/test_main_general.py index 2021939e6..914e25cce 100644 --- a/tests/main/test_main_general.py +++ b/tests/main/test_main_general.py @@ -29,7 +29,6 @@ from datamodel_code_generator.format import CodeFormatter, PythonVersion from datamodel_code_generator.model.pydantic_v2 import UnionMode from datamodel_code_generator.parser.openapi import OpenAPIParser -from datamodel_code_generator.util import is_pydantic_v2 from tests.conftest import assert_output, create_assert_file_content, freeze_time from tests.main.conftest import ( DATA_PATH, @@ -2028,18 +2027,6 @@ def test_generate_with_dict_raw_data_types_raises_error(input_file_type: InputFi generate(auto_error_dict, input_file_type=input_file_type) -def test_pydantic_v1_deprecation_warning(output_file: Path, mocker: MockerFixture) -> None: - """Test that deprecation warning is emitted when running with Pydantic v1.""" - mocker.patch("datamodel_code_generator.__main__.is_pydantic_v2", return_value=False) - - with pytest.warns(DeprecationWarning, match=r"Pydantic v1 runtime support is deprecated"): - run_main_and_assert( - input_path=JSON_SCHEMA_DATA_PATH / "simple_string.json", - output_path=output_file, - input_file_type="jsonschema", - ) - - @pytest.mark.skipif(pydantic.VERSION < "2.0.0", reason="GenerateConfig requires Pydantic v2") def test_generate_with_config_object(output_file: Path) -> None: """Test generate() with GenerateConfig object.""" @@ -2311,7 +2298,6 @@ def test_use_annotated_no_warning_pydantic_v1(output_file: Path) -> None: assert not any("--use-annotated will be enabled" in str(warning.message) for warning in w) -@pytest.mark.skipif(not is_pydantic_v2(), reason="GenerateConfig requires Pydantic v2") def test_import_generate_config_from_top_level() -> None: """Test that GenerateConfig can be imported from top-level module.""" from datamodel_code_generator import GenerateConfig as TopLevelGenerateConfig @@ -2320,7 +2306,6 @@ def test_import_generate_config_from_top_level() -> None: assert TopLevelGenerateConfig is GenerateConfig -@pytest.mark.skipif(not is_pydantic_v2(), reason="GenerateConfig requires Pydantic v2") def test_generate_with_imported_config_from_top_level() -> None: """Test generate() with GenerateConfig imported from top-level.""" config = datamodel_code_generator.GenerateConfig(class_name="TestModel") @@ -2329,14 +2314,6 @@ def test_generate_with_imported_config_from_top_level() -> None: assert "class TestModel" in result -@pytest.mark.skipif(not is_pydantic_v2(), reason="GenerateConfig requires Pydantic v2") def test_all_exports_includes_generate_config() -> None: - """Test that __all__ includes GenerateConfig in Pydantic v2.""" + """Test that __all__ includes GenerateConfig.""" assert "GenerateConfig" in datamodel_code_generator.__all__ - - -@pytest.mark.skipif(is_pydantic_v2(), reason="Test for Pydantic v1 only") -def test_import_generate_config_fails_on_v1() -> None: - """GenerateConfig should not be importable from top-level in Pydantic v1.""" - with pytest.raises(ImportError, match="only available in Pydantic v2"): - _ = datamodel_code_generator.GenerateConfig diff --git a/tests/main/test_public_api_signature_baseline.py b/tests/main/test_public_api_signature_baseline.py index cbb7420ea..f78948a59 100644 --- a/tests/main/test_public_api_signature_baseline.py +++ b/tests/main/test_public_api_signature_baseline.py @@ -506,19 +506,18 @@ def test_generate_signature_matches_baseline() -> None: f" TypedDict: {_normalize_type(dict_type)}" ) - # 3. Verify default values match between baseline and GenerateConfig (Pydantic v2 only) - if is_pydantic_v2(): - from datamodel_code_generator.config import GenerateConfig - from datamodel_code_generator.model.pydantic_v2 import UnionMode - from datamodel_code_generator.types import StrictTypes + # 3. Verify default values match between baseline and GenerateConfig + from datamodel_code_generator.config import GenerateConfig + from datamodel_code_generator.model.pydantic_v2 import UnionMode + from datamodel_code_generator.types import StrictTypes - GenerateConfig.model_rebuild(_types_namespace={"StrictTypes": StrictTypes, "UnionMode": UnionMode}) + GenerateConfig.model_rebuild(_types_namespace={"StrictTypes": StrictTypes, "UnionMode": UnionMode}) - for name, param in baseline_params.items(): - config_default = GenerateConfig.model_fields[name].default - assert config_default == param.default, ( - f"Default mismatch for '{name}':\n Baseline: {param.default!r}\n GenerateConfig: {config_default!r}" - ) + for name, param in baseline_params.items(): + config_default = GenerateConfig.model_fields[name].default + assert config_default == param.default, ( + f"Default mismatch for '{name}':\n Baseline: {param.default!r}\n GenerateConfig: {config_default!r}" + ) def test_parser_signature_matches_baseline() -> None: @@ -551,29 +550,28 @@ def test_parser_signature_matches_baseline() -> None: f" TypedDict: {_normalize_type(dict_type)}" ) - if is_pydantic_v2(): - from datamodel_code_generator.config import ParserConfig - from datamodel_code_generator.model.base import DataModel, DataModelFieldBase - from datamodel_code_generator.model.pydantic_v2 import UnionMode - from datamodel_code_generator.types import DataTypeManager, StrictTypes - - ParserConfig.model_rebuild( - _types_namespace={ - "StrictTypes": StrictTypes, - "UnionMode": UnionMode, - "DataModel": DataModel, - "DataModelFieldBase": DataModelFieldBase, - "DataTypeManager": DataTypeManager, - } - ) + from datamodel_code_generator.config import ParserConfig + from datamodel_code_generator.model.base import DataModel, DataModelFieldBase + from datamodel_code_generator.model.pydantic_v2 import UnionMode + from datamodel_code_generator.types import DataTypeManager, StrictTypes + + ParserConfig.model_rebuild( + _types_namespace={ + "StrictTypes": StrictTypes, + "UnionMode": UnionMode, + "DataModel": DataModel, + "DataModelFieldBase": DataModelFieldBase, + "DataTypeManager": DataTypeManager, + } + ) - for name, param in baseline_params.items(): - config_default = ParserConfig.model_fields[name].default - if callable(param.default) and config_default is None: - continue - assert config_default == param.default, ( - f"Default mismatch for '{name}':\n Baseline: {param.default!r}\n ParserConfig: {config_default!r}" - ) + for name, param in baseline_params.items(): + config_default = ParserConfig.model_fields[name].default + if callable(param.default) and config_default is None: + continue + assert config_default == param.default, ( + f"Default mismatch for '{name}':\n Baseline: {param.default!r}\n ParserConfig: {config_default!r}" + ) @PYDANTIC_V2_SKIP diff --git a/tests/parser/test_jsonschema.py b/tests/parser/test_jsonschema.py index 5bb7efea7..1d9854828 100644 --- a/tests/parser/test_jsonschema.py +++ b/tests/parser/test_jsonschema.py @@ -25,7 +25,6 @@ ) from datamodel_code_generator.reference import SPECIAL_PATH_MARKER, Reference from datamodel_code_generator.types import DataType -from datamodel_code_generator.util import model_dump, model_validate from tests.conftest import assert_output if TYPE_CHECKING: @@ -54,7 +53,7 @@ def test_get_model_by_path(schema: dict, path: str, model: dict) -> None: def test_json_schema_object_ref_url_json(mocker: MockerFixture) -> None: """Test JSON schema object reference with JSON URL.""" parser = JsonSchemaParser("") - obj = model_validate(JsonSchemaObject, {"$ref": "https://example.com/person.schema.json#/definitions/User"}) + obj = JsonSchemaObject.model_validate({"$ref": "https://example.com/person.schema.json#/definitions/User"}) mock_get = mocker.patch("httpx.get") mock_get.return_value.text = json.dumps( { @@ -95,7 +94,7 @@ def test_json_schema_object_ref_url_json(mocker: MockerFixture) -> None: def test_json_schema_object_ref_url_yaml(mocker: MockerFixture) -> None: """Test JSON schema object reference with YAML URL.""" parser = JsonSchemaParser("") - obj = model_validate(JsonSchemaObject, {"$ref": "https://example.org/schema.yaml#/definitions/User"}) + obj = JsonSchemaObject.model_validate({"$ref": "https://example.org/schema.yaml#/definitions/User"}) mock_get = mocker.patch("httpx.get") mock_get.return_value.text = yaml.safe_dump(json.load((DATA_PATH / "user.json").open())) @@ -125,8 +124,7 @@ def test_json_schema_object_cached_ref_url_yaml(mocker: MockerFixture) -> None: """Test JSON schema object cached reference with YAML URL.""" parser = JsonSchemaParser("") - obj = model_validate( - JsonSchemaObject, + obj = JsonSchemaObject.model_validate( { "type": "object", "properties": { @@ -264,7 +262,7 @@ def test_parse_object(source_obj: dict[str, Any], generated_classes: str) -> Non data_model_field_type=DataModelFieldBase, source="", ) - parser.parse_object("Person", model_validate(JsonSchemaObject, source_obj), []) + parser.parse_object("Person", JsonSchemaObject.model_validate(source_obj), []) assert dump_templates(list(parser.results)) == generated_classes @@ -287,7 +285,7 @@ def test_parse_object(source_obj: dict[str, Any], generated_classes: str) -> Non def test_parse_any_root_object(source_obj: dict[str, Any], generated_classes: str) -> None: """Test parsing any root object.""" parser = JsonSchemaParser("") - parser.parse_root_type("AnyObject", model_validate(JsonSchemaObject, source_obj), []) + parser.parse_root_type("AnyObject", JsonSchemaObject.model_validate(source_obj), []) assert dump_templates(list(parser.results)) == generated_classes @@ -435,8 +433,9 @@ def test_get_data_type( import_ = None parser = JsonSchemaParser("", use_pendulum=use_pendulum) - assert model_dump(parser.get_data_type(JsonSchemaObject(type=schema_type, format=schema_format))) == model_dump( - DataType(type=result_type, import_=import_) + assert ( + parser.get_data_type(JsonSchemaObject(type=schema_type, format=schema_format)).model_dump() + == DataType(type=result_type, import_=import_).model_dump() ) @@ -554,7 +553,7 @@ class AltJsonSchemaParser(JsonSchemaParser): data_model_field_type=DataModelFieldBase, source="", ) - parser.parse_object("Person", model_validate(AltJsonSchemaObject, source_obj), []) + parser.parse_object("Person", AltJsonSchemaObject.model_validate(source_obj), []) assert dump_templates(list(parser.results)) == generated_classes @@ -878,7 +877,7 @@ def test_get_ref_body_from_url_file_local_path(mocker: MockerFixture) -> None: def test_merge_ref_with_schema_no_ref() -> None: """Test _merge_ref_with_schema returns object unchanged when no $ref is present.""" parser = JsonSchemaParser("") - obj = model_validate(JsonSchemaObject, {"type": "string", "minLength": 5}) + obj = JsonSchemaObject.model_validate({"type": "string", "minLength": 5}) result = parser._merge_ref_with_schema(obj) assert result is obj @@ -886,8 +885,7 @@ def test_merge_ref_with_schema_no_ref() -> None: def test_has_ref_with_schema_keywords_extras_with_schema_affecting_keys() -> None: """Test has_ref_with_schema_keywords when extras contains schema-affecting keys.""" # const is stored in extras and is schema-affecting - obj = model_validate( - JsonSchemaObject, + obj = JsonSchemaObject.model_validate( { "$ref": "#/$defs/Base", "const": "active", @@ -902,8 +900,7 @@ def test_has_ref_with_schema_keywords_extras_with_schema_affecting_keys() -> Non def test_has_ref_with_schema_keywords_extras_with_metadata_only_keys() -> None: """Test has_ref_with_schema_keywords when extras contains only metadata keys.""" # $comment is metadata-only, should not trigger merge - obj = model_validate( - JsonSchemaObject, + obj = JsonSchemaObject.model_validate( { "$ref": "#/$defs/Base", "$comment": "this is a comment", @@ -923,8 +920,7 @@ def test_has_ref_with_schema_keywords_extras_with_extension_keys() -> None: self-referencing schemas. """ # x-* extensions are vendor extensions, should not trigger merge - obj = model_validate( - JsonSchemaObject, + obj = JsonSchemaObject.model_validate( { "$ref": "#/$defs/Base", "deprecated": False, # metadata-only field @@ -943,8 +939,7 @@ def test_has_ref_with_schema_keywords_extras_with_extension_keys() -> None: def test_has_ref_with_schema_keywords_no_extras() -> None: """Test has_ref_with_schema_keywords when extras is empty.""" # Only $ref and a schema-affecting field, no extras - obj = model_validate( - JsonSchemaObject, + obj = JsonSchemaObject.model_validate( { "$ref": "#/$defs/Base", "minLength": 10, @@ -989,7 +984,7 @@ def test_parse_combined_schema_anyof_with_ref_and_schema_keywords() -> None: def test_parse_enum_empty_enum_not_nullable() -> None: """Test parse_enum returns null type when enum_fields is empty and not nullable.""" parser = JsonSchemaParser("") - obj = model_validate(JsonSchemaObject, {"type": "integer", "enum": []}) + obj = JsonSchemaObject.model_validate({"type": "integer", "enum": []}) result = parser.parse_enum("EmptyEnum", obj, ["EmptyEnum"]) assert result.type == "None" @@ -1012,14 +1007,14 @@ def test_parse_enum_empty_enum_not_nullable() -> None: def test_is_root_model_schema(schema: dict[str, Any], expected: bool) -> None: """Test _is_root_model_schema returns correct value for various schema types.""" parser = JsonSchemaParser("") - obj = model_validate(JsonSchemaObject, schema) + obj = JsonSchemaObject.model_validate(schema) assert parser._is_root_model_schema(obj) is expected def test_merge_primitive_schemas_for_allof_single_item() -> None: """Test _merge_primitive_schemas_for_allof returns unchanged item when single.""" parser = JsonSchemaParser("") - item = model_validate(JsonSchemaObject, {"type": "string", "minLength": 1}) + item = JsonSchemaObject.model_validate({"type": "string", "minLength": 1}) result = parser._merge_primitive_schemas_for_allof([item]) assert result == item @@ -1029,8 +1024,8 @@ def test_merge_primitive_schemas_for_allof_nomerge_mode() -> None: parser = JsonSchemaParser("") parser.allof_merge_mode = AllOfMergeMode.NoMerge items = [ - model_validate(JsonSchemaObject, {"type": "string", "pattern": "^a.*"}), - model_validate(JsonSchemaObject, {"minLength": 5}), + JsonSchemaObject.model_validate({"type": "string", "pattern": "^a.*"}), + JsonSchemaObject.model_validate({"minLength": 5}), ] result = parser._merge_primitive_schemas_for_allof(items) assert result.pattern == "^a.*" @@ -1042,8 +1037,8 @@ def test_merge_primitive_schemas_for_allof_nomerge_mode_with_format() -> None: parser = JsonSchemaParser("") parser.allof_merge_mode = AllOfMergeMode.NoMerge items = [ - model_validate(JsonSchemaObject, {"type": "string"}), - model_validate(JsonSchemaObject, {"format": "email"}), + JsonSchemaObject.model_validate({"type": "string"}), + JsonSchemaObject.model_validate({"format": "email"}), ] result = parser._merge_primitive_schemas_for_allof(items) assert result.format == "email" @@ -1054,8 +1049,8 @@ def test_merge_primitive_schemas_for_allof_constraints_mode_with_format() -> Non parser = JsonSchemaParser("") parser.allof_merge_mode = AllOfMergeMode.Constraints items = [ - model_validate(JsonSchemaObject, {"type": "string", "pattern": "^a.*"}), - model_validate(JsonSchemaObject, {"format": "email"}), + JsonSchemaObject.model_validate({"type": "string", "pattern": "^a.*"}), + JsonSchemaObject.model_validate({"format": "email"}), ] result = parser._merge_primitive_schemas_for_allof(items) assert result.format == "email" @@ -1064,8 +1059,7 @@ def test_merge_primitive_schemas_for_allof_constraints_mode_with_format() -> Non def test_handle_allof_root_model_special_path_marker() -> None: """Test _handle_allof_root_model_with_constraints returns None for special path.""" parser = JsonSchemaParser("") - obj = model_validate( - JsonSchemaObject, + obj = JsonSchemaObject.model_validate( { "allOf": [ {"$ref": "#/definitions/Base"}, @@ -1081,8 +1075,7 @@ def test_handle_allof_root_model_special_path_marker() -> None: def test_handle_allof_root_model_multiple_refs() -> None: """Test _handle_allof_root_model_with_constraints returns None for multiple refs.""" parser = JsonSchemaParser("") - obj = model_validate( - JsonSchemaObject, + obj = JsonSchemaObject.model_validate( { "allOf": [ {"$ref": "#/definitions/Base1"}, @@ -1097,8 +1090,7 @@ def test_handle_allof_root_model_multiple_refs() -> None: def test_handle_allof_root_model_no_refs() -> None: """Test _handle_allof_root_model_with_constraints returns None when no refs.""" parser = JsonSchemaParser("") - obj = model_validate( - JsonSchemaObject, + obj = JsonSchemaObject.model_validate( { "allOf": [ {"type": "string"}, @@ -1113,9 +1105,8 @@ def test_handle_allof_root_model_no_refs() -> None: def test_handle_allof_root_model_no_constraint_items() -> None: """Test _handle_allof_root_model_with_constraints returns None when no constraints.""" parser = JsonSchemaParser("") - parser._load_ref_schema_object = lambda _ref: model_validate(JsonSchemaObject, {"type": "string"}) - obj = model_validate( - JsonSchemaObject, + parser._load_ref_schema_object = lambda _ref: JsonSchemaObject.model_validate({"type": "string"}) + obj = JsonSchemaObject.model_validate( { "allOf": [ {"$ref": "#/definitions/Base"}, @@ -1129,9 +1120,8 @@ def test_handle_allof_root_model_no_constraint_items() -> None: def test_handle_allof_root_model_constraint_with_properties() -> None: """Test _handle_allof_root_model_with_constraints returns None when constraint has properties.""" parser = JsonSchemaParser("") - parser._load_ref_schema_object = lambda _ref: model_validate(JsonSchemaObject, {"type": "string"}) - obj = model_validate( - JsonSchemaObject, + parser._load_ref_schema_object = lambda _ref: JsonSchemaObject.model_validate({"type": "string"}) + obj = JsonSchemaObject.model_validate( { "allOf": [ {"$ref": "#/definitions/Base"}, @@ -1146,9 +1136,8 @@ def test_handle_allof_root_model_constraint_with_properties() -> None: def test_handle_allof_root_model_constraint_with_items() -> None: """Test _handle_allof_root_model_with_constraints returns None when constraint has items.""" parser = JsonSchemaParser("") - parser._load_ref_schema_object = lambda _ref: model_validate(JsonSchemaObject, {"type": "string"}) - obj = model_validate( - JsonSchemaObject, + parser._load_ref_schema_object = lambda _ref: JsonSchemaObject.model_validate({"type": "string"}) + obj = JsonSchemaObject.model_validate( { "allOf": [ {"$ref": "#/definitions/Base"}, @@ -1163,9 +1152,8 @@ def test_handle_allof_root_model_constraint_with_items() -> None: def test_handle_allof_root_model_incompatible_types() -> None: """Test _handle_allof_root_model_with_constraints returns None for incompatible types.""" parser = JsonSchemaParser("") - parser._load_ref_schema_object = lambda _ref: model_validate(JsonSchemaObject, {"type": "string"}) - obj = model_validate( - JsonSchemaObject, + parser._load_ref_schema_object = lambda _ref: JsonSchemaObject.model_validate({"type": "string"}) + obj = JsonSchemaObject.model_validate( { "allOf": [ {"$ref": "#/definitions/Base"}, @@ -1180,15 +1168,13 @@ def test_handle_allof_root_model_incompatible_types() -> None: def test_handle_allof_root_model_ref_to_non_root() -> None: """Test _handle_allof_root_model_with_constraints returns None when ref is not root model.""" parser = JsonSchemaParser("") - parser._load_ref_schema_object = lambda _ref: model_validate( - JsonSchemaObject, + parser._load_ref_schema_object = lambda _ref: JsonSchemaObject.model_validate( { "type": "object", "properties": {"id": {"type": "integer"}}, }, ) - obj = model_validate( - JsonSchemaObject, + obj = JsonSchemaObject.model_validate( { "allOf": [ {"$ref": "#/definitions/Base"}, @@ -1245,7 +1231,7 @@ def test_timestamp_with_time_zone_format() -> None: def test_get_python_type_flags(x_python_type: str, expected: dict[str, bool]) -> None: """Test _get_python_type_flags extracts collection flags correctly.""" parser = JsonSchemaParser("") - obj = model_validate(JsonSchemaObject, {"x-python-type": x_python_type}) + obj = JsonSchemaObject.model_validate({"x-python-type": x_python_type}) result = parser._get_python_type_flags(obj) assert result == expected diff --git a/tests/parser/test_openapi.py b/tests/parser/test_openapi.py index 6eaa8d6ce..4f73f444f 100644 --- a/tests/parser/test_openapi.py +++ b/tests/parser/test_openapi.py @@ -24,7 +24,6 @@ RequestBodyObject, ResponseObject, ) -from datamodel_code_generator.util import model_dump, model_validate from tests.conftest import assert_output, assert_parser_modules, assert_parser_results DATA_PATH: Path = Path(__file__).parents[1] / "data" / "openapi" @@ -139,7 +138,7 @@ class Pets(BaseModel): def test_parse_object(source_obj: dict[str, Any], generated_classes: str) -> None: """Test parsing OpenAPI object schemas.""" parser = OpenAPIParser("") - parser.parse_object("Pets", model_validate(JsonSchemaObject, source_obj), []) + parser.parse_object("Pets", JsonSchemaObject.model_validate(source_obj), []) assert dump_templates(list(parser.results)) == generated_classes @@ -183,7 +182,7 @@ class Pets(BaseModel): def test_parse_array(source_obj: dict[str, Any], generated_classes: str) -> None: """Test parsing OpenAPI array schemas.""" parser = OpenAPIParser("") - parser.parse_array("Pets", model_validate(JsonSchemaObject, source_obj), []) + parser.parse_array("Pets", JsonSchemaObject.model_validate(source_obj), []) assert dump_templates(list(parser.results)) == generated_classes @@ -237,7 +236,7 @@ def test_openapi_parser_parse(with_import: bool, format_: bool, base_class: str def test_parse_root_type(source_obj: dict[str, Any], generated_classes: str) -> None: """Test parsing OpenAPI root type schemas.""" parser = OpenAPIParser("") - parser.parse_root_type("Name", model_validate(JsonSchemaObject, source_obj), []) + parser.parse_root_type("Name", JsonSchemaObject.model_validate(source_obj), []) assert dump_templates(list(parser.results)) == generated_classes @@ -585,7 +584,7 @@ def test_openapi_model_resolver(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) parser.parse() references = { - k: model_dump(v, exclude={"source", "module_name", "actual_module_name", "children"}) + k: v.model_dump(exclude={"source", "module_name", "actual_module_name", "children"}) for k, v in parser.model_resolver.references.items() } assert references == { @@ -745,8 +744,8 @@ def test_parse_all_parameters_duplicate_names_exception() -> None: """Test parsing parameters with duplicate names raises exception.""" parser = OpenAPIParser("", include_path_parameters=True) parameters = [ - model_validate(ParameterObject, {"name": "duplicate_param", "in": "path", "schema": {"type": "string"}}), - model_validate(ParameterObject, {"name": "duplicate_param", "in": "query", "schema": {"type": "integer"}}), + ParameterObject.model_validate({"name": "duplicate_param", "in": "path", "schema": {"type": "string"}}), + ParameterObject.model_validate({"name": "duplicate_param", "in": "query", "schema": {"type": "integer"}}), ] with pytest.raises(Exception) as exc_info: # noqa: PT011 @@ -831,7 +830,7 @@ def test_parse_request_body_return(request_body_data: dict[str, Any], expected_t "TestRequest", RequestBodyObject( content={ - media_type: model_validate(MediaObject, media_data) + media_type: MediaObject.model_validate(media_data) for media_type, media_data in request_body_data.items() } ), @@ -870,7 +869,7 @@ def test_parse_all_parameters_return(parameters_data: list[dict[str, Any]], expe ) result = parser.parse_all_parameters( "TestParametersQuery", - [model_validate(ParameterObject, param_data) for param_data in parameters_data], + [ParameterObject.model_validate(param_data) for param_data in parameters_data], ["test", "path"], ) if expected_type_hint is None: @@ -935,7 +934,7 @@ def test_parse_responses_return( result = parser.parse_responses( "TestResponse", { - status_code: model_validate(ResponseObject, response_data) + status_code: ResponseObject.model_validate(response_data) for status_code, response_data in responses_data.items() }, ["test", "path"], @@ -970,7 +969,7 @@ def test_parse_all_parameters_strict_nullable() -> None: ] result = parser.parse_all_parameters( "TestParametersQuery", - [model_validate(ParameterObject, param_data) for param_data in parameters_data], + [ParameterObject.model_validate(param_data) for param_data in parameters_data], ["test", "path"], ) assert result is not None diff --git a/tests/test_input_model.py b/tests/test_input_model.py index d1c2692f4..4b22ac942 100644 --- a/tests/test_input_model.py +++ b/tests/test_input_model.py @@ -263,57 +263,6 @@ def test_input_model_unsupported_type(capsys: pytest.CaptureFixture[str]) -> Non ) -@SKIP_PYDANTIC_V1 -def test_input_model_pydantic_v1_runtime_error( - capsys: pytest.CaptureFixture[str], - monkeypatch: pytest.MonkeyPatch, -) -> None: - """Test error when Pydantic v1 runtime is detected.""" - import builtins - - from tests.data.python.input_model import pydantic_models - - original_hasattr = builtins.hasattr - - def mock_hasattr(obj: object, name: str) -> bool: - if name == "model_json_schema" and obj is pydantic_models.User: - return False - return original_hasattr(obj, name) - - monkeypatch.setattr(builtins, "hasattr", mock_hasattr) - - run_input_model_error_and_assert( - input_model="tests.data.python.input_model.pydantic_models:User", - capsys=capsys, - expected_stderr_contains="requires Pydantic v2 runtime", - ) - - -@SKIP_PYDANTIC_V1 -def test_input_model_dataclass_pydantic_import_error( - capsys: pytest.CaptureFixture[str], - monkeypatch: pytest.MonkeyPatch, -) -> None: - """Test error when TypeAdapter import fails for dataclass.""" - import builtins - - original_import = builtins.__import__ - - def mock_import(name: str, *args: object, **kwargs: object) -> object: - if name == "pydantic" and "TypeAdapter" in str(args): - msg = "mocked import error" - raise ImportError(msg) - return original_import(name, *args, **kwargs) - - monkeypatch.setattr(builtins, "__import__", mock_import) - - run_input_model_error_and_assert( - input_model="tests.data.python.input_model.dataclass_models:User", - capsys=capsys, - expected_stderr_contains="requires Pydantic v2 runtime", - ) - - @SKIP_PYDANTIC_V1 def test_input_model_mutual_exclusion_with_input( tmp_path: Path, @@ -1112,32 +1061,6 @@ def test_input_model_multiple_non_basemodel_error( ) -def test_input_model_multiple_pydantic_v1_error( - capsys: pytest.CaptureFixture[str], - monkeypatch: pytest.MonkeyPatch, -) -> None: - """Test error when multiple --input-model used with Pydantic v1 model.""" - import builtins - - original_hasattr = builtins.hasattr - - def mock_hasattr(obj: object, name: str) -> bool: - if name == "model_json_schema": - return False - return original_hasattr(obj, name) - - monkeypatch.setattr(builtins, "hasattr", mock_hasattr) - - run_multiple_input_models_error_and_assert( - input_models=[ - "tests.data.python.input_model.inheritance_models:ChildA", - "tests.data.python.input_model.inheritance_models:ChildB", - ], - capsys=capsys, - expected_stderr_contains="requires Pydantic v2 runtime", - ) - - @SKIP_PYDANTIC_V1 def test_input_model_multiple_invalid_format_error( capsys: pytest.CaptureFixture[str], diff --git a/tox.ini b/tox.ini index 31b4bd73c..45888d0e5 100644 --- a/tox.ini +++ b/tox.ini @@ -7,7 +7,6 @@ env_list = py314-parallel py313-parallel py312-parallel - py312-pydantic1-parallel py312-black{24, 23, 22}-parallel py312-isort{8, 7, 6, 5}-parallel py311-parallel @@ -54,7 +53,6 @@ dependency_groups = isort7: isort7 isort6: isort6 isort5: isort5 - pydantic1: pydantic1 [testenv:fix] description = format the code base to adhere to our styles, and complain about what we cannot do automatically @@ -96,7 +94,6 @@ depends = py314-parallel py313-parallel py312-parallel - py312-pydantic1-parallel py312-black{24, 23, 22}-parallel py312-isort{8, 7, 6, 5}-parallel py311-parallel