@@ -670,6 +670,7 @@ def __init__(self, defaults=None, dict_type=_default_dict,
670670 self ._optcre = re .compile (self ._OPT_TMPL .format (delim = d ),
671671 re .VERBOSE )
672672 self ._comments = _CommentSpec (comment_prefixes or (), inline_comment_prefixes or ())
673+ self ._loaded_sources = []
673674 self ._strict = strict
674675 self ._allow_no_value = allow_no_value
675676 self ._empty_lines_in_values = empty_lines_in_values
@@ -757,6 +758,7 @@ def read(self, filenames, encoding=None):
757758 if isinstance (filename , os .PathLike ):
758759 filename = os .fspath (filename )
759760 read_ok .append (filename )
761+ self ._loaded_sources .append (read_ok )
760762 return read_ok
761763
762764 def read_file (self , f , source = None ):
@@ -773,6 +775,7 @@ def read_file(self, f, source=None):
773775 except AttributeError :
774776 source = '<???>'
775777 self ._read (f , source )
778+ self ._loaded_sources .append (source )
776779
777780 def read_string (self , string , source = '<string>' ):
778781 """Read configuration from a given string."""
@@ -809,6 +812,7 @@ def read_dict(self, dictionary, source='<dict>'):
809812 raise DuplicateOptionError (section , key , source )
810813 elements_added .add ((section , key ))
811814 self .set (section , key , value )
815+ self ._loaded_sources .append (source )
812816
813817 def get (self , section , option , * , raw = False , vars = None , fallback = _UNSET ):
814818 """Get an option value for a given section.
@@ -1048,6 +1052,38 @@ def __iter__(self):
10481052 # XXX does it break when underlying container state changed?
10491053 return itertools .chain ((self .default_section ,), self ._sections .keys ())
10501054
1055+ def __str__ (self ):
1056+ config_dict = {
1057+ section : dict (self .items (section , raw = True ))
1058+ for section in self .sections ()
1059+ }
1060+ return f"<ConfigParser: { config_dict } >"
1061+
1062+ def __repr__ (self ):
1063+ params = {
1064+ "defaults" : self ._defaults if self ._defaults else None ,
1065+ "dict_type" : type (self ._dict ).__name__ ,
1066+ "allow_no_value" : self ._allow_no_value ,
1067+ "delimiters" : self ._delimiters ,
1068+ "strict" : self ._strict ,
1069+ "default_section" : self .default_section ,
1070+ "interpolation" : type (self ._interpolation ).__name__ ,
1071+ }
1072+ params = {k : v for k , v in params .items () if v is not None }
1073+ sections_count = len (self ._sections )
1074+ state = {
1075+ "loaded_sources" : self ._loaded_sources ,
1076+ "sections_count" : sections_count ,
1077+ "sections" : list (self ._sections )[:5 ], # limit to 5 section names for readability
1078+ }
1079+
1080+ if sections_count > 5 :
1081+ state ["sections_truncated" ] = f"...and { sections_count - 5 } more"
1082+
1083+ return (f"<{ self .__class__ .__name__ } ("
1084+ f"params={ params } , "
1085+ f"state={ state } )>" )
1086+
10511087 def _read (self , fp , fpname ):
10521088 """Parse a sectioned configuration file.
10531089
@@ -1068,6 +1104,7 @@ def _read(self, fp, fpname):
10681104 try :
10691105 ParsingError ._raise_all (self ._read_inner (fp , fpname ))
10701106 finally :
1107+ self ._loaded_sources .append (fpname )
10711108 self ._join_multiline_values ()
10721109
10731110 def _read_inner (self , fp , fpname ):
@@ -1218,11 +1255,14 @@ def _convert_to_boolean(self, value):
12181255
12191256 def _validate_key_contents (self , key ):
12201257 """Raises an InvalidWriteError for any keys containing
1221- delimiters or that match the section header pattern"""
1258+ delimiters or that begins with the section header pattern"""
12221259 if re .match (self .SECTCRE , key ):
1223- raise InvalidWriteError ("Cannot write keys matching section pattern" )
1224- if any (delim in key for delim in self ._delimiters ):
1225- raise InvalidWriteError ("Cannot write key that contains delimiters" )
1260+ raise InvalidWriteError (
1261+ f"Cannot write key { key } ; begins with section pattern" )
1262+ for delim in self ._delimiters :
1263+ if delim in key :
1264+ raise InvalidWriteError (
1265+ f"Cannot write key { key } ; contains delimiter { delim } " )
12261266
12271267 def _validate_value_types (self , * , section = "" , option = "" , value = "" ):
12281268 """Raises a TypeError for illegal non-string values.
0 commit comments