Skip to content

Commit 5dcbc09

Browse files
butvinmclaude
andauthored
Fix type alias template crash with reuse_model on empty fields (#3060)
* Fix type alias template crash when reuse_model creates empty fields Guard fields[0] access in TypeStatement, TypeAliasType, TypeAliasAnnotation, and UnionTypeStatement templates. When fields is empty (reuse model), fall back to base_class, producing e.g. `type Foo = Bar`. Fixes #3059 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix same empty fields crash in UnionTypeAliasAnnotation and UnionTypeAliasType These two union templates had the identical unguarded fields[0] access. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Skip reuse_model type alias test on old black versions Black 22/23 can't parse Python 3.14 type statement syntax. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Rename input fixture to match expected output filename Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 8c96599 commit 5dcbc09

9 files changed

Lines changed: 80 additions & 9 deletions

File tree

src/datamodel_code_generator/model/template/TypeAliasAnnotation.jinja2

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,22 @@ Annotated[{{ _field.type_hint }}, {{ _field.field }}]
88
{%- endif -%}
99
{%- endmacro -%}
1010

11+
{%- if fields %}
1112
{{ class_name }}: TypeAlias = {{ get_type_annotation(fields[0]) }}{% if comment is defined %} # {{ comment }}{% endif %}
1213
{%- if description %}
1314
"""
1415
{{ description | escape_docstring | indent(0) }}
1516
"""
16-
{%- elif fields and fields[0].docstring %}
17+
{%- elif fields[0].docstring %}
1718
"""
1819
{{ fields[0].docstring | escape_docstring | indent(0) }}
1920
"""
2021
{%- endif %}
22+
{%- else %}
23+
{{ class_name }}: TypeAlias = {{ base_class }}{% if comment is defined %} # {{ comment }}{% endif %}
24+
{%- if description %}
25+
"""
26+
{{ description | escape_docstring | indent(0) }}
27+
"""
28+
{%- endif %}
29+
{%- endif %}

src/datamodel_code_generator/model/template/TypeAliasType.jinja2

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,22 @@ Annotated[{{ _field.type_hint }}, {{ _field.field }}]
88
{%- endif -%}
99
{%- endmacro -%}
1010

11+
{%- if fields %}
1112
{{ class_name }} = TypeAliasType("{{ class_name }}", {{ get_type_annotation(fields[0]) }}){% if comment is defined %} # {{ comment }}{% endif %}
1213
{%- if description %}
1314
"""
1415
{{ description | escape_docstring | indent(0) }}
1516
"""
16-
{%- elif fields and fields[0].docstring %}
17+
{%- elif fields[0].docstring %}
1718
"""
1819
{{ fields[0].docstring | escape_docstring | indent(0) }}
1920
"""
2021
{%- endif %}
22+
{%- else %}
23+
{{ class_name }} = TypeAliasType("{{ class_name }}", {{ base_class }}){% if comment is defined %} # {{ comment }}{% endif %}
24+
{%- if description %}
25+
"""
26+
{{ description | escape_docstring | indent(0) }}
27+
"""
28+
{%- endif %}
29+
{%- endif %}

src/datamodel_code_generator/model/template/TypeStatement.jinja2

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,22 @@ Annotated[{{ _field.type_hint }}, {{ _field.field }}]
88
{%- endif -%}
99
{%- endmacro -%}
1010

11+
{%- if fields %}
1112
type {{ class_name }} = {{ get_type_annotation(fields[0]) }}{% if comment is defined %} # {{ comment }}{% endif %}
1213
{%- if description %}
1314
"""
1415
{{ description | escape_docstring | indent(0) }}
1516
"""
16-
{%- elif fields and fields[0].docstring %}
17+
{%- elif fields[0].docstring %}
1718
"""
1819
{{ fields[0].docstring | escape_docstring | indent(0) }}
1920
"""
21+
{%- endif %}
22+
{%- else %}
23+
type {{ class_name }} = {{ base_class }}{% if comment is defined %} # {{ comment }}{% endif %}
24+
{%- if description %}
25+
"""
26+
{{ description | escape_docstring | indent(0) }}
27+
"""
28+
{%- endif %}
2029
{%- endif %}

src/datamodel_code_generator/model/template/UnionTypeAliasAnnotation.jinja2

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@
66
{%- for field in fields %}
77
'{{ field.name }}',
88
{%- endfor %}
9-
]{% else %}
10-
{{ class_name }}: TypeAlias = {{ fields[0].name }}{% endif %}
9+
]{% elif fields %}
10+
{{ class_name }}: TypeAlias = {{ fields[0].name }}{% else %}
11+
{{ class_name }}: TypeAlias = {{ base_class }}{% endif %}

src/datamodel_code_generator/model/template/UnionTypeAliasType.jinja2

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@
66
{%- for field in fields %}
77
'{{ field.name }}',
88
{%- endfor %}
9-
]){% else %}
10-
{{ class_name }} = TypeAliasType("{{ class_name }}", {{ fields[0].name }}){% endif %}
9+
]){% elif fields %}
10+
{{ class_name }} = TypeAliasType("{{ class_name }}", {{ fields[0].name }}){% else %}
11+
{{ class_name }} = TypeAliasType("{{ class_name }}", {{ base_class }}){% endif %}

src/datamodel_code_generator/model/template/UnionTypeStatement.jinja2

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ type {{ class_name }} = Union[
66
{%- for field in fields %}
77
'{{ field.name }}',
88
{%- endfor %}
9-
]{% else %}
10-
type {{ class_name }} = {{ fields[0].name }}{% endif %}
9+
]{% elif fields %}
10+
type {{ class_name }} = {{ fields[0].name }}{% else %}
11+
type {{ class_name }} = {{ base_class }}{% endif %}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# generated by datamodel-codegen:
2+
# filename: reuse_model_with_type_alias.json
3+
# timestamp: 2019-07-26T00:00:00+00:00
4+
5+
type Foo = str
6+
7+
8+
type Bar = Foo
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"openapi": "3.0.0",
3+
"info": {"title": "Test", "version": "1.0.0"},
4+
"paths": {},
5+
"components": {
6+
"schemas": {
7+
"Foo": {"type": "string"},
8+
"Bar": {"type": "string"}
9+
}
10+
}
11+
}

tests/main/openapi/test_main_openapi.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5224,3 +5224,25 @@ def test_ref_merge_parameters(output_file: Path) -> None:
52245224
"parameters",
52255225
],
52265226
)
5227+
5228+
5229+
@BLACK_PY314_SKIP
5230+
def test_main_reuse_model_with_type_alias(output_file: Path) -> None:
5231+
"""Test --reuse-model with --use-type-alias doesn't crash on empty fields.
5232+
5233+
Regression test for https://github.com/koxudaxi/datamodel-code-generator/issues/3059
5234+
"""
5235+
run_main_and_assert(
5236+
input_path=OPEN_API_DATA_PATH / "reuse_model_with_type_alias.json",
5237+
output_path=output_file,
5238+
input_file_type="openapi",
5239+
assert_func=assert_file_content,
5240+
extra_args=[
5241+
"--output-model-type",
5242+
"pydantic_v2.BaseModel",
5243+
"--target-python-version",
5244+
"3.14",
5245+
"--reuse-model",
5246+
"--use-type-alias",
5247+
],
5248+
)

0 commit comments

Comments
 (0)