Skip to content

Commit 3f6daa7

Browse files
committed
Sync RootModel config before rendering
1 parent 9638913 commit 3f6daa7

2 files changed

Lines changed: 55 additions & 6 deletions

File tree

src/datamodel_code_generator/model/pydantic_v2/root_model.py

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@
99

1010
from datamodel_code_generator import Error
1111
from datamodel_code_generator.imports import IMPORT_ANY, Import
12-
from datamodel_code_generator.model.pydantic_v2.base_model import _CONFIG_ITEMS_TEMPLATE_DATA_KEY, BaseModel
12+
from datamodel_code_generator.model.pydantic_v2.base_model import (
13+
_CONFIG_ITEMS_TEMPLATE_DATA_KEY,
14+
BaseModel,
15+
_config_dict_items,
16+
)
1317
from datamodel_code_generator.model.pydantic_v2.imports import IMPORT_CONFIG_DICT
1418

1519
IMPORT_ABC_ITERATOR = Import.from_full_path("collections.abc.Iterator")
@@ -44,15 +48,38 @@ def __init__(
4448

4549
super().__init__(**kwargs)
4650

47-
config = self.extra_template_data.get("config")
48-
has_meaningful_config = config is not None and (
49-
getattr(config, "regex_engine", None) is not None or getattr(config, "frozen", None) is not None
50-
)
51-
if not has_meaningful_config:
51+
if not self._has_meaningful_config(self.extra_template_data.get("config")):
5252
self.extra_template_data.pop("config", None)
5353
self.extra_template_data.pop(_CONFIG_ITEMS_TEMPLATE_DATA_KEY, None)
5454
self._additional_imports = [imp for imp in self._additional_imports if imp != IMPORT_CONFIG_DICT]
5555

56+
@staticmethod
57+
def _has_meaningful_config(config: Any) -> bool:
58+
match config:
59+
case None:
60+
return False
61+
case dict():
62+
return config.get("regex_engine") is not None or config.get("frozen") is not None
63+
case _:
64+
return getattr(config, "regex_engine", None) is not None or getattr(config, "frozen", None) is not None
65+
66+
def _sync_config_items(self) -> None:
67+
if _CONFIG_ITEMS_TEMPLATE_DATA_KEY in self.extra_template_data:
68+
return
69+
config = self.extra_template_data.get("config")
70+
if not self._has_meaningful_config(config):
71+
self.extra_template_data.pop("config", None)
72+
return
73+
if config_items := _config_dict_items(config):
74+
self.extra_template_data[_CONFIG_ITEMS_TEMPLATE_DATA_KEY] = config_items
75+
if IMPORT_CONFIG_DICT not in self._additional_imports:
76+
self._additional_imports.append(IMPORT_CONFIG_DICT)
77+
self.clear_imports_cache()
78+
return
79+
self.extra_template_data.pop("config", None)
80+
self._additional_imports = [imp for imp in self._additional_imports if imp != IMPORT_CONFIG_DICT]
81+
self.clear_imports_cache()
82+
5683
def add_sequence_interface(self, item_type: str, slice_type: str) -> None:
5784
"""Add sequence interface helpers that delegate to the wrapped root value."""
5885
self._additional_imports.append(IMPORT_ABC_ITERATOR)
@@ -68,6 +95,7 @@ def add_sequence_interface(self, item_type: str, slice_type: str) -> None:
6895

6996
def render(self, *, class_name: str | None = None) -> str:
7097
"""Render the RootModel and validate custom sequence templates when needed."""
98+
self._sync_config_items()
7199
rendered = super().render(class_name=class_name)
72100
self._validate_custom_template_sequence_interface(rendered)
73101
return rendered

tests/model/pydantic_v2/test_root_model.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,3 +293,24 @@ def test_root_model_renders_existing_config_object() -> None:
293293
assert "model_config = ConfigDict(" in rendered
294294
assert 'regex_engine="python-re",' in rendered
295295
assert _CONFIG_ITEMS_TEMPLATE_DATA_KEY in root_model.extra_template_data
296+
297+
298+
def test_root_model_renders_late_config_dict() -> None:
299+
"""RootModel syncs config_items for config added after initialization."""
300+
root_model = RootModel(
301+
fields=[
302+
DataModelFieldBase(
303+
name="a",
304+
data_type=DataType(type="str"),
305+
required=True,
306+
)
307+
],
308+
reference=Reference(name="TestRootModel", path="test_root_model"),
309+
)
310+
root_model.extra_template_data["config"] = {"regex_engine": '"python-re"'}
311+
312+
rendered = root_model.render()
313+
314+
assert "model_config = ConfigDict(" in rendered
315+
assert 'regex_engine="python-re",' in rendered
316+
assert _CONFIG_ITEMS_TEMPLATE_DATA_KEY in root_model.extra_template_data

0 commit comments

Comments
 (0)