@@ -68,30 +68,26 @@ class Confdantic(BaseModel):
6868
6969 def to_commented_yaml (self ) -> CommentedMap | CommentedSeq :
7070 """Converts the Confdantic instance to a CommentedMap or CommentedSeq for YAML serialization."""
71- return self ._to_commented_yaml (self )
71+ data = self .model_dump ()
72+ return self ._to_commented_yaml (data )
7273
7374 def _to_commented_yaml (self , obj : Any ) -> CommentedMap | CommentedSeq | Any :
74- if issubclass (obj . __class__ , BaseModel ):
75+ if isinstance (obj , dict ):
7576 cm = CommentedMap ()
76- for field_name , field in obj .model_fields .items ():
77- value = getattr (obj , field_name )
78- cm [field_name ] = self ._to_commented_yaml (value )
79-
80- comment = get_comment (field , format = "yaml" )
81- if comment :
82- cm .yaml_add_eol_comment (comment , field_name )
77+ for key , value in obj .items ():
78+ cm [key ] = self ._to_commented_yaml (value )
8379
80+ if issubclass (self .__class__ , BaseModel ) and key in self .__class__ .model_fields :
81+ field = self .__class__ .model_fields [key ]
82+ comment = get_comment (field , format = "yaml" )
83+ if comment :
84+ cm .yaml_add_eol_comment (comment , key )
8485 return cm
8586 elif isinstance (obj , list ):
8687 cs = CommentedSeq ()
8788 for item in obj :
8889 cs .append (self ._to_commented_yaml (item ))
8990 return cs
90- elif isinstance (obj , dict ):
91- cm = CommentedMap ()
92- for key , value in obj .items ():
93- cm [key ] = self ._to_commented_yaml (value )
94- return cm
9591 else :
9692 return obj
9793
@@ -126,7 +122,13 @@ def load(cls, filepath: str):
126122 case _:
127123 raise ValueError (f"Unknown file extension: { ext } " )
128124
129- def save (self , filepath : str , overwrite : bool = True , comments : bool = True ):
125+ def save (
126+ self ,
127+ filepath : str ,
128+ overwrite : bool = True ,
129+ comments : bool = True ,
130+ serialize_unsupported : bool = False ,
131+ ):
130132 """
131133 Save the configuration to a file.
132134
@@ -137,6 +139,7 @@ def save(self, filepath: str, overwrite: bool = True, comments: bool = True):
137139 filepath (str): The path where the configuration file will be saved.
138140 overwrite (bool, optional): Whether to overwrite the file if it already exists. Defaults to True.
139141 comments (bool, optional): Whether to include comments in the saved file. Defaults to True.
142+ serialize_unsupported (bool, optional): Whether to serialize unsupported types as strings. Defaults to False.
140143
141144 Raises:
142145 FileExistsError: If the file already exists and overwrite is False.
@@ -148,11 +151,22 @@ def save(self, filepath: str, overwrite: bool = True, comments: bool = True):
148151 ext = file_ext (filepath )
149152 match ext :
150153 case "toml" | "tml" :
151- return self .save_toml (filepath , overwrite = overwrite , comments = comments )
154+ return self .save_toml (
155+ filepath ,
156+ overwrite = overwrite ,
157+ comments = comments ,
158+ serialize_unsupported = serialize_unsupported ,
159+ )
152160 case "yaml" | "yml" :
153- return self .save_yaml (filepath = filepath , overwrite = overwrite )
161+ return self .save_yaml (
162+ filepath = filepath ,
163+ overwrite = overwrite ,
164+ serialize_unsupported = serialize_unsupported ,
165+ )
154166 case "json" :
155- return self .save_json (filepath , overwrite = overwrite )
167+ return self .save_json (
168+ filepath , overwrite = overwrite , serialize_unsupported = serialize_unsupported
169+ )
156170 case _:
157171 raise ValueError (f"Unknown file extension: { ext } " )
158172
@@ -173,14 +187,20 @@ def load_toml(cls, filepath: str):
173187 return cls .model_validate (toml .load (f ))
174188
175189 @classmethod
176- def load_json (cls , filepath : str ):
177- with open (filepath ) as f :
190+ def load_json (cls , filepath : str , encoding : str = "utf-8" ):
191+ with open (filepath , encoding = encoding ) as f :
178192 return cls .model_validate (json .load (f ))
179193
180- def save_toml (self , filepath : str , overwrite : bool = True , comments : bool = True ) -> None :
194+ def save_toml (
195+ self ,
196+ filepath : str ,
197+ overwrite : bool = True ,
198+ comments : bool = True ,
199+ serialize_unsupported : bool = False ,
200+ ) -> None :
181201 if os .path .exists (filepath ) and not overwrite :
182202 raise FileExistsError (filepath )
183- data = self .model_dump ()
203+ data = self .model_dump (mode = "json" ) if serialize_unsupported else self . model_dump ( )
184204 toml_string = tomlkit .dumps (data )
185205 toml_doc = tomlkit .loads (toml_string )
186206
@@ -189,7 +209,7 @@ def save_toml(self, filepath: str, overwrite: bool = True, comments: bool = True
189209 tomlkit .dump (toml_doc , f )
190210 return
191211
192- for name , field in self .model_fields .items ():
212+ for name , field in self .__class__ . model_fields .items ():
193213 item = toml_doc .item (name )
194214 comment = get_comment (field , format = "toml" )
195215 if comment :
@@ -214,26 +234,35 @@ def save_toml(self, filepath: str, overwrite: bool = True, comments: bool = True
214234 with open (filepath , "w" ) as f :
215235 tomlkit .dump (toml_doc , f )
216236
217- def save_json (self , filepath : str , overwrite : bool = True ) -> None :
237+ def save_json (
238+ self ,
239+ filepath : str ,
240+ overwrite : bool = True ,
241+ serialize_unsupported : bool = False ,
242+ ) -> None :
218243 if os .path .exists (filepath ) and not overwrite :
219244 raise FileExistsError (filepath )
220- data = self .model_dump ()
245+ data = self .model_dump (mode = "json" ) if serialize_unsupported else self . model_dump ( )
221246 with open (filepath , "w" ) as f :
222- json .dump (data , f )
223-
224- def save_yaml (self , filepath : str , overwrite : bool = True , comments : bool = True ) -> None :
247+ json .dump (data , f , indent = 4 , default = str )
248+
249+ def save_yaml (
250+ self ,
251+ filepath : str ,
252+ overwrite : bool = True ,
253+ comments : bool = True ,
254+ serialize_unsupported : bool = False ,
255+ ) -> None :
225256 if os .path .exists (filepath ) and not overwrite :
226257 raise FileExistsError (filepath )
227258
228259 yaml = YAML ()
229260 yaml .indent (mapping = 2 , sequence = 4 , offset = 2 )
230261 yaml .preserve_quotes = True
231262
232- data = self .model_dump ()
263+ data = self .model_dump (mode = "json" ) if serialize_unsupported else self . model_dump ( )
233264 if comments :
234- data = self .to_commented_yaml ()
235- else :
236- data = self .model_dump ()
265+ data = self ._to_commented_yaml (data )
237266 with open (filepath , "w" ) as f :
238267 yaml .dump (data , f )
239268
0 commit comments