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

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    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