Skip to content

Commit 58394c8

Browse files
committed
improve nested comments for yaml
1 parent d3a1478 commit 58394c8

3 files changed

Lines changed: 37 additions & 41 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,4 +159,5 @@ cython_debug/
159159
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
160160
#.idea/
161161
*example*.py
162-
162+
config.yaml
163+
config.toml

confdantic/confdantic.py

Lines changed: 34 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,37 @@ class Confdantic(BaseModel):
5656

5757
model_config = ConfigDict(validate_assignment=True)
5858

59+
def to_commented_yaml(self) -> CommentedMap | CommentedSeq:
60+
"""
61+
Converts the Confdantic instance to a CommentedMap or CommentedSeq for YAML serialization.
62+
"""
63+
return self._to_commented_yaml(self)
64+
65+
def _to_commented_yaml(self, obj: T.Any) -> CommentedMap | CommentedSeq | T.Any:
66+
if issubclass(obj.__class__, BaseModel):
67+
cm = CommentedMap()
68+
for field_name, field in obj.model_fields.items():
69+
value = getattr(obj, field_name)
70+
cm[field_name] = self._to_commented_yaml(value)
71+
72+
comment = get_comment(field)
73+
if comment:
74+
cm.yaml_add_eol_comment(comment, field_name)
75+
76+
return cm
77+
elif isinstance(obj, list):
78+
cs = CommentedSeq()
79+
for item in obj:
80+
cs.append(self._to_commented_yaml(item))
81+
return cs
82+
elif isinstance(obj, dict):
83+
cm = CommentedMap()
84+
for key, value in obj.items():
85+
cm[key] = self._to_commented_yaml(value)
86+
return cm
87+
else:
88+
return obj
89+
5990
@classmethod
6091
def load(cls, filepath: str):
6192
"""
@@ -119,43 +150,6 @@ def save(self, filepath: str, overwrite: bool = True, comments: bool = True):
119150
case _:
120151
raise ValueError(f"Unknown file extension: {ext}")
121152

122-
def _add_comments_to_yaml(self, data: T.Any, model: type[BaseModel]) -> T.Any:
123-
if isinstance(data, dict):
124-
comments_map = CommentedMap(data)
125-
for name, field in model.model_fields.items():
126-
if name not in data:
127-
continue
128-
129-
comment = get_comment(field)
130-
if comment:
131-
comments_map.yaml_add_eol_comment(comment, name)
132-
133-
if isinstance(data[name], dict):
134-
try:
135-
is_base_model = issubclass(field.annotation, BaseModel)
136-
except TypeError:
137-
is_base_model = False
138-
139-
if is_base_model:
140-
comments_map[name] = self._add_comments_to_yaml(
141-
data[name], field.annotation
142-
)
143-
elif isinstance(data[name], list):
144-
comments_map[name] = self._add_comments_to_yaml(data[name], field.annotation)
145-
return comments_map
146-
elif isinstance(data, list):
147-
comments_seq = CommentedSeq(data)
148-
for i, item in enumerate(data):
149-
if isinstance(item, dict):
150-
try:
151-
item_type = model.__args__[0] if hasattr(model, "__args__") else model
152-
comments_seq[i] = self._add_comments_to_yaml(item, item_type)
153-
except (AttributeError, IndexError):
154-
pass
155-
return comments_seq
156-
else:
157-
return data
158-
159153
@classmethod
160154
def load_yaml(cls, filepath: str):
161155
if not os.path.exists(filepath):
@@ -231,8 +225,9 @@ def save_yaml(self, filepath: str, overwrite: bool = True, comments: bool = True
231225

232226
data = self.model_dump()
233227
if comments:
234-
data = self._add_comments_to_yaml(data, type(self))
235-
228+
data = self.to_commented_yaml()
229+
else:
230+
data = self.model_dump()
236231
with open(filepath, "w") as f:
237232
yaml.dump(data, f)
238233

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "confdantic"
3-
version = "0.0.2"
3+
version = "0.0.3"
44
description = "Confdantic is a Python library that enhances Pydantic's capabilities for working with JSON, YAML, and TOML formats. It preserves field descriptions as comments when serializing to YAML or TOML, making it great for generating user-friendly configuration files."
55
authors = [{ name = "Žiga Ivanšek", email = "ziga.ivansek@gmail.com" }]
66
license = { file = "LICENSE.txt" }

0 commit comments

Comments
 (0)