Skip to content

Commit 2b659f0

Browse files
authored
Fix missing | None for nullable enum literals in TypedDict (#2991)
* Fix missing | None for nullable enum literals in TypedDict * Update expected output for MultipleTypeEnum with | None * Guard against all-None enums producing empty Literal * Remove unreachable empty-literal guard
1 parent 992af20 commit 2b659f0

File tree

6 files changed

+67
-3
lines changed

6 files changed

+67
-3
lines changed

src/datamodel_code_generator/parser/jsonschema.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3527,7 +3527,10 @@ def _parse_multiple_types_with_properties(
35273527

35283528
def parse_enum_as_literal(self, obj: JsonSchemaObject) -> DataType:
35293529
"""Parse enum values as a Literal type."""
3530-
return self.data_type(literals=[i for i in obj.enum if i is not None])
3530+
return self.data_type(
3531+
literals=[i for i in obj.enum if i is not None],
3532+
is_optional=None in obj.enum,
3533+
)
35313534

35323535
@classmethod
35333536
def _get_field_name_from_dict_enum(cls, enum_part: dict[str, Any], index: int) -> str:
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# generated by datamodel-codegen:
2+
# filename: nullable_enum_literal_typed_dict.json
3+
# timestamp: 2019-07-26T00:00:00+00:00
4+
5+
from __future__ import annotations
6+
7+
from typing import Literal, NotRequired, TypeAlias, TypedDict
8+
9+
StatusEnum: TypeAlias = Literal['active', 'inactive'] | None
10+
11+
12+
class Record(TypedDict):
13+
required_inline: Literal['active', 'inactive'] | None
14+
required_ref: StatusEnum
15+
optional_inline: NotRequired[Literal['pending', 'completed'] | None]

tests/data/expected/main/openapi/enum_models/all.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class AliasEnum(BaseModel):
4949

5050

5151
class MultipleTypeEnum(BaseModel):
52-
__root__: Literal['red', 'amber', 'green', 42]
52+
__root__: Literal['red', 'amber', 'green', 42] | None
5353

5454

5555
class SingleEnum(BaseModel):

tests/data/expected/main/openapi/enum_models/as_literal.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class AliasEnum(BaseModel):
4949

5050

5151
class MultipleTypeEnum(BaseModel):
52-
__root__: Literal['red', 'amber', 'green', 42]
52+
__root__: Literal['red', 'amber', 'green', 42] | None
5353

5454

5555
class SingleEnum(BaseModel):
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"title": "Record",
4+
"type": "object",
5+
"definitions": {
6+
"StatusEnum": {
7+
"type": ["string", "null"],
8+
"enum": ["active", "inactive", null]
9+
}
10+
},
11+
"properties": {
12+
"required_inline": {
13+
"type": ["string", "null"],
14+
"enum": ["active", "inactive", null]
15+
},
16+
"required_ref": {
17+
"$ref": "#/definitions/StatusEnum"
18+
},
19+
"optional_inline": {
20+
"type": ["string", "null"],
21+
"enum": ["pending", "completed", null]
22+
}
23+
},
24+
"required": ["required_inline", "required_ref"]
25+
}

tests/main/jsonschema/test_main_jsonschema.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4311,6 +4311,27 @@ def test_main_typed_dict_enum_field_as_literal_all(output_file: Path) -> None:
43114311
)
43124312

43134313

4314+
@pytest.mark.skipif(
4315+
black.__version__.split(".")[0] == "22",
4316+
reason="Installed black doesn't support Python version 3.11",
4317+
)
4318+
def test_main_typed_dict_nullable_enum_literal(output_file: Path) -> None:
4319+
"""Test TypedDict with nullable enum literals generates | None correctly."""
4320+
run_main_and_assert(
4321+
input_path=JSON_SCHEMA_DATA_PATH / "nullable_enum_literal_typed_dict.json",
4322+
output_path=output_file,
4323+
input_file_type=None,
4324+
assert_func=assert_file_content,
4325+
expected_file="typed_dict_nullable_enum_literal.py",
4326+
extra_args=[
4327+
"--output-model-type",
4328+
"typing.TypedDict",
4329+
"--target-python-version",
4330+
"3.11",
4331+
],
4332+
)
4333+
4334+
43144335
@pytest.mark.cli_doc(
43154336
options=["--use-closed-typed-dict"],
43164337
option_description="""Generate TypedDict with PEP 728 closed/extra_items (default: enabled).

0 commit comments

Comments
 (0)