Skip to content

Commit 35871c2

Browse files
Add validation error for --use-specialized-enum when target Python version < 3.11 (#2651)
* Fix --use-specialized-enum option to require Python 3.11+ and update help text * docs: update command help in README 🤖 Generated by GitHub Actions * Update help text for --use-specialized-enum option to clarify Python version requirement * docs: update command help in README 🤖 Generated by GitHub Actions * test: skip test for black version 22 due to StrEnum formatting issue --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 8bbc4df commit 35871c2

8 files changed

Lines changed: 130 additions & 5 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -397,8 +397,8 @@ Typing customization:
397397
Use pydantic.SerializeAsAny for fields with types that have subtypes
398398
(Pydantic v2 only)
399399
--use-specialized-enum, --no-use-specialized-enum
400-
Don''t use specialized Enum class (StrEnum, IntEnum) even if the
401-
target Python version supports it
400+
Use specialized Enum class (StrEnum, IntEnum). Requires --target-
401+
python-version 3.11+
402402
--use-standard-collections
403403
Use standard collections for type hinting (list, dict)
404404
--use-subclass-enum Define generic Enum class as subclass with field type when enum has

docs/index.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -389,8 +389,8 @@ Typing customization:
389389
Use pydantic.SerializeAsAny for fields with types that have subtypes
390390
(Pydantic v2 only)
391391
--use-specialized-enum, --no-use-specialized-enum
392-
Don''t use specialized Enum class (StrEnum, IntEnum) even if the
393-
target Python version supports it
392+
Use specialized Enum class (StrEnum, IntEnum). Requires --target-
393+
python-version 3.11+
394394
--use-standard-collections
395395
Use standard collections for type hinting (list, dict)
396396
--use-subclass-enum Define generic Enum class as subclass with field type when enum has

src/datamodel_code_generator/__main__.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,20 @@ def main(args: Sequence[str] | None = None) -> Exit: # noqa: PLR0911, PLR0912,
733733
file=sys.stderr,
734734
)
735735

736+
if (
737+
config.use_specialized_enum
738+
and namespace.use_specialized_enum is not False # CLI didn't disable it
739+
and (namespace.use_specialized_enum is True or pyproject_config.get("use_specialized_enum") is True)
740+
and not config.target_python_version.has_strenum
741+
):
742+
print( # noqa: T201
743+
f"Error: --use-specialized-enum requires --target-python-version 3.11 or later.\n"
744+
f"Current target version: {config.target_python_version.value}\n"
745+
f"StrEnum is only available in Python 3.11+.",
746+
file=sys.stderr,
747+
)
748+
return Exit.ERROR
749+
736750
extra_template_data: defaultdict[str, dict[str, Any]] | None
737751
if config.extra_template_data is None:
738752
extra_template_data = None

src/datamodel_code_generator/arguments.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ def start_section(self, heading: str | None) -> None:
399399
)
400400
typing_options.add_argument(
401401
"--use-specialized-enum",
402-
help="Don't use specialized Enum class (StrEnum, IntEnum) even if the target Python version supports it",
402+
help="Use specialized Enum class (StrEnum, IntEnum). Requires --target-python-version 3.11+",
403403
action=BooleanOptionalAction,
404404
default=None,
405405
)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# generated by datamodel-codegen:
2+
# filename: string_enum.json
3+
# timestamp: 2019-07-26T00:00:00+00:00
4+
5+
from __future__ import annotations
6+
7+
from enum import Enum
8+
9+
10+
class Model(Enum):
11+
A = 'A'
12+
B = 'B'
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# generated by datamodel-codegen:
2+
# filename: string_enum.json
3+
# timestamp: 2019-07-26T00:00:00+00:00
4+
5+
from __future__ import annotations
6+
7+
from enum import StrEnum
8+
9+
10+
class Model(StrEnum):
11+
A = 'A'
12+
B = 'B'
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"type": "string", "enum": ["A", "B"]}

tests/main/test_main_general.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from argparse import ArgumentTypeError, Namespace
66
from typing import TYPE_CHECKING
77

