1313import os
1414import tomlkit
1515from typing import Any , Dict
16+ import configparser
17+ from io import StringIO
18+ import shlex
19+ import yaml
1620
1721try : # use gamuLogger if available # pragma: no cover
1822 from gamuLogger import Logger
@@ -395,6 +399,98 @@ def _from_string(self, config_string: str) -> None:
395399 """
396400 self ._config = tomlkit .loads (config_string )
397401
402+
403+ class YAMLConfig (FileConfig ):
404+ """
405+ YAML configuration management class.
406+ """
407+ def _to_string (self ) -> str :
408+ """
409+ String representation of the configuration in YAML format.
410+ """
411+ return yaml .safe_dump (self ._config , sort_keys = False )
412+
413+ def _from_string (self , config_string : str ) -> None :
414+ """
415+ Create a configuration object from a YAML string.
416+ """
417+ loaded = yaml .safe_load (config_string )
418+ # YAML can produce None for empty documents
419+ self ._config = loaded if isinstance (loaded , dict ) and loaded is not None else ({} if loaded is None else loaded )
420+
421+
422+ class INIConfig (FileConfig ):
423+ """
424+ INI configuration management class using configparser.
425+ Sections become nested dictionaries.
426+ """
427+ def _to_string (self ) -> str :
428+ parser = configparser .RawConfigParser ()
429+ # If there are top-level keys (not nested under a section), put them in DEFAULT
430+ defaults = {}
431+ for k , v in list (self ._config .items ()):
432+ if isinstance (v , dict ):
433+ parser [k ] = {str (kk ): str (vv ) for kk , vv in v .items ()}
434+ else :
435+ defaults [k ] = str (v )
436+ if defaults :
437+ parser .defaults ().update ({str (k ): str (v ) for k , v in defaults .items ()})
438+ sio = StringIO ()
439+ parser .write (sio )
440+ return sio .getvalue ()
441+
442+ def _from_string (self , config_string : str ) -> None :
443+ parser = configparser .RawConfigParser ()
444+ try :
445+ parser .read_string (config_string )
446+ except configparser .MissingSectionHeaderError :
447+ # Treat top-level key=value pairs by prepending a DEFAULT section
448+ parser .read_string ("[DEFAULT]\n " + config_string )
449+ data : Dict [str , Any ] = {}
450+ # defaults (top-level keys)
451+ defaults = dict (parser .defaults ())
452+ for k , v in defaults .items ():
453+ data [k ] = v
454+ for section in parser .sections ():
455+ items = dict (parser .items (section ))
456+ data [section ] = items
457+ self ._config = data
458+
459+
460+ class EnvConfig (FileConfig ):
461+ """
462+ .env-style configuration (KEY=VALUE per line).
463+ """
464+ def _to_string (self ) -> str :
465+ lines = []
466+ for k , v in self ._config .items ():
467+ lines .append (f"{ k } ={ v } " )
468+ return "\n " .join (lines ) + ("\n " if lines else "" )
469+
470+ def _from_string (self , config_string : str ) -> None :
471+ data : Dict [str , Any ] = {}
472+ for raw_line in config_string .splitlines ():
473+ line = raw_line .strip ()
474+ if not line or line .startswith ('#' ) or line .startswith (';' ):
475+ continue
476+ # support export VAR=VAL
477+ if line .startswith ('export ' ):
478+ line = line [len ('export ' ):]
479+ if '=' not in line :
480+ continue
481+ key , value = line .split ('=' , 1 )
482+ key = key .strip ()
483+ value = value .strip ()
484+ # remove surrounding quotes if present
485+ if (value .startswith ('"' ) and value .endswith ('"' )) or (value .startswith ("'" ) and value .endswith ("'" )):
486+ try :
487+ # preserve escaped sequences by using shlex
488+ value = shlex .split (value )[0 ]
489+ except Exception :
490+ value = value [1 :- 1 ]
491+ data [key ] = value
492+ self ._config = data
493+
398494class MemoryConfig (BaseConfig ):
399495 """
400496 In-memory configuration management class.
@@ -418,4 +514,10 @@ def get_config(file_path: str) -> FileConfig:
418514 return JSONConfig (file_path )
419515 if file_path .lower ().endswith ('.toml' ):
420516 return TOMLConfig (file_path )
517+ if file_path .lower ().endswith (('.yaml' , '.yml' )):
518+ return YAMLConfig (file_path )
519+ if file_path .lower ().endswith (('.ini' , '.cfg' )):
520+ return INIConfig (file_path )
521+ if file_path .lower ().endswith ('.env' ):
522+ return EnvConfig (file_path )
421523 raise ValueError (f"Unsupported configuration file format: { file_path } " )
0 commit comments