1717 __init__(defaults=None, dict_type=_default_dict, allow_no_value=False,
1818 delimiters=('=', ':'), comment_prefixes=('#', ';'),
1919 inline_comment_prefixes=None, strict=True,
20- empty_lines_in_values=True):
20+ empty_lines_in_values=True, default_section='DEFAULT',
21+ interpolation=<unset>, converters=<unset>):
2122 Create the parser. When `defaults' is given, it is initialized into the
2223 dictionary or intrinsic defaults. The keys must be strings, the values
2324 must be appropriate for %()s string interpolation.
4748 When `allow_no_value' is True (default: False), options without
4849 values are accepted; the value presented for these is None.
4950
51+ When `default_section' is given, the name of the special section is
52+ named accordingly. By default it is called ``"DEFAULT"`` but this can
53+ be customized to point to any other valid section name. Its current
54+ value can be retrieved using the ``parser_instance.default_section``
55+ attribute and may be modified at runtime.
56+
57+ When `interpolation` is given, it should be an Interpolation subclass
58+ instance. It will be used as the handler for option value
59+ pre-processing when using getters. RawConfigParser object s don't do
60+ any sort of interpolation, whereas ConfigParser uses an instance of
61+ BasicInterpolation. The library also provides a ``zc.buildbot``
62+ inspired ExtendedInterpolation implementation.
63+
64+ When `converters` is given, it should be a dictionary where each key
65+ represents the name of a type converter and each value is a callable
66+ implementing the conversion from string to the desired datatype. Every
67+ converter gets its corresponding get*() method on the parser object and
68+ section proxies.
69+
5070 sections()
5171 Return all the configuration section names, sans DEFAULT.
5272
129149
130150__all__ = ["NoSectionError" , "DuplicateOptionError" , "DuplicateSectionError" ,
131151 "NoOptionError" , "InterpolationError" , "InterpolationDepthError" ,
132- "InterpolationSyntaxError " , "ParsingError " ,
133- "MissingSectionHeaderError" ,
152+ "InterpolationMissingOptionError " , "InterpolationSyntaxError " ,
153+ "ParsingError" , " MissingSectionHeaderError" ,
134154 "ConfigParser" , "SafeConfigParser" , "RawConfigParser" ,
155+ "Interpolation" , "BasicInterpolation" , "ExtendedInterpolation" ,
156+ "LegacyInterpolation" , "SectionProxy" , "ConverterMapping" ,
135157 "DEFAULTSECT" , "MAX_INTERPOLATION_DEPTH" ]
136158
137159DEFAULTSECT = "DEFAULT"
@@ -580,11 +602,12 @@ def __init__(self, defaults=None, dict_type=_default_dict,
580602 comment_prefixes = ('#' , ';' ), inline_comment_prefixes = None ,
581603 strict = True , empty_lines_in_values = True ,
582604 default_section = DEFAULTSECT ,
583- interpolation = _UNSET ):
605+ interpolation = _UNSET , converters = _UNSET ):
584606
585607 self ._dict = dict_type
586608 self ._sections = self ._dict ()
587609 self ._defaults = self ._dict ()
610+ self ._converters = ConverterMapping (self )
588611 self ._proxies = self ._dict ()
589612 self ._proxies [default_section ] = SectionProxy (self , default_section )
590613 if defaults :
@@ -612,6 +635,8 @@ def __init__(self, defaults=None, dict_type=_default_dict,
612635 self ._interpolation = self ._DEFAULT_INTERPOLATION
613636 if self ._interpolation is None :
614637 self ._interpolation = Interpolation ()
638+ if converters is not _UNSET :
639+ self ._converters .update (converters )
615640
616641 def defaults (self ):
617642 return self ._defaults
@@ -775,36 +800,31 @@ def get(self, section, option, *, raw=False, vars=None, fallback=_UNSET):
775800 def _get (self , section , conv , option , ** kwargs ):
776801 return conv (self .get (section , option , ** kwargs ))
777802
778- def getint (self , section , option , * , raw = False , vars = None ,
779- fallback = _UNSET ):
803+ def _get_conv (self , section , option , conv , * , raw = False , vars = None ,
804+ fallback = _UNSET , ** kwargs ):
780805 try :
781- return self ._get (section , int , option , raw = raw , vars = vars )
806+ return self ._get (section , conv , option , raw = raw , vars = vars ,
807+ ** kwargs )
782808 except (NoSectionError , NoOptionError ):
783809 if fallback is _UNSET :
784810 raise
785- else :
786- return fallback
811+ return fallback
812+
813+ # getint, getfloat and getboolean provided directly for backwards compat
814+ def getint (self , section , option , * , raw = False , vars = None ,
815+ fallback = _UNSET , ** kwargs ):
816+ return self ._get_conv (section , option , int , raw = raw , vars = vars ,
817+ fallback = fallback , ** kwargs )
787818
788819 def getfloat (self , section , option , * , raw = False , vars = None ,
789- fallback = _UNSET ):
790- try :
791- return self ._get (section , float , option , raw = raw , vars = vars )
792- except (NoSectionError , NoOptionError ):
793- if fallback is _UNSET :
794- raise
795- else :
796- return fallback
820+ fallback = _UNSET , ** kwargs ):
821+ return self ._get_conv (section , option , float , raw = raw , vars = vars ,
822+ fallback = fallback , ** kwargs )
797823
798824 def getboolean (self , section , option , * , raw = False , vars = None ,
799- fallback = _UNSET ):
800- try :
801- return self ._get (section , self ._convert_to_boolean , option ,
802- raw = raw , vars = vars )
803- except (NoSectionError , NoOptionError ):
804- if fallback is _UNSET :
805- raise
806- else :
807- return fallback
825+ fallback = _UNSET , ** kwargs ):
826+ return self ._get_conv (section , option , self ._convert_to_boolean ,
827+ raw = raw , vars = vars , fallback = fallback , ** kwargs )
808828
809829 def items (self , section = _UNSET , raw = False , vars = None ):
810830 """Return a list of (name, value) tuples for each option in a section.
@@ -1154,6 +1174,10 @@ def _validate_value_types(self, *, section="", option="", value=""):
11541174 if not isinstance (value , str ):
11551175 raise TypeError ("option values must be strings" )
11561176
1177+ @property
1178+ def converters (self ):
1179+ return self ._converters
1180+
11571181
11581182class ConfigParser (RawConfigParser ):
11591183 """ConfigParser implementing interpolation."""
@@ -1194,6 +1218,10 @@ def __init__(self, parser, name):
11941218 """Creates a view on a section of the specified `name` in `parser`."""
11951219 self ._parser = parser
11961220 self ._name = name
1221+ for conv in parser .converters :
1222+ key = 'get' + conv
1223+ getter = functools .partial (self .get , _impl = getattr (parser , key ))
1224+ setattr (self , key , getter )
11971225
11981226 def __repr__ (self ):
11991227 return '<Section: {}>' .format (self ._name )
@@ -1227,22 +1255,6 @@ def _options(self):
12271255 else :
12281256 return self ._parser .defaults ()
12291257
1230- def get (self , option , fallback = None , * , raw = False , vars = None ):
1231- return self ._parser .get (self ._name , option , raw = raw , vars = vars ,
1232- fallback = fallback )
1233-
1234- def getint (self , option , fallback = None , * , raw = False , vars = None ):
1235- return self ._parser .getint (self ._name , option , raw = raw , vars = vars ,
1236- fallback = fallback )
1237-
1238- def getfloat (self , option , fallback = None , * , raw = False , vars = None ):
1239- return self ._parser .getfloat (self ._name , option , raw = raw , vars = vars ,
1240- fallback = fallback )
1241-
1242- def getboolean (self , option , fallback = None , * , raw = False , vars = None ):
1243- return self ._parser .getboolean (self ._name , option , raw = raw , vars = vars ,
1244- fallback = fallback )
1245-
12461258 @property
12471259 def parser (self ):
12481260 # The parser object of the proxy is read-only.
@@ -1252,3 +1264,77 @@ def parser(self):
12521264 def name (self ):
12531265 # The name of the section on a proxy is read-only.
12541266 return self ._name
1267+
1268+ def get (self , option , fallback = None , * , raw = False , vars = None ,
1269+ _impl = None , ** kwargs ):
1270+ """Get an option value.
1271+
1272+ Unless `fallback` is provided, `None` will be returned if the option
1273+ is not found.
1274+
1275+ """
1276+ # If `_impl` is provided, it should be a getter method on the parser
1277+ # object that provides the desired type conversion.
1278+ if not _impl :
1279+ _impl = self ._parser .get
1280+ return _impl (self ._name , option , raw = raw , vars = vars ,
1281+ fallback = fallback , ** kwargs )
1282+
1283+
1284+ class ConverterMapping (MutableMapping ):
1285+ """Enables reuse of get*() methods between the parser and section proxies.
1286+
1287+ If a parser class implements a getter directly, the value for the given
1288+ key will be ``None``. The presence of the converter name here enables
1289+ section proxies to find and use the implementation on the parser class.
1290+ """
1291+
1292+ GETTERCRE = re .compile (r"^get(?P<name>.+)$" )
1293+
1294+ def __init__ (self , parser ):
1295+ self ._parser = parser
1296+ self ._data = {}
1297+ for getter in dir (self ._parser ):
1298+ m = self .GETTERCRE .match (getter )
1299+ if not m or not callable (getattr (self ._parser , getter )):
1300+ continue
1301+ self ._data [m .group ('name' )] = None # See class docstring.
1302+
1303+ def __getitem__ (self , key ):
1304+ return self ._data [key ]
1305+
1306+ def __setitem__ (self , key , value ):
1307+ try :
1308+ k = 'get' + key
1309+ except TypeError :
1310+ raise ValueError ('Incompatible key: {} (type: {})'
1311+ '' .format (key , type (key )))
1312+ if k == 'get' :
1313+ raise ValueError ('Incompatible key: cannot use "" as a name' )
1314+ self ._data [key ] = value
1315+ func = functools .partial (self ._parser ._get_conv , conv = value )
1316+ func .converter = value
1317+ setattr (self ._parser , k , func )
1318+ for proxy in self ._parser .values ():
1319+ getter = functools .partial (proxy .get , _impl = func )
1320+ setattr (proxy , k , getter )
1321+
1322+ def __delitem__ (self , key ):
1323+ try :
1324+ k = 'get' + (key or None )
1325+ except TypeError :
1326+ raise KeyError (key )
1327+ del self ._data [key ]
1328+ for inst in itertools .chain ((self ._parser ,), self ._parser .values ()):
1329+ try :
1330+ delattr (inst , k )
1331+ except AttributeError :
1332+ # don't raise since the entry was present in _data, silently
1333+ # clean up
1334+ continue
1335+
1336+ def __iter__ (self ):
1337+ return iter (self ._data )
1338+
1339+ def __len__ (self ):
1340+ return len (self ._data )
0 commit comments