Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: koxudaxi/datamodel-code-generator
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 0.27.3
Choose a base ref
...
head repository: koxudaxi/datamodel-code-generator
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 0.28.0
Choose a head ref
  • 1 commit
  • 73 files changed
  • 1 contributor

Commits on Feb 14, 2025

  1. Drop <3.8 support as run and generation target (#2324)

    * Drop <3.8 support as run and generation target
    
    This technically can be considered a bit breaking, as if someone passes
    in `--python-target 3.6` that will now fail as we removed 3.6, but IMHO is the right approach.
    Signed-off-by: Bernát Gábor <bgabor8@bloomberg.net>
    
    * Fix linter errors
    
    Signed-off-by: Bernát Gábor <bgabor8@bloomberg.net>
    
    * Update readme
    
    Signed-off-by: Bernát Gábor <bgabor8@bloomberg.net>
    
    * We always can do future import that removes strings definitions
    
    Signed-off-by: Bernát Gábor <bgabor8@bloomberg.net>
    
    ---------
    
    Signed-off-by: Bernát Gábor <bgabor8@bloomberg.net>
    gaborbernat authored Feb 14, 2025
    Copy the full SHA
    7e3c6e9 View commit details
Showing with 646 additions and 2,552 deletions.
  1. +1 −4 .github/workflows/test.yaml
  2. +1 −1 .pre-commit-config.yaml
  3. +2 −2 README.md
  4. +2 −2 docs/index.md
  5. +2 −18 pyproject.toml
  6. +10 −7 src/datamodel_code_generator/__init__.py
  7. +18 −39 src/datamodel_code_generator/__main__.py
  8. +2 −2 src/datamodel_code_generator/arguments.py
  9. +9 −39 src/datamodel_code_generator/format.py
  10. +4 −1 src/datamodel_code_generator/http.py
  11. +5 −4 src/datamodel_code_generator/imports.py
  12. +4 −2 src/datamodel_code_generator/model/__init__.py
  13. +12 −23 src/datamodel_code_generator/model/base.py
  14. +7 −14 src/datamodel_code_generator/model/dataclass.py
  15. +2 −2 src/datamodel_code_generator/model/enum.py
  16. +9 −17 src/datamodel_code_generator/model/msgspec.py
  17. +4 −1 src/datamodel_code_generator/model/pydantic/__init__.py
  18. +4 −4 src/datamodel_code_generator/model/pydantic/base_model.py
  19. +2 −2 src/datamodel_code_generator/model/pydantic/dataclass.py
  20. +6 −3 src/datamodel_code_generator/model/pydantic/types.py
  21. +5 −2 src/datamodel_code_generator/model/pydantic_v2/__init__.py
  22. +4 −12 src/datamodel_code_generator/model/pydantic_v2/base_model.py
  23. +4 −1 src/datamodel_code_generator/model/pydantic_v2/types.py
  24. +2 −2 src/datamodel_code_generator/model/scalar.py
  25. +5 −16 src/datamodel_code_generator/model/typed_dict.py
  26. +6 −3 src/datamodel_code_generator/model/types.py
  27. +2 −2 src/datamodel_code_generator/model/union.py
  28. +6 −3 src/datamodel_code_generator/parser/__init__.py
  29. +11 −27 src/datamodel_code_generator/parser/base.py
  30. +7 −8 src/datamodel_code_generator/parser/graphql.py
  31. +33 −44 src/datamodel_code_generator/parser/jsonschema.py
  32. +20 −31 src/datamodel_code_generator/parser/openapi.py
  33. +11 −27 src/datamodel_code_generator/reference.py
  34. +14 −45 src/datamodel_code_generator/types.py
  35. +1 −10 src/datamodel_code_generator/util.py
  36. +10 −0 tests/conftest.py
  37. +1 −2 tests/data/expected/main/graphql/annotated.py
  38. +1 −2 tests/data/expected/main/graphql/annotated_field_aliases.py
  39. +1 −2 tests/data/expected/main/graphql/annotated_use_standard_collections.py
  40. +1 −2 tests/data/expected/main/graphql/annotated_use_standard_collections_use_union_operator.py
  41. +1 −2 tests/data/expected/main/graphql/annotated_use_union_operator.py
  42. +1 −2 tests/data/expected/main/jsonschema/discriminator_literals_msgspec.py
  43. +1 −2 tests/data/expected/main/jsonschema/discriminator_literals_msgspec_keyword_only.py
  44. +1 −2 tests/data/expected/main/jsonschema/discriminator_literals_msgspec_keyword_only_omit_defaults.py
  45. +1 −2 tests/data/expected/main/jsonschema/discriminator_with_external_reference_msgspec.py
  46. +1 −2 ...ma/discriminator_with_external_references_folder_msgspec/inner_folder/artificial_folder/type_1.py
  47. +1 −2 ...cted/main/jsonschema/discriminator_with_external_references_folder_msgspec/inner_folder/schema.py
  48. +1 −2 ...cted/main/jsonschema/discriminator_with_external_references_folder_msgspec/inner_folder/type_2.py
  49. +1 −2 ...xpected/main/jsonschema/discriminator_with_external_references_folder_msgspec/subfolder/type_5.py
  50. +1 −2 tests/data/expected/main/jsonschema/discriminator_with_external_references_folder_msgspec/type_4.py
  51. +1 −2 ...ta/expected/main/jsonschema/duplicate_field_constraints_msgspec_py38_collapse_root_models/test.py
  52. +1 −2 tests/data/expected/main/openapi/const_field_msgspec.py
  53. +1 −2 tests/data/expected/main/openapi/enum_models/{as_literal_py37.py → as_literal.py}
  54. +1 −2 tests/data/expected/main/openapi/msgspec_struct.py
  55. +1 −2 tests/data/expected/main/openapi/msgspec_struct_snake_case.py
  56. +8 −4 tests/data/expected/main/openapi/pyproject.py
  57. +6 −4 tests/data/expected/main/openapi/target_python_version.py
  58. 0 tests/data/expected/main/openapi/{typed_dict_py_38.py → typed_dict_py.py}
  59. +1 −2 tests/data/expected/main/openapi/use_annotated_with_field_constraints_py38.py
  60. +6 −4 tests/data/expected/main_kr/target_python_version/output.py
  61. 0 tests/data/expected/parser/openapi/openapi_parser_parse_enum_models/{output_py37.py → output.py}
  62. +0 −136 tests/data/expected/parser/openapi/openapi_parser_parse_enum_models/output_py36.py
  63. +1 −1 tests/data/project/pyproject.toml
  64. +32 −53 tests/main/jsonschema/test_main_jsonschema.py
  65. +28 −68 tests/main/openapi/test_main_openapi.py
  66. +8 −18 tests/main/test_types.py
  67. +2 −2 tests/parser/test_jsonschema.py
  68. +3 −8 tests/parser/test_openapi.py
  69. +8 −8 tests/test_format.py
  70. +4 −1 tests/test_imports.py
  71. +2 −2 tests/test_main_kr.py
  72. +0 −7 tox.ini
  73. +271 −1,776 uv.lock
5 changes: 1 addition & 4 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -19,16 +19,13 @@ jobs:
strategy:
fail-fast: false
matrix:
py: ["3.13", "3.12", "3.11", "3.10", "3.9", "3.8"]
py: ["3.13", "3.12", "3.11", "3.10", "3.9"]
os: [ubuntu-24.04, windows-latest, macos-latest]
tox_env: ['']
include:
- tox_env: py3.12-black24
- tox_env: py3.12-black23
- tox_env: py3.12-black22
- tox_env: py3.8-black19
- tox_env: py3.8-pydantic18
- tox_env: py3.8-isort4
runs-on: ${{ matrix.os == '' && 'ubuntu-24.04' || matrix.os }}
env:
OS: ${{ matrix.os == '' && 'ubuntu-24.04' || matrix.os}}
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@ repos:
exclude: "^tests/data"
- id: ruff
exclude: "^tests/data"
args: ["--exit-non-zero-on-fix"]
args: ["--fix", "--unsafe-fixes", "--exit-non-zero-on-fix"]
- repo: https://github.com/codespell-project/codespell
rev: v2.4.1
hooks:
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -462,8 +462,8 @@ Model customization:
pydantic: datetime, dataclass: str, ...)
--reuse-model Reuse models on the field when a module has the model with the same
content
--target-python-version {3.6,3.7,3.8,3.9,3.10,3.11,3.12,3.13}
target python version (default: 3.8)
--target-python-version {3.9,3.10,3.11,3.12,3.13}
target python version
--treat-dot-as-module
treat dotted module names as modules
--use-exact-imports import exact types instead of modules, for example: "from .foo
4 changes: 2 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -454,8 +454,8 @@ Model customization:
pydantic: datetime, dataclass: str, ...)
--reuse-model Reuse models on the field when a module has the model with the same
content
--target-python-version {3.6,3.7,3.8,3.9,3.10,3.11,3.12,3.13}
target python version (default: 3.8)
--target-python-version {3.9,3.10,3.11,3.12,3.13}
target python version
--treat-dot-as-module
treat dotted module names as modules
--use-exact-imports import exact types instead of modules, for example: "from .foo
20 changes: 2 additions & 18 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -12,13 +12,12 @@ readme.content-type = "text/markdown"
readme.file = "README.md"
license = "MIT"
authors = [ { name = "Koudai Aono", email = "koxudaxi@gmail.com" } ]
requires-python = ">=3.8"
requires-python = ">=3.9"
classifiers = [
"Development Status :: 4 - Beta",
"License :: OSI Approved :: MIT License",
"Natural Language :: English",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
@@ -98,13 +97,10 @@ docs = [
"mkdocs>=1.6",
"mkdocs-material>=9.5.31",
]
black19-pydantic18 = [ "black==19.10b0", "pydantic==1.8.2" ]
black22 = [ "black==22.1" ]
black23 = [ "black==23.12" ]
black24 = [ "black==24.1" ]
pydantic18 = [ "pydantic==1.8.2" ]
isort4-pydantic15 = [ "isort[pyproject]==4.3.21", "pydantic==1.5.1" ]
fix = [ "pre-commit>=3.5" ]
fix = [ "pre-commit-uv>=4.1.4" ]
pkg-meta = [ "check-wheel-contents>=0.6.1", "twine>=6.1", "uv>=0.5.22" ]
coverage = [
"covdefaults>=2.3",
@@ -220,18 +216,6 @@ conflicts = [
{ group = "black24" },
{ group = "black22" },
{ group = "black23" },
{ group = "black19-pydantic18" },
{ group = "pydantic18" },
{ group = "isort4-pydantic15" },
{ group = "pkg-meta" },
],
[
{ group = "black24" },
{ group = "black22" },
{ group = "black23" },
{ group = "black19-pydantic18" },
{ group = "pydantic18" },
{ group = "isort4-pydantic15" },
{ group = "dev" },
],
]
17 changes: 10 additions & 7 deletions src/datamodel_code_generator/__init__.py
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
import contextlib
import os
import sys
from collections.abc import Iterator, Mapping, Sequence
from datetime import datetime, timezone
from enum import Enum
from pathlib import Path
@@ -11,10 +12,7 @@
TYPE_CHECKING,
Any,
Callable,
Dict,
Iterator,
Mapping,
Sequence,
Final,
TextIO,
TypeVar,
cast,
@@ -24,10 +22,13 @@
import yaml

import datamodel_code_generator.pydantic_patch # noqa: F401
from datamodel_code_generator.format import DatetimeClassType, PythonVersion
from datamodel_code_generator.format import DatetimeClassType, PythonVersion, PythonVersionMin
from datamodel_code_generator.parser import DefaultPutDict, LiteralType
from datamodel_code_generator.util import SafeLoader

MIN_VERSION: Final[int] = 9
MAX_VERSION: Final[int] = 13

T = TypeVar("T")

try:
@@ -212,7 +213,7 @@ def generate( # noqa: PLR0912, PLR0913, PLR0914, PLR0915
input_file_type: InputFileType = InputFileType.Auto,
output: Path | None = None,
output_model_type: DataModelType = DataModelType.PydanticBaseModel,
target_python_version: PythonVersion = PythonVersion.PY_38,
target_python_version: PythonVersion = PythonVersionMin,
base_class: str = "",
additional_imports: list[str] | None = None,
custom_template_dir: Path | None = None,
@@ -367,7 +368,7 @@ def get_header_and_first_line(csv_file: IO[str]) -> dict[str, Any]:
obj = (
ast.literal_eval(input_.read_text(encoding=encoding))
if isinstance(input_, Path)
else cast("Dict[Any, Any]", input_)
else cast("dict[Any, Any]", input_)
)
else: # pragma: no cover
msg = f"Unsupported input file type: {input_file_type}"
@@ -554,6 +555,8 @@ def infer_input_type(text: str) -> InputFileType:
)

__all__ = [
"MAX_VERSION",
"MIN_VERSION",
"DefaultPutDict",
"Error",
"InputFileType",
57 changes: 18 additions & 39 deletions src/datamodel_code_generator/__main__.py
Original file line number Diff line number Diff line change
@@ -9,20 +9,11 @@
import sys
import warnings
from collections import defaultdict
from collections.abc import Sequence # noqa: TC003 # pydantic needs it
from enum import IntEnum
from io import TextIOBase
from pathlib import Path
from typing import (
TYPE_CHECKING,
Any,
List,
Optional,
Sequence,
Set,
Tuple,
Union,
cast,
)
from typing import TYPE_CHECKING, Any, Optional, Union, cast
from urllib.parse import ParseResult, urlparse

import argcomplete
@@ -47,6 +38,7 @@
from datamodel_code_generator.format import (
DatetimeClassType,
PythonVersion,
PythonVersionMin,
is_supported_in_black,
)
from datamodel_code_generator.model.pydantic_v2 import UnionMode # noqa: TC001 # needed for pydantic
@@ -142,34 +134,21 @@ def validate_url(cls, value: Any) -> ParseResult | None: # noqa: N805
msg = f"This protocol doesn't support only http/https. --input={value}"
raise Error(msg) # pragma: no cover

@model_validator(mode="after")
def validate_use_generic_container_types(cls, values: dict[str, Any]) -> dict[str, Any]: # noqa: N805
if values.get("use_generic_container_types"):
target_python_version: PythonVersion = values["target_python_version"]
if target_python_version == target_python_version.PY_36:
msg = (
f"`--use-generic-container-types` can not be used with `--target-python-version` "
f"{target_python_version.PY_36.value}.\n"
" The version will be not supported in a future version"
)
raise Error(msg)
return values

@model_validator(mode="after")
@model_validator()
def validate_original_field_name_delimiter(cls, values: dict[str, Any]) -> dict[str, Any]: # noqa: N805
if values.get("original_field_name_delimiter") is not None and not values.get("snake_case_field"):
msg = "`--original-field-name-delimiter` can not be used without `--snake-case-field`."
raise Error(msg)
return values

@model_validator(mode="after")
@model_validator()
def validate_custom_file_header(cls, values: dict[str, Any]) -> dict[str, Any]: # noqa: N805
if values.get("custom_file_header") and values.get("custom_file_header_path"):
msg = "`--custom_file_header_path` can not be used with `--custom_file_header`."
raise Error(msg) # pragma: no cover
return values

@model_validator(mode="after")
@model_validator()
def validate_keyword_only(cls, values: dict[str, Any]) -> dict[str, Any]: # noqa: N805
output_model_type: DataModelType = values.get("output_model_type") # pyright: ignore[reportAssignmentType]
python_target: PythonVersion = values.get("target_python_version") # pyright: ignore[reportAssignmentType]
@@ -182,7 +161,7 @@ def validate_keyword_only(cls, values: dict[str, Any]) -> dict[str, Any]: # noq
raise Error(msg)
return values

@model_validator(mode="after")
@model_validator()
def validate_output_datetime_class(cls, values: dict[str, Any]) -> dict[str, Any]: # noqa: N805
datetime_class_type: DatetimeClassType | None = values.get("output_datetime_class")
if (
@@ -246,15 +225,15 @@ def validate_custom_formatters(cls, values: dict[str, Any]) -> dict[str, Any]:

if PYDANTIC_V2:

@model_validator(mode="after") # pyright: ignore[reportArgumentType]
@model_validator() # pyright: ignore[reportArgumentType]
def validate_root(self: Self) -> Self:
if self.use_annotated:
self.field_constraints = True
return self

else:

@model_validator(mode="after")
@model_validator()
def validate_root(cls, values: Any) -> Any: # noqa: N805
if values.get("use_annotated"):
values["field_constraints"] = True
@@ -266,9 +245,9 @@ def validate_root(cls, values: Any) -> Any: # noqa: N805
output: Optional[Path] = None # noqa: UP045
debug: bool = False
disable_warnings: bool = False
target_python_version: PythonVersion = PythonVersion.PY_38
target_python_version: PythonVersion = PythonVersionMin
base_class: str = ""
additional_imports: Optional[List[str]] = None # noqa: UP006, UP045
additional_imports: Optional[list[str]] = None # noqa: UP045
custom_template_dir: Optional[Path] = None # noqa: UP045
extra_template_data: Optional[TextIOBase] = None # noqa: UP045
validation: bool = False
@@ -299,17 +278,17 @@ def validate_root(cls, values: Any) -> Any: # noqa: N805
enable_faux_immutability: bool = False
url: Optional[ParseResult] = None # noqa: UP045
disable_appending_item_suffix: bool = False
strict_types: List[StrictTypes] = [] # noqa: UP006
strict_types: list[StrictTypes] = []
empty_enum_field_name: Optional[str] = None # noqa: UP045
field_extra_keys: Optional[Set[str]] = None # noqa: UP006, UP045
field_extra_keys: Optional[set[str]] = None # noqa: UP045
field_include_all_keys: bool = False
field_extra_keys_without_x_prefix: Optional[Set[str]] = None # noqa: UP006, UP045
openapi_scopes: Optional[List[OpenAPIScope]] = [OpenAPIScope.Schemas] # noqa: UP006, UP045
field_extra_keys_without_x_prefix: Optional[set[str]] = None # noqa: UP045
openapi_scopes: Optional[list[OpenAPIScope]] = [OpenAPIScope.Schemas] # noqa: UP045
wrap_string_literal: Optional[bool] = None # noqa: UP045
use_title_as_name: bool = False
use_operation_id_as_name: bool = False
use_unique_items_as_set: bool = False
http_headers: Optional[Sequence[Tuple[str, str]]] = None # noqa: UP006, UP045
http_headers: Optional[Sequence[tuple[str, str]]] = None # noqa: UP045
http_ignore_tls: bool = False
use_annotated: bool = False
use_non_positive_negative_number_constrained_types: bool = False
@@ -322,10 +301,10 @@ def validate_root(cls, values: Any) -> Any: # noqa: N805
keep_model_order: bool = False
custom_file_header: Optional[str] = None # noqa: UP045
custom_file_header_path: Optional[Path] = None # noqa: UP045
custom_formatters: Optional[List[str]] = None # noqa: UP006, UP045
custom_formatters: Optional[list[str]] = None # noqa: UP045
custom_formatters_kwargs: Optional[TextIOBase] = None # noqa: UP045
use_pendulum: bool = False
http_query_parameters: Optional[Sequence[Tuple[str, str]]] = None # noqa: UP006, UP045
http_query_parameters: Optional[Sequence[tuple[str, str]]] = None # noqa: UP045
treat_dot_as_module: bool = False
use_exact_imports: bool = False
union_mode: Optional[UnionMode] = None # noqa: UP045
4 changes: 2 additions & 2 deletions src/datamodel_code_generator/arguments.py
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@

if TYPE_CHECKING:
from argparse import Action
from typing import Iterable
from collections.abc import Iterable

DEFAULT_ENCODING = locale.getpreferredencoding()

@@ -161,7 +161,7 @@ def start_section(self, heading: str | None) -> None:
)
model_options.add_argument(
"--target-python-version",
help="target python version (default: 3.8)",
help="target python version",
choices=[v.value for v in PythonVersion],
)
model_options.add_argument(
Loading