8+
import black
89
import pytest
910

1011
from datamodel_code_generator import (
@@ -864,3 +865,88 @@ def test_check_respects_pyproject_toml_settings(tmp_path: Path) -> None:
864865
extra_args=["--disable-timestamp", "--check"],
865866
expected_exit=Exit.OK,
866867
)
868+
869+
870+
def test_use_specialized_enum_requires_python_311(
871+
tmp_path: Path,
872+
capsys: pytest.CaptureFixture[str],
873+
) -> None:
874+
"""Test --use-specialized-enum requires --target-python-version 3.11+."""
875+
input_json = tmp_path / "input.json"
876+
input_json.write_text(
877+
'{"type": "string", "enum": ["A", "B"]}',
878+
encoding="utf-8",
879+
)
880+
881+
run_main_and_assert(
882+
input_path=input_json,
883+
output_path=tmp_path / "output.py",
884+
input_file_type="jsonschema",
885+
extra_args=["--use-specialized-enum"],
886+
expected_exit=Exit.ERROR,
887+
capsys=capsys,
888+
expected_stderr_contains="--use-specialized-enum requires --target-python-version 3.11 or later",
889+
)
890+
891+
892+
@pytest.mark.skipif(
893+
black.__version__.split(".")[0] == "22",
894+
reason="Installed black doesn't support StrEnum formatting",
895+
)
896+
def test_use_specialized_enum_with_python_311_ok(output_file: Path) -> None:
897+
"""Test --use-specialized-enum works with --target-python-version 3.11."""
898+
run_main_and_assert(
899+
input_path=JSON_SCHEMA_DATA_PATH / "string_enum.json",
900+
output_path=output_file,
901+
input_file_type="jsonschema",
902+
extra_args=["--use-specialized-enum", "--target-python-version", "3.11"],
903+
assert_func=assert_file_content,
904+
expected_file="use_specialized_enum_py311.py",
905+
)
906+
907+
908+
def test_use_specialized_enum_pyproject_requires_python_311(
909+
tmp_path: Path,
910+
capsys: pytest.CaptureFixture[str],
911+
) -> None:
912+
"""Test use_specialized_enum in pyproject.toml requires target_python_version 3.11+."""
913+
pyproject_toml = tmp_path / "pyproject.toml"
914+
pyproject_toml.write_text(
915+
"[tool.datamodel-codegen]\nuse_specialized_enum = true\n",
916+
encoding="utf-8",
917+
)
918+
919+
input_json = tmp_path / "input.json"
920+
input_json.write_text(
921+
'{"type": "string", "enum": ["A", "B"]}',
922+
encoding="utf-8",
923+
)
924+
925+
with chdir(tmp_path):
926+
run_main_and_assert(
927+
input_path=input_json,
928+
output_path=tmp_path / "output.py",
929+
input_file_type="jsonschema",
930+
expected_exit=Exit.ERROR,
931+
capsys=capsys,
932+
expected_stderr_contains="--use-specialized-enum requires --target-python-version 3.11 or later",
933+
)
934+
935+
936+
def test_use_specialized_enum_pyproject_override_with_cli(output_file: Path, tmp_path: Path) -> None:
937+
"""Test --no-use-specialized-enum CLI can override pyproject.toml use_specialized_enum=true."""
938+
pyproject_toml = tmp_path / "pyproject.toml"
939+
pyproject_toml.write_text(
940+
"[tool.datamodel-codegen]\nuse_specialized_enum = true\n",
941+
encoding="utf-8",
942+
)
943+
944+
with chdir(tmp_path):
945+
run_main_and_assert(
946+
input_path=JSON_SCHEMA_DATA_PATH / "string_enum.json",
947+
output_path=output_file,
948+
input_file_type="jsonschema",
949+
extra_args=["--no-use-specialized-enum"],
950+
assert_func=assert_file_content,
951+
expected_file="no_use_specialized_enum.py",
952+
)

0 commit comments

Comments
 (0)