1*cda5da8dSAndroid Build Coastguard Worker"""Configuration file parser. 2*cda5da8dSAndroid Build Coastguard Worker 3*cda5da8dSAndroid Build Coastguard WorkerA configuration file consists of sections, lead by a "[section]" header, 4*cda5da8dSAndroid Build Coastguard Workerand followed by "name: value" entries, with continuations and such in 5*cda5da8dSAndroid Build Coastguard Workerthe style of RFC 822. 6*cda5da8dSAndroid Build Coastguard Worker 7*cda5da8dSAndroid Build Coastguard WorkerIntrinsic defaults can be specified by passing them into the 8*cda5da8dSAndroid Build Coastguard WorkerConfigParser constructor as a dictionary. 9*cda5da8dSAndroid Build Coastguard Worker 10*cda5da8dSAndroid Build Coastguard Workerclass: 11*cda5da8dSAndroid Build Coastguard Worker 12*cda5da8dSAndroid Build Coastguard WorkerConfigParser -- responsible for parsing a list of 13*cda5da8dSAndroid Build Coastguard Worker configuration files, and managing the parsed database. 14*cda5da8dSAndroid Build Coastguard Worker 15*cda5da8dSAndroid Build Coastguard Worker methods: 16*cda5da8dSAndroid Build Coastguard Worker 17*cda5da8dSAndroid Build Coastguard Worker __init__(defaults=None, dict_type=_default_dict, allow_no_value=False, 18*cda5da8dSAndroid Build Coastguard Worker delimiters=('=', ':'), comment_prefixes=('#', ';'), 19*cda5da8dSAndroid Build Coastguard Worker inline_comment_prefixes=None, strict=True, 20*cda5da8dSAndroid Build Coastguard Worker empty_lines_in_values=True, default_section='DEFAULT', 21*cda5da8dSAndroid Build Coastguard Worker interpolation=<unset>, converters=<unset>): 22*cda5da8dSAndroid Build Coastguard Worker 23*cda5da8dSAndroid Build Coastguard Worker Create the parser. When `defaults` is given, it is initialized into the 24*cda5da8dSAndroid Build Coastguard Worker dictionary or intrinsic defaults. The keys must be strings, the values 25*cda5da8dSAndroid Build Coastguard Worker must be appropriate for %()s string interpolation. 26*cda5da8dSAndroid Build Coastguard Worker 27*cda5da8dSAndroid Build Coastguard Worker When `dict_type` is given, it will be used to create the dictionary 28*cda5da8dSAndroid Build Coastguard Worker objects for the list of sections, for the options within a section, and 29*cda5da8dSAndroid Build Coastguard Worker for the default values. 30*cda5da8dSAndroid Build Coastguard Worker 31*cda5da8dSAndroid Build Coastguard Worker When `delimiters` is given, it will be used as the set of substrings 32*cda5da8dSAndroid Build Coastguard Worker that divide keys from values. 33*cda5da8dSAndroid Build Coastguard Worker 34*cda5da8dSAndroid Build Coastguard Worker When `comment_prefixes` is given, it will be used as the set of 35*cda5da8dSAndroid Build Coastguard Worker substrings that prefix comments in empty lines. Comments can be 36*cda5da8dSAndroid Build Coastguard Worker indented. 37*cda5da8dSAndroid Build Coastguard Worker 38*cda5da8dSAndroid Build Coastguard Worker When `inline_comment_prefixes` is given, it will be used as the set of 39*cda5da8dSAndroid Build Coastguard Worker substrings that prefix comments in non-empty lines. 40*cda5da8dSAndroid Build Coastguard Worker 41*cda5da8dSAndroid Build Coastguard Worker When `strict` is True, the parser won't allow for any section or option 42*cda5da8dSAndroid Build Coastguard Worker duplicates while reading from a single source (file, string or 43*cda5da8dSAndroid Build Coastguard Worker dictionary). Default is True. 44*cda5da8dSAndroid Build Coastguard Worker 45*cda5da8dSAndroid Build Coastguard Worker When `empty_lines_in_values` is False (default: True), each empty line 46*cda5da8dSAndroid Build Coastguard Worker marks the end of an option. Otherwise, internal empty lines of 47*cda5da8dSAndroid Build Coastguard Worker a multiline option are kept as part of the value. 48*cda5da8dSAndroid Build Coastguard Worker 49*cda5da8dSAndroid Build Coastguard Worker When `allow_no_value` is True (default: False), options without 50*cda5da8dSAndroid Build Coastguard Worker values are accepted; the value presented for these is None. 51*cda5da8dSAndroid Build Coastguard Worker 52*cda5da8dSAndroid Build Coastguard Worker When `default_section` is given, the name of the special section is 53*cda5da8dSAndroid Build Coastguard Worker named accordingly. By default it is called ``"DEFAULT"`` but this can 54*cda5da8dSAndroid Build Coastguard Worker be customized to point to any other valid section name. Its current 55*cda5da8dSAndroid Build Coastguard Worker value can be retrieved using the ``parser_instance.default_section`` 56*cda5da8dSAndroid Build Coastguard Worker attribute and may be modified at runtime. 57*cda5da8dSAndroid Build Coastguard Worker 58*cda5da8dSAndroid Build Coastguard Worker When `interpolation` is given, it should be an Interpolation subclass 59*cda5da8dSAndroid Build Coastguard Worker instance. It will be used as the handler for option value 60*cda5da8dSAndroid Build Coastguard Worker pre-processing when using getters. RawConfigParser objects don't do 61*cda5da8dSAndroid Build Coastguard Worker any sort of interpolation, whereas ConfigParser uses an instance of 62*cda5da8dSAndroid Build Coastguard Worker BasicInterpolation. The library also provides a ``zc.buildbot`` 63*cda5da8dSAndroid Build Coastguard Worker inspired ExtendedInterpolation implementation. 64*cda5da8dSAndroid Build Coastguard Worker 65*cda5da8dSAndroid Build Coastguard Worker When `converters` is given, it should be a dictionary where each key 66*cda5da8dSAndroid Build Coastguard Worker represents the name of a type converter and each value is a callable 67*cda5da8dSAndroid Build Coastguard Worker implementing the conversion from string to the desired datatype. Every 68*cda5da8dSAndroid Build Coastguard Worker converter gets its corresponding get*() method on the parser object and 69*cda5da8dSAndroid Build Coastguard Worker section proxies. 70*cda5da8dSAndroid Build Coastguard Worker 71*cda5da8dSAndroid Build Coastguard Worker sections() 72*cda5da8dSAndroid Build Coastguard Worker Return all the configuration section names, sans DEFAULT. 73*cda5da8dSAndroid Build Coastguard Worker 74*cda5da8dSAndroid Build Coastguard Worker has_section(section) 75*cda5da8dSAndroid Build Coastguard Worker Return whether the given section exists. 76*cda5da8dSAndroid Build Coastguard Worker 77*cda5da8dSAndroid Build Coastguard Worker has_option(section, option) 78*cda5da8dSAndroid Build Coastguard Worker Return whether the given option exists in the given section. 79*cda5da8dSAndroid Build Coastguard Worker 80*cda5da8dSAndroid Build Coastguard Worker options(section) 81*cda5da8dSAndroid Build Coastguard Worker Return list of configuration options for the named section. 82*cda5da8dSAndroid Build Coastguard Worker 83*cda5da8dSAndroid Build Coastguard Worker read(filenames, encoding=None) 84*cda5da8dSAndroid Build Coastguard Worker Read and parse the iterable of named configuration files, given by 85*cda5da8dSAndroid Build Coastguard Worker name. A single filename is also allowed. Non-existing files 86*cda5da8dSAndroid Build Coastguard Worker are ignored. Return list of successfully read files. 87*cda5da8dSAndroid Build Coastguard Worker 88*cda5da8dSAndroid Build Coastguard Worker read_file(f, filename=None) 89*cda5da8dSAndroid Build Coastguard Worker Read and parse one configuration file, given as a file object. 90*cda5da8dSAndroid Build Coastguard Worker The filename defaults to f.name; it is only used in error 91*cda5da8dSAndroid Build Coastguard Worker messages (if f has no `name` attribute, the string `<???>` is used). 92*cda5da8dSAndroid Build Coastguard Worker 93*cda5da8dSAndroid Build Coastguard Worker read_string(string) 94*cda5da8dSAndroid Build Coastguard Worker Read configuration from a given string. 95*cda5da8dSAndroid Build Coastguard Worker 96*cda5da8dSAndroid Build Coastguard Worker read_dict(dictionary) 97*cda5da8dSAndroid Build Coastguard Worker Read configuration from a dictionary. Keys are section names, 98*cda5da8dSAndroid Build Coastguard Worker values are dictionaries with keys and values that should be present 99*cda5da8dSAndroid Build Coastguard Worker in the section. If the used dictionary type preserves order, sections 100*cda5da8dSAndroid Build Coastguard Worker and their keys will be added in order. Values are automatically 101*cda5da8dSAndroid Build Coastguard Worker converted to strings. 102*cda5da8dSAndroid Build Coastguard Worker 103*cda5da8dSAndroid Build Coastguard Worker get(section, option, raw=False, vars=None, fallback=_UNSET) 104*cda5da8dSAndroid Build Coastguard Worker Return a string value for the named option. All % interpolations are 105*cda5da8dSAndroid Build Coastguard Worker expanded in the return values, based on the defaults passed into the 106*cda5da8dSAndroid Build Coastguard Worker constructor and the DEFAULT section. Additional substitutions may be 107*cda5da8dSAndroid Build Coastguard Worker provided using the `vars` argument, which must be a dictionary whose 108*cda5da8dSAndroid Build Coastguard Worker contents override any pre-existing defaults. If `option` is a key in 109*cda5da8dSAndroid Build Coastguard Worker `vars`, the value from `vars` is used. 110*cda5da8dSAndroid Build Coastguard Worker 111*cda5da8dSAndroid Build Coastguard Worker getint(section, options, raw=False, vars=None, fallback=_UNSET) 112*cda5da8dSAndroid Build Coastguard Worker Like get(), but convert value to an integer. 113*cda5da8dSAndroid Build Coastguard Worker 114*cda5da8dSAndroid Build Coastguard Worker getfloat(section, options, raw=False, vars=None, fallback=_UNSET) 115*cda5da8dSAndroid Build Coastguard Worker Like get(), but convert value to a float. 116*cda5da8dSAndroid Build Coastguard Worker 117*cda5da8dSAndroid Build Coastguard Worker getboolean(section, options, raw=False, vars=None, fallback=_UNSET) 118*cda5da8dSAndroid Build Coastguard Worker Like get(), but convert value to a boolean (currently case 119*cda5da8dSAndroid Build Coastguard Worker insensitively defined as 0, false, no, off for False, and 1, true, 120*cda5da8dSAndroid Build Coastguard Worker yes, on for True). Returns False or True. 121*cda5da8dSAndroid Build Coastguard Worker 122*cda5da8dSAndroid Build Coastguard Worker items(section=_UNSET, raw=False, vars=None) 123*cda5da8dSAndroid Build Coastguard Worker If section is given, return a list of tuples with (name, value) for 124*cda5da8dSAndroid Build Coastguard Worker each option in the section. Otherwise, return a list of tuples with 125*cda5da8dSAndroid Build Coastguard Worker (section_name, section_proxy) for each section, including DEFAULTSECT. 126*cda5da8dSAndroid Build Coastguard Worker 127*cda5da8dSAndroid Build Coastguard Worker remove_section(section) 128*cda5da8dSAndroid Build Coastguard Worker Remove the given file section and all its options. 129*cda5da8dSAndroid Build Coastguard Worker 130*cda5da8dSAndroid Build Coastguard Worker remove_option(section, option) 131*cda5da8dSAndroid Build Coastguard Worker Remove the given option from the given section. 132*cda5da8dSAndroid Build Coastguard Worker 133*cda5da8dSAndroid Build Coastguard Worker set(section, option, value) 134*cda5da8dSAndroid Build Coastguard Worker Set the given option. 135*cda5da8dSAndroid Build Coastguard Worker 136*cda5da8dSAndroid Build Coastguard Worker write(fp, space_around_delimiters=True) 137*cda5da8dSAndroid Build Coastguard Worker Write the configuration state in .ini format. If 138*cda5da8dSAndroid Build Coastguard Worker `space_around_delimiters` is True (the default), delimiters 139*cda5da8dSAndroid Build Coastguard Worker between keys and values are surrounded by spaces. 140*cda5da8dSAndroid Build Coastguard Worker""" 141*cda5da8dSAndroid Build Coastguard Worker 142*cda5da8dSAndroid Build Coastguard Workerfrom collections.abc import MutableMapping 143*cda5da8dSAndroid Build Coastguard Workerfrom collections import ChainMap as _ChainMap 144*cda5da8dSAndroid Build Coastguard Workerimport functools 145*cda5da8dSAndroid Build Coastguard Workerimport io 146*cda5da8dSAndroid Build Coastguard Workerimport itertools 147*cda5da8dSAndroid Build Coastguard Workerimport os 148*cda5da8dSAndroid Build Coastguard Workerimport re 149*cda5da8dSAndroid Build Coastguard Workerimport sys 150*cda5da8dSAndroid Build Coastguard Workerimport warnings 151*cda5da8dSAndroid Build Coastguard Worker 152*cda5da8dSAndroid Build Coastguard Worker__all__ = ["NoSectionError", "DuplicateOptionError", "DuplicateSectionError", 153*cda5da8dSAndroid Build Coastguard Worker "NoOptionError", "InterpolationError", "InterpolationDepthError", 154*cda5da8dSAndroid Build Coastguard Worker "InterpolationMissingOptionError", "InterpolationSyntaxError", 155*cda5da8dSAndroid Build Coastguard Worker "ParsingError", "MissingSectionHeaderError", 156*cda5da8dSAndroid Build Coastguard Worker "ConfigParser", "SafeConfigParser", "RawConfigParser", 157*cda5da8dSAndroid Build Coastguard Worker "Interpolation", "BasicInterpolation", "ExtendedInterpolation", 158*cda5da8dSAndroid Build Coastguard Worker "LegacyInterpolation", "SectionProxy", "ConverterMapping", 159*cda5da8dSAndroid Build Coastguard Worker "DEFAULTSECT", "MAX_INTERPOLATION_DEPTH"] 160*cda5da8dSAndroid Build Coastguard Worker 161*cda5da8dSAndroid Build Coastguard Worker_default_dict = dict 162*cda5da8dSAndroid Build Coastguard WorkerDEFAULTSECT = "DEFAULT" 163*cda5da8dSAndroid Build Coastguard Worker 164*cda5da8dSAndroid Build Coastguard WorkerMAX_INTERPOLATION_DEPTH = 10 165*cda5da8dSAndroid Build Coastguard Worker 166*cda5da8dSAndroid Build Coastguard Worker 167*cda5da8dSAndroid Build Coastguard Worker 168*cda5da8dSAndroid Build Coastguard Worker# exception classes 169*cda5da8dSAndroid Build Coastguard Workerclass Error(Exception): 170*cda5da8dSAndroid Build Coastguard Worker """Base class for ConfigParser exceptions.""" 171*cda5da8dSAndroid Build Coastguard Worker 172*cda5da8dSAndroid Build Coastguard Worker def __init__(self, msg=''): 173*cda5da8dSAndroid Build Coastguard Worker self.message = msg 174*cda5da8dSAndroid Build Coastguard Worker Exception.__init__(self, msg) 175*cda5da8dSAndroid Build Coastguard Worker 176*cda5da8dSAndroid Build Coastguard Worker def __repr__(self): 177*cda5da8dSAndroid Build Coastguard Worker return self.message 178*cda5da8dSAndroid Build Coastguard Worker 179*cda5da8dSAndroid Build Coastguard Worker __str__ = __repr__ 180*cda5da8dSAndroid Build Coastguard Worker 181*cda5da8dSAndroid Build Coastguard Worker 182*cda5da8dSAndroid Build Coastguard Workerclass NoSectionError(Error): 183*cda5da8dSAndroid Build Coastguard Worker """Raised when no section matches a requested option.""" 184*cda5da8dSAndroid Build Coastguard Worker 185*cda5da8dSAndroid Build Coastguard Worker def __init__(self, section): 186*cda5da8dSAndroid Build Coastguard Worker Error.__init__(self, 'No section: %r' % (section,)) 187*cda5da8dSAndroid Build Coastguard Worker self.section = section 188*cda5da8dSAndroid Build Coastguard Worker self.args = (section, ) 189*cda5da8dSAndroid Build Coastguard Worker 190*cda5da8dSAndroid Build Coastguard Worker 191*cda5da8dSAndroid Build Coastguard Workerclass DuplicateSectionError(Error): 192*cda5da8dSAndroid Build Coastguard Worker """Raised when a section is repeated in an input source. 193*cda5da8dSAndroid Build Coastguard Worker 194*cda5da8dSAndroid Build Coastguard Worker Possible repetitions that raise this exception are: multiple creation 195*cda5da8dSAndroid Build Coastguard Worker using the API or in strict parsers when a section is found more than once 196*cda5da8dSAndroid Build Coastguard Worker in a single input file, string or dictionary. 197*cda5da8dSAndroid Build Coastguard Worker """ 198*cda5da8dSAndroid Build Coastguard Worker 199*cda5da8dSAndroid Build Coastguard Worker def __init__(self, section, source=None, lineno=None): 200*cda5da8dSAndroid Build Coastguard Worker msg = [repr(section), " already exists"] 201*cda5da8dSAndroid Build Coastguard Worker if source is not None: 202*cda5da8dSAndroid Build Coastguard Worker message = ["While reading from ", repr(source)] 203*cda5da8dSAndroid Build Coastguard Worker if lineno is not None: 204*cda5da8dSAndroid Build Coastguard Worker message.append(" [line {0:2d}]".format(lineno)) 205*cda5da8dSAndroid Build Coastguard Worker message.append(": section ") 206*cda5da8dSAndroid Build Coastguard Worker message.extend(msg) 207*cda5da8dSAndroid Build Coastguard Worker msg = message 208*cda5da8dSAndroid Build Coastguard Worker else: 209*cda5da8dSAndroid Build Coastguard Worker msg.insert(0, "Section ") 210*cda5da8dSAndroid Build Coastguard Worker Error.__init__(self, "".join(msg)) 211*cda5da8dSAndroid Build Coastguard Worker self.section = section 212*cda5da8dSAndroid Build Coastguard Worker self.source = source 213*cda5da8dSAndroid Build Coastguard Worker self.lineno = lineno 214*cda5da8dSAndroid Build Coastguard Worker self.args = (section, source, lineno) 215*cda5da8dSAndroid Build Coastguard Worker 216*cda5da8dSAndroid Build Coastguard Worker 217*cda5da8dSAndroid Build Coastguard Workerclass DuplicateOptionError(Error): 218*cda5da8dSAndroid Build Coastguard Worker """Raised by strict parsers when an option is repeated in an input source. 219*cda5da8dSAndroid Build Coastguard Worker 220*cda5da8dSAndroid Build Coastguard Worker Current implementation raises this exception only when an option is found 221*cda5da8dSAndroid Build Coastguard Worker more than once in a single file, string or dictionary. 222*cda5da8dSAndroid Build Coastguard Worker """ 223*cda5da8dSAndroid Build Coastguard Worker 224*cda5da8dSAndroid Build Coastguard Worker def __init__(self, section, option, source=None, lineno=None): 225*cda5da8dSAndroid Build Coastguard Worker msg = [repr(option), " in section ", repr(section), 226*cda5da8dSAndroid Build Coastguard Worker " already exists"] 227*cda5da8dSAndroid Build Coastguard Worker if source is not None: 228*cda5da8dSAndroid Build Coastguard Worker message = ["While reading from ", repr(source)] 229*cda5da8dSAndroid Build Coastguard Worker if lineno is not None: 230*cda5da8dSAndroid Build Coastguard Worker message.append(" [line {0:2d}]".format(lineno)) 231*cda5da8dSAndroid Build Coastguard Worker message.append(": option ") 232*cda5da8dSAndroid Build Coastguard Worker message.extend(msg) 233*cda5da8dSAndroid Build Coastguard Worker msg = message 234*cda5da8dSAndroid Build Coastguard Worker else: 235*cda5da8dSAndroid Build Coastguard Worker msg.insert(0, "Option ") 236*cda5da8dSAndroid Build Coastguard Worker Error.__init__(self, "".join(msg)) 237*cda5da8dSAndroid Build Coastguard Worker self.section = section 238*cda5da8dSAndroid Build Coastguard Worker self.option = option 239*cda5da8dSAndroid Build Coastguard Worker self.source = source 240*cda5da8dSAndroid Build Coastguard Worker self.lineno = lineno 241*cda5da8dSAndroid Build Coastguard Worker self.args = (section, option, source, lineno) 242*cda5da8dSAndroid Build Coastguard Worker 243*cda5da8dSAndroid Build Coastguard Worker 244*cda5da8dSAndroid Build Coastguard Workerclass NoOptionError(Error): 245*cda5da8dSAndroid Build Coastguard Worker """A requested option was not found.""" 246*cda5da8dSAndroid Build Coastguard Worker 247*cda5da8dSAndroid Build Coastguard Worker def __init__(self, option, section): 248*cda5da8dSAndroid Build Coastguard Worker Error.__init__(self, "No option %r in section: %r" % 249*cda5da8dSAndroid Build Coastguard Worker (option, section)) 250*cda5da8dSAndroid Build Coastguard Worker self.option = option 251*cda5da8dSAndroid Build Coastguard Worker self.section = section 252*cda5da8dSAndroid Build Coastguard Worker self.args = (option, section) 253*cda5da8dSAndroid Build Coastguard Worker 254*cda5da8dSAndroid Build Coastguard Worker 255*cda5da8dSAndroid Build Coastguard Workerclass InterpolationError(Error): 256*cda5da8dSAndroid Build Coastguard Worker """Base class for interpolation-related exceptions.""" 257*cda5da8dSAndroid Build Coastguard Worker 258*cda5da8dSAndroid Build Coastguard Worker def __init__(self, option, section, msg): 259*cda5da8dSAndroid Build Coastguard Worker Error.__init__(self, msg) 260*cda5da8dSAndroid Build Coastguard Worker self.option = option 261*cda5da8dSAndroid Build Coastguard Worker self.section = section 262*cda5da8dSAndroid Build Coastguard Worker self.args = (option, section, msg) 263*cda5da8dSAndroid Build Coastguard Worker 264*cda5da8dSAndroid Build Coastguard Worker 265*cda5da8dSAndroid Build Coastguard Workerclass InterpolationMissingOptionError(InterpolationError): 266*cda5da8dSAndroid Build Coastguard Worker """A string substitution required a setting which was not available.""" 267*cda5da8dSAndroid Build Coastguard Worker 268*cda5da8dSAndroid Build Coastguard Worker def __init__(self, option, section, rawval, reference): 269*cda5da8dSAndroid Build Coastguard Worker msg = ("Bad value substitution: option {!r} in section {!r} contains " 270*cda5da8dSAndroid Build Coastguard Worker "an interpolation key {!r} which is not a valid option name. " 271*cda5da8dSAndroid Build Coastguard Worker "Raw value: {!r}".format(option, section, reference, rawval)) 272*cda5da8dSAndroid Build Coastguard Worker InterpolationError.__init__(self, option, section, msg) 273*cda5da8dSAndroid Build Coastguard Worker self.reference = reference 274*cda5da8dSAndroid Build Coastguard Worker self.args = (option, section, rawval, reference) 275*cda5da8dSAndroid Build Coastguard Worker 276*cda5da8dSAndroid Build Coastguard Worker 277*cda5da8dSAndroid Build Coastguard Workerclass InterpolationSyntaxError(InterpolationError): 278*cda5da8dSAndroid Build Coastguard Worker """Raised when the source text contains invalid syntax. 279*cda5da8dSAndroid Build Coastguard Worker 280*cda5da8dSAndroid Build Coastguard Worker Current implementation raises this exception when the source text into 281*cda5da8dSAndroid Build Coastguard Worker which substitutions are made does not conform to the required syntax. 282*cda5da8dSAndroid Build Coastguard Worker """ 283*cda5da8dSAndroid Build Coastguard Worker 284*cda5da8dSAndroid Build Coastguard Worker 285*cda5da8dSAndroid Build Coastguard Workerclass InterpolationDepthError(InterpolationError): 286*cda5da8dSAndroid Build Coastguard Worker """Raised when substitutions are nested too deeply.""" 287*cda5da8dSAndroid Build Coastguard Worker 288*cda5da8dSAndroid Build Coastguard Worker def __init__(self, option, section, rawval): 289*cda5da8dSAndroid Build Coastguard Worker msg = ("Recursion limit exceeded in value substitution: option {!r} " 290*cda5da8dSAndroid Build Coastguard Worker "in section {!r} contains an interpolation key which " 291*cda5da8dSAndroid Build Coastguard Worker "cannot be substituted in {} steps. Raw value: {!r}" 292*cda5da8dSAndroid Build Coastguard Worker "".format(option, section, MAX_INTERPOLATION_DEPTH, 293*cda5da8dSAndroid Build Coastguard Worker rawval)) 294*cda5da8dSAndroid Build Coastguard Worker InterpolationError.__init__(self, option, section, msg) 295*cda5da8dSAndroid Build Coastguard Worker self.args = (option, section, rawval) 296*cda5da8dSAndroid Build Coastguard Worker 297*cda5da8dSAndroid Build Coastguard Worker 298*cda5da8dSAndroid Build Coastguard Workerclass ParsingError(Error): 299*cda5da8dSAndroid Build Coastguard Worker """Raised when a configuration file does not follow legal syntax.""" 300*cda5da8dSAndroid Build Coastguard Worker 301*cda5da8dSAndroid Build Coastguard Worker def __init__(self, source=None, filename=None): 302*cda5da8dSAndroid Build Coastguard Worker # Exactly one of `source'/`filename' arguments has to be given. 303*cda5da8dSAndroid Build Coastguard Worker # `filename' kept for compatibility. 304*cda5da8dSAndroid Build Coastguard Worker if filename and source: 305*cda5da8dSAndroid Build Coastguard Worker raise ValueError("Cannot specify both `filename' and `source'. " 306*cda5da8dSAndroid Build Coastguard Worker "Use `source'.") 307*cda5da8dSAndroid Build Coastguard Worker elif not filename and not source: 308*cda5da8dSAndroid Build Coastguard Worker raise ValueError("Required argument `source' not given.") 309*cda5da8dSAndroid Build Coastguard Worker elif filename: 310*cda5da8dSAndroid Build Coastguard Worker source = filename 311*cda5da8dSAndroid Build Coastguard Worker Error.__init__(self, 'Source contains parsing errors: %r' % source) 312*cda5da8dSAndroid Build Coastguard Worker self.source = source 313*cda5da8dSAndroid Build Coastguard Worker self.errors = [] 314*cda5da8dSAndroid Build Coastguard Worker self.args = (source, ) 315*cda5da8dSAndroid Build Coastguard Worker 316*cda5da8dSAndroid Build Coastguard Worker @property 317*cda5da8dSAndroid Build Coastguard Worker def filename(self): 318*cda5da8dSAndroid Build Coastguard Worker """Deprecated, use `source'.""" 319*cda5da8dSAndroid Build Coastguard Worker warnings.warn( 320*cda5da8dSAndroid Build Coastguard Worker "The 'filename' attribute will be removed in Python 3.12. " 321*cda5da8dSAndroid Build Coastguard Worker "Use 'source' instead.", 322*cda5da8dSAndroid Build Coastguard Worker DeprecationWarning, stacklevel=2 323*cda5da8dSAndroid Build Coastguard Worker ) 324*cda5da8dSAndroid Build Coastguard Worker return self.source 325*cda5da8dSAndroid Build Coastguard Worker 326*cda5da8dSAndroid Build Coastguard Worker @filename.setter 327*cda5da8dSAndroid Build Coastguard Worker def filename(self, value): 328*cda5da8dSAndroid Build Coastguard Worker """Deprecated, user `source'.""" 329*cda5da8dSAndroid Build Coastguard Worker warnings.warn( 330*cda5da8dSAndroid Build Coastguard Worker "The 'filename' attribute will be removed in Python 3.12. " 331*cda5da8dSAndroid Build Coastguard Worker "Use 'source' instead.", 332*cda5da8dSAndroid Build Coastguard Worker DeprecationWarning, stacklevel=2 333*cda5da8dSAndroid Build Coastguard Worker ) 334*cda5da8dSAndroid Build Coastguard Worker self.source = value 335*cda5da8dSAndroid Build Coastguard Worker 336*cda5da8dSAndroid Build Coastguard Worker def append(self, lineno, line): 337*cda5da8dSAndroid Build Coastguard Worker self.errors.append((lineno, line)) 338*cda5da8dSAndroid Build Coastguard Worker self.message += '\n\t[line %2d]: %s' % (lineno, line) 339*cda5da8dSAndroid Build Coastguard Worker 340*cda5da8dSAndroid Build Coastguard Worker 341*cda5da8dSAndroid Build Coastguard Workerclass MissingSectionHeaderError(ParsingError): 342*cda5da8dSAndroid Build Coastguard Worker """Raised when a key-value pair is found before any section header.""" 343*cda5da8dSAndroid Build Coastguard Worker 344*cda5da8dSAndroid Build Coastguard Worker def __init__(self, filename, lineno, line): 345*cda5da8dSAndroid Build Coastguard Worker Error.__init__( 346*cda5da8dSAndroid Build Coastguard Worker self, 347*cda5da8dSAndroid Build Coastguard Worker 'File contains no section headers.\nfile: %r, line: %d\n%r' % 348*cda5da8dSAndroid Build Coastguard Worker (filename, lineno, line)) 349*cda5da8dSAndroid Build Coastguard Worker self.source = filename 350*cda5da8dSAndroid Build Coastguard Worker self.lineno = lineno 351*cda5da8dSAndroid Build Coastguard Worker self.line = line 352*cda5da8dSAndroid Build Coastguard Worker self.args = (filename, lineno, line) 353*cda5da8dSAndroid Build Coastguard Worker 354*cda5da8dSAndroid Build Coastguard Worker 355*cda5da8dSAndroid Build Coastguard Worker# Used in parser getters to indicate the default behaviour when a specific 356*cda5da8dSAndroid Build Coastguard Worker# option is not found it to raise an exception. Created to enable `None` as 357*cda5da8dSAndroid Build Coastguard Worker# a valid fallback value. 358*cda5da8dSAndroid Build Coastguard Worker_UNSET = object() 359*cda5da8dSAndroid Build Coastguard Worker 360*cda5da8dSAndroid Build Coastguard Worker 361*cda5da8dSAndroid Build Coastguard Workerclass Interpolation: 362*cda5da8dSAndroid Build Coastguard Worker """Dummy interpolation that passes the value through with no changes.""" 363*cda5da8dSAndroid Build Coastguard Worker 364*cda5da8dSAndroid Build Coastguard Worker def before_get(self, parser, section, option, value, defaults): 365*cda5da8dSAndroid Build Coastguard Worker return value 366*cda5da8dSAndroid Build Coastguard Worker 367*cda5da8dSAndroid Build Coastguard Worker def before_set(self, parser, section, option, value): 368*cda5da8dSAndroid Build Coastguard Worker return value 369*cda5da8dSAndroid Build Coastguard Worker 370*cda5da8dSAndroid Build Coastguard Worker def before_read(self, parser, section, option, value): 371*cda5da8dSAndroid Build Coastguard Worker return value 372*cda5da8dSAndroid Build Coastguard Worker 373*cda5da8dSAndroid Build Coastguard Worker def before_write(self, parser, section, option, value): 374*cda5da8dSAndroid Build Coastguard Worker return value 375*cda5da8dSAndroid Build Coastguard Worker 376*cda5da8dSAndroid Build Coastguard Worker 377*cda5da8dSAndroid Build Coastguard Workerclass BasicInterpolation(Interpolation): 378*cda5da8dSAndroid Build Coastguard Worker """Interpolation as implemented in the classic ConfigParser. 379*cda5da8dSAndroid Build Coastguard Worker 380*cda5da8dSAndroid Build Coastguard Worker The option values can contain format strings which refer to other values in 381*cda5da8dSAndroid Build Coastguard Worker the same section, or values in the special default section. 382*cda5da8dSAndroid Build Coastguard Worker 383*cda5da8dSAndroid Build Coastguard Worker For example: 384*cda5da8dSAndroid Build Coastguard Worker 385*cda5da8dSAndroid Build Coastguard Worker something: %(dir)s/whatever 386*cda5da8dSAndroid Build Coastguard Worker 387*cda5da8dSAndroid Build Coastguard Worker would resolve the "%(dir)s" to the value of dir. All reference 388*cda5da8dSAndroid Build Coastguard Worker expansions are done late, on demand. If a user needs to use a bare % in 389*cda5da8dSAndroid Build Coastguard Worker a configuration file, she can escape it by writing %%. Other % usage 390*cda5da8dSAndroid Build Coastguard Worker is considered a user error and raises `InterpolationSyntaxError`.""" 391*cda5da8dSAndroid Build Coastguard Worker 392*cda5da8dSAndroid Build Coastguard Worker _KEYCRE = re.compile(r"%\(([^)]+)\)s") 393*cda5da8dSAndroid Build Coastguard Worker 394*cda5da8dSAndroid Build Coastguard Worker def before_get(self, parser, section, option, value, defaults): 395*cda5da8dSAndroid Build Coastguard Worker L = [] 396*cda5da8dSAndroid Build Coastguard Worker self._interpolate_some(parser, option, L, value, section, defaults, 1) 397*cda5da8dSAndroid Build Coastguard Worker return ''.join(L) 398*cda5da8dSAndroid Build Coastguard Worker 399*cda5da8dSAndroid Build Coastguard Worker def before_set(self, parser, section, option, value): 400*cda5da8dSAndroid Build Coastguard Worker tmp_value = value.replace('%%', '') # escaped percent signs 401*cda5da8dSAndroid Build Coastguard Worker tmp_value = self._KEYCRE.sub('', tmp_value) # valid syntax 402*cda5da8dSAndroid Build Coastguard Worker if '%' in tmp_value: 403*cda5da8dSAndroid Build Coastguard Worker raise ValueError("invalid interpolation syntax in %r at " 404*cda5da8dSAndroid Build Coastguard Worker "position %d" % (value, tmp_value.find('%'))) 405*cda5da8dSAndroid Build Coastguard Worker return value 406*cda5da8dSAndroid Build Coastguard Worker 407*cda5da8dSAndroid Build Coastguard Worker def _interpolate_some(self, parser, option, accum, rest, section, map, 408*cda5da8dSAndroid Build Coastguard Worker depth): 409*cda5da8dSAndroid Build Coastguard Worker rawval = parser.get(section, option, raw=True, fallback=rest) 410*cda5da8dSAndroid Build Coastguard Worker if depth > MAX_INTERPOLATION_DEPTH: 411*cda5da8dSAndroid Build Coastguard Worker raise InterpolationDepthError(option, section, rawval) 412*cda5da8dSAndroid Build Coastguard Worker while rest: 413*cda5da8dSAndroid Build Coastguard Worker p = rest.find("%") 414*cda5da8dSAndroid Build Coastguard Worker if p < 0: 415*cda5da8dSAndroid Build Coastguard Worker accum.append(rest) 416*cda5da8dSAndroid Build Coastguard Worker return 417*cda5da8dSAndroid Build Coastguard Worker if p > 0: 418*cda5da8dSAndroid Build Coastguard Worker accum.append(rest[:p]) 419*cda5da8dSAndroid Build Coastguard Worker rest = rest[p:] 420*cda5da8dSAndroid Build Coastguard Worker # p is no longer used 421*cda5da8dSAndroid Build Coastguard Worker c = rest[1:2] 422*cda5da8dSAndroid Build Coastguard Worker if c == "%": 423*cda5da8dSAndroid Build Coastguard Worker accum.append("%") 424*cda5da8dSAndroid Build Coastguard Worker rest = rest[2:] 425*cda5da8dSAndroid Build Coastguard Worker elif c == "(": 426*cda5da8dSAndroid Build Coastguard Worker m = self._KEYCRE.match(rest) 427*cda5da8dSAndroid Build Coastguard Worker if m is None: 428*cda5da8dSAndroid Build Coastguard Worker raise InterpolationSyntaxError(option, section, 429*cda5da8dSAndroid Build Coastguard Worker "bad interpolation variable reference %r" % rest) 430*cda5da8dSAndroid Build Coastguard Worker var = parser.optionxform(m.group(1)) 431*cda5da8dSAndroid Build Coastguard Worker rest = rest[m.end():] 432*cda5da8dSAndroid Build Coastguard Worker try: 433*cda5da8dSAndroid Build Coastguard Worker v = map[var] 434*cda5da8dSAndroid Build Coastguard Worker except KeyError: 435*cda5da8dSAndroid Build Coastguard Worker raise InterpolationMissingOptionError( 436*cda5da8dSAndroid Build Coastguard Worker option, section, rawval, var) from None 437*cda5da8dSAndroid Build Coastguard Worker if "%" in v: 438*cda5da8dSAndroid Build Coastguard Worker self._interpolate_some(parser, option, accum, v, 439*cda5da8dSAndroid Build Coastguard Worker section, map, depth + 1) 440*cda5da8dSAndroid Build Coastguard Worker else: 441*cda5da8dSAndroid Build Coastguard Worker accum.append(v) 442*cda5da8dSAndroid Build Coastguard Worker else: 443*cda5da8dSAndroid Build Coastguard Worker raise InterpolationSyntaxError( 444*cda5da8dSAndroid Build Coastguard Worker option, section, 445*cda5da8dSAndroid Build Coastguard Worker "'%%' must be followed by '%%' or '(', " 446*cda5da8dSAndroid Build Coastguard Worker "found: %r" % (rest,)) 447*cda5da8dSAndroid Build Coastguard Worker 448*cda5da8dSAndroid Build Coastguard Worker 449*cda5da8dSAndroid Build Coastguard Workerclass ExtendedInterpolation(Interpolation): 450*cda5da8dSAndroid Build Coastguard Worker """Advanced variant of interpolation, supports the syntax used by 451*cda5da8dSAndroid Build Coastguard Worker `zc.buildout`. Enables interpolation between sections.""" 452*cda5da8dSAndroid Build Coastguard Worker 453*cda5da8dSAndroid Build Coastguard Worker _KEYCRE = re.compile(r"\$\{([^}]+)\}") 454*cda5da8dSAndroid Build Coastguard Worker 455*cda5da8dSAndroid Build Coastguard Worker def before_get(self, parser, section, option, value, defaults): 456*cda5da8dSAndroid Build Coastguard Worker L = [] 457*cda5da8dSAndroid Build Coastguard Worker self._interpolate_some(parser, option, L, value, section, defaults, 1) 458*cda5da8dSAndroid Build Coastguard Worker return ''.join(L) 459*cda5da8dSAndroid Build Coastguard Worker 460*cda5da8dSAndroid Build Coastguard Worker def before_set(self, parser, section, option, value): 461*cda5da8dSAndroid Build Coastguard Worker tmp_value = value.replace('$$', '') # escaped dollar signs 462*cda5da8dSAndroid Build Coastguard Worker tmp_value = self._KEYCRE.sub('', tmp_value) # valid syntax 463*cda5da8dSAndroid Build Coastguard Worker if '$' in tmp_value: 464*cda5da8dSAndroid Build Coastguard Worker raise ValueError("invalid interpolation syntax in %r at " 465*cda5da8dSAndroid Build Coastguard Worker "position %d" % (value, tmp_value.find('$'))) 466*cda5da8dSAndroid Build Coastguard Worker return value 467*cda5da8dSAndroid Build Coastguard Worker 468*cda5da8dSAndroid Build Coastguard Worker def _interpolate_some(self, parser, option, accum, rest, section, map, 469*cda5da8dSAndroid Build Coastguard Worker depth): 470*cda5da8dSAndroid Build Coastguard Worker rawval = parser.get(section, option, raw=True, fallback=rest) 471*cda5da8dSAndroid Build Coastguard Worker if depth > MAX_INTERPOLATION_DEPTH: 472*cda5da8dSAndroid Build Coastguard Worker raise InterpolationDepthError(option, section, rawval) 473*cda5da8dSAndroid Build Coastguard Worker while rest: 474*cda5da8dSAndroid Build Coastguard Worker p = rest.find("$") 475*cda5da8dSAndroid Build Coastguard Worker if p < 0: 476*cda5da8dSAndroid Build Coastguard Worker accum.append(rest) 477*cda5da8dSAndroid Build Coastguard Worker return 478*cda5da8dSAndroid Build Coastguard Worker if p > 0: 479*cda5da8dSAndroid Build Coastguard Worker accum.append(rest[:p]) 480*cda5da8dSAndroid Build Coastguard Worker rest = rest[p:] 481*cda5da8dSAndroid Build Coastguard Worker # p is no longer used 482*cda5da8dSAndroid Build Coastguard Worker c = rest[1:2] 483*cda5da8dSAndroid Build Coastguard Worker if c == "$": 484*cda5da8dSAndroid Build Coastguard Worker accum.append("$") 485*cda5da8dSAndroid Build Coastguard Worker rest = rest[2:] 486*cda5da8dSAndroid Build Coastguard Worker elif c == "{": 487*cda5da8dSAndroid Build Coastguard Worker m = self._KEYCRE.match(rest) 488*cda5da8dSAndroid Build Coastguard Worker if m is None: 489*cda5da8dSAndroid Build Coastguard Worker raise InterpolationSyntaxError(option, section, 490*cda5da8dSAndroid Build Coastguard Worker "bad interpolation variable reference %r" % rest) 491*cda5da8dSAndroid Build Coastguard Worker path = m.group(1).split(':') 492*cda5da8dSAndroid Build Coastguard Worker rest = rest[m.end():] 493*cda5da8dSAndroid Build Coastguard Worker sect = section 494*cda5da8dSAndroid Build Coastguard Worker opt = option 495*cda5da8dSAndroid Build Coastguard Worker try: 496*cda5da8dSAndroid Build Coastguard Worker if len(path) == 1: 497*cda5da8dSAndroid Build Coastguard Worker opt = parser.optionxform(path[0]) 498*cda5da8dSAndroid Build Coastguard Worker v = map[opt] 499*cda5da8dSAndroid Build Coastguard Worker elif len(path) == 2: 500*cda5da8dSAndroid Build Coastguard Worker sect = path[0] 501*cda5da8dSAndroid Build Coastguard Worker opt = parser.optionxform(path[1]) 502*cda5da8dSAndroid Build Coastguard Worker v = parser.get(sect, opt, raw=True) 503*cda5da8dSAndroid Build Coastguard Worker else: 504*cda5da8dSAndroid Build Coastguard Worker raise InterpolationSyntaxError( 505*cda5da8dSAndroid Build Coastguard Worker option, section, 506*cda5da8dSAndroid Build Coastguard Worker "More than one ':' found: %r" % (rest,)) 507*cda5da8dSAndroid Build Coastguard Worker except (KeyError, NoSectionError, NoOptionError): 508*cda5da8dSAndroid Build Coastguard Worker raise InterpolationMissingOptionError( 509*cda5da8dSAndroid Build Coastguard Worker option, section, rawval, ":".join(path)) from None 510*cda5da8dSAndroid Build Coastguard Worker if "$" in v: 511*cda5da8dSAndroid Build Coastguard Worker self._interpolate_some(parser, opt, accum, v, sect, 512*cda5da8dSAndroid Build Coastguard Worker dict(parser.items(sect, raw=True)), 513*cda5da8dSAndroid Build Coastguard Worker depth + 1) 514*cda5da8dSAndroid Build Coastguard Worker else: 515*cda5da8dSAndroid Build Coastguard Worker accum.append(v) 516*cda5da8dSAndroid Build Coastguard Worker else: 517*cda5da8dSAndroid Build Coastguard Worker raise InterpolationSyntaxError( 518*cda5da8dSAndroid Build Coastguard Worker option, section, 519*cda5da8dSAndroid Build Coastguard Worker "'$' must be followed by '$' or '{', " 520*cda5da8dSAndroid Build Coastguard Worker "found: %r" % (rest,)) 521*cda5da8dSAndroid Build Coastguard Worker 522*cda5da8dSAndroid Build Coastguard Worker 523*cda5da8dSAndroid Build Coastguard Workerclass LegacyInterpolation(Interpolation): 524*cda5da8dSAndroid Build Coastguard Worker """Deprecated interpolation used in old versions of ConfigParser. 525*cda5da8dSAndroid Build Coastguard Worker Use BasicInterpolation or ExtendedInterpolation instead.""" 526*cda5da8dSAndroid Build Coastguard Worker 527*cda5da8dSAndroid Build Coastguard Worker _KEYCRE = re.compile(r"%\(([^)]*)\)s|.") 528*cda5da8dSAndroid Build Coastguard Worker 529*cda5da8dSAndroid Build Coastguard Worker def __init__(self, *args, **kwargs): 530*cda5da8dSAndroid Build Coastguard Worker super().__init__(*args, **kwargs) 531*cda5da8dSAndroid Build Coastguard Worker warnings.warn( 532*cda5da8dSAndroid Build Coastguard Worker "LegacyInterpolation has been deprecated since Python 3.2 " 533*cda5da8dSAndroid Build Coastguard Worker "and will be removed from the configparser module in Python 3.13. " 534*cda5da8dSAndroid Build Coastguard Worker "Use BasicInterpolation or ExtendedInterpolation instead.", 535*cda5da8dSAndroid Build Coastguard Worker DeprecationWarning, stacklevel=2 536*cda5da8dSAndroid Build Coastguard Worker ) 537*cda5da8dSAndroid Build Coastguard Worker 538*cda5da8dSAndroid Build Coastguard Worker def before_get(self, parser, section, option, value, vars): 539*cda5da8dSAndroid Build Coastguard Worker rawval = value 540*cda5da8dSAndroid Build Coastguard Worker depth = MAX_INTERPOLATION_DEPTH 541*cda5da8dSAndroid Build Coastguard Worker while depth: # Loop through this until it's done 542*cda5da8dSAndroid Build Coastguard Worker depth -= 1 543*cda5da8dSAndroid Build Coastguard Worker if value and "%(" in value: 544*cda5da8dSAndroid Build Coastguard Worker replace = functools.partial(self._interpolation_replace, 545*cda5da8dSAndroid Build Coastguard Worker parser=parser) 546*cda5da8dSAndroid Build Coastguard Worker value = self._KEYCRE.sub(replace, value) 547*cda5da8dSAndroid Build Coastguard Worker try: 548*cda5da8dSAndroid Build Coastguard Worker value = value % vars 549*cda5da8dSAndroid Build Coastguard Worker except KeyError as e: 550*cda5da8dSAndroid Build Coastguard Worker raise InterpolationMissingOptionError( 551*cda5da8dSAndroid Build Coastguard Worker option, section, rawval, e.args[0]) from None 552*cda5da8dSAndroid Build Coastguard Worker else: 553*cda5da8dSAndroid Build Coastguard Worker break 554*cda5da8dSAndroid Build Coastguard Worker if value and "%(" in value: 555*cda5da8dSAndroid Build Coastguard Worker raise InterpolationDepthError(option, section, rawval) 556*cda5da8dSAndroid Build Coastguard Worker return value 557*cda5da8dSAndroid Build Coastguard Worker 558*cda5da8dSAndroid Build Coastguard Worker def before_set(self, parser, section, option, value): 559*cda5da8dSAndroid Build Coastguard Worker return value 560*cda5da8dSAndroid Build Coastguard Worker 561*cda5da8dSAndroid Build Coastguard Worker @staticmethod 562*cda5da8dSAndroid Build Coastguard Worker def _interpolation_replace(match, parser): 563*cda5da8dSAndroid Build Coastguard Worker s = match.group(1) 564*cda5da8dSAndroid Build Coastguard Worker if s is None: 565*cda5da8dSAndroid Build Coastguard Worker return match.group() 566*cda5da8dSAndroid Build Coastguard Worker else: 567*cda5da8dSAndroid Build Coastguard Worker return "%%(%s)s" % parser.optionxform(s) 568*cda5da8dSAndroid Build Coastguard Worker 569*cda5da8dSAndroid Build Coastguard Worker 570*cda5da8dSAndroid Build Coastguard Workerclass RawConfigParser(MutableMapping): 571*cda5da8dSAndroid Build Coastguard Worker """ConfigParser that does not do interpolation.""" 572*cda5da8dSAndroid Build Coastguard Worker 573*cda5da8dSAndroid Build Coastguard Worker # Regular expressions for parsing section headers and options 574*cda5da8dSAndroid Build Coastguard Worker _SECT_TMPL = r""" 575*cda5da8dSAndroid Build Coastguard Worker \[ # [ 576*cda5da8dSAndroid Build Coastguard Worker (?P<header>.+) # very permissive! 577*cda5da8dSAndroid Build Coastguard Worker \] # ] 578*cda5da8dSAndroid Build Coastguard Worker """ 579*cda5da8dSAndroid Build Coastguard Worker _OPT_TMPL = r""" 580*cda5da8dSAndroid Build Coastguard Worker (?P<option>.*?) # very permissive! 581*cda5da8dSAndroid Build Coastguard Worker \s*(?P<vi>{delim})\s* # any number of space/tab, 582*cda5da8dSAndroid Build Coastguard Worker # followed by any of the 583*cda5da8dSAndroid Build Coastguard Worker # allowed delimiters, 584*cda5da8dSAndroid Build Coastguard Worker # followed by any space/tab 585*cda5da8dSAndroid Build Coastguard Worker (?P<value>.*)$ # everything up to eol 586*cda5da8dSAndroid Build Coastguard Worker """ 587*cda5da8dSAndroid Build Coastguard Worker _OPT_NV_TMPL = r""" 588*cda5da8dSAndroid Build Coastguard Worker (?P<option>.*?) # very permissive! 589*cda5da8dSAndroid Build Coastguard Worker \s*(?: # any number of space/tab, 590*cda5da8dSAndroid Build Coastguard Worker (?P<vi>{delim})\s* # optionally followed by 591*cda5da8dSAndroid Build Coastguard Worker # any of the allowed 592*cda5da8dSAndroid Build Coastguard Worker # delimiters, followed by any 593*cda5da8dSAndroid Build Coastguard Worker # space/tab 594*cda5da8dSAndroid Build Coastguard Worker (?P<value>.*))?$ # everything up to eol 595*cda5da8dSAndroid Build Coastguard Worker """ 596*cda5da8dSAndroid Build Coastguard Worker # Interpolation algorithm to be used if the user does not specify another 597*cda5da8dSAndroid Build Coastguard Worker _DEFAULT_INTERPOLATION = Interpolation() 598*cda5da8dSAndroid Build Coastguard Worker # Compiled regular expression for matching sections 599*cda5da8dSAndroid Build Coastguard Worker SECTCRE = re.compile(_SECT_TMPL, re.VERBOSE) 600*cda5da8dSAndroid Build Coastguard Worker # Compiled regular expression for matching options with typical separators 601*cda5da8dSAndroid Build Coastguard Worker OPTCRE = re.compile(_OPT_TMPL.format(delim="=|:"), re.VERBOSE) 602*cda5da8dSAndroid Build Coastguard Worker # Compiled regular expression for matching options with optional values 603*cda5da8dSAndroid Build Coastguard Worker # delimited using typical separators 604*cda5da8dSAndroid Build Coastguard Worker OPTCRE_NV = re.compile(_OPT_NV_TMPL.format(delim="=|:"), re.VERBOSE) 605*cda5da8dSAndroid Build Coastguard Worker # Compiled regular expression for matching leading whitespace in a line 606*cda5da8dSAndroid Build Coastguard Worker NONSPACECRE = re.compile(r"\S") 607*cda5da8dSAndroid Build Coastguard Worker # Possible boolean values in the configuration. 608*cda5da8dSAndroid Build Coastguard Worker BOOLEAN_STATES = {'1': True, 'yes': True, 'true': True, 'on': True, 609*cda5da8dSAndroid Build Coastguard Worker '0': False, 'no': False, 'false': False, 'off': False} 610*cda5da8dSAndroid Build Coastguard Worker 611*cda5da8dSAndroid Build Coastguard Worker def __init__(self, defaults=None, dict_type=_default_dict, 612*cda5da8dSAndroid Build Coastguard Worker allow_no_value=False, *, delimiters=('=', ':'), 613*cda5da8dSAndroid Build Coastguard Worker comment_prefixes=('#', ';'), inline_comment_prefixes=None, 614*cda5da8dSAndroid Build Coastguard Worker strict=True, empty_lines_in_values=True, 615*cda5da8dSAndroid Build Coastguard Worker default_section=DEFAULTSECT, 616*cda5da8dSAndroid Build Coastguard Worker interpolation=_UNSET, converters=_UNSET): 617*cda5da8dSAndroid Build Coastguard Worker 618*cda5da8dSAndroid Build Coastguard Worker self._dict = dict_type 619*cda5da8dSAndroid Build Coastguard Worker self._sections = self._dict() 620*cda5da8dSAndroid Build Coastguard Worker self._defaults = self._dict() 621*cda5da8dSAndroid Build Coastguard Worker self._converters = ConverterMapping(self) 622*cda5da8dSAndroid Build Coastguard Worker self._proxies = self._dict() 623*cda5da8dSAndroid Build Coastguard Worker self._proxies[default_section] = SectionProxy(self, default_section) 624*cda5da8dSAndroid Build Coastguard Worker self._delimiters = tuple(delimiters) 625*cda5da8dSAndroid Build Coastguard Worker if delimiters == ('=', ':'): 626*cda5da8dSAndroid Build Coastguard Worker self._optcre = self.OPTCRE_NV if allow_no_value else self.OPTCRE 627*cda5da8dSAndroid Build Coastguard Worker else: 628*cda5da8dSAndroid Build Coastguard Worker d = "|".join(re.escape(d) for d in delimiters) 629*cda5da8dSAndroid Build Coastguard Worker if allow_no_value: 630*cda5da8dSAndroid Build Coastguard Worker self._optcre = re.compile(self._OPT_NV_TMPL.format(delim=d), 631*cda5da8dSAndroid Build Coastguard Worker re.VERBOSE) 632*cda5da8dSAndroid Build Coastguard Worker else: 633*cda5da8dSAndroid Build Coastguard Worker self._optcre = re.compile(self._OPT_TMPL.format(delim=d), 634*cda5da8dSAndroid Build Coastguard Worker re.VERBOSE) 635*cda5da8dSAndroid Build Coastguard Worker self._comment_prefixes = tuple(comment_prefixes or ()) 636*cda5da8dSAndroid Build Coastguard Worker self._inline_comment_prefixes = tuple(inline_comment_prefixes or ()) 637*cda5da8dSAndroid Build Coastguard Worker self._strict = strict 638*cda5da8dSAndroid Build Coastguard Worker self._allow_no_value = allow_no_value 639*cda5da8dSAndroid Build Coastguard Worker self._empty_lines_in_values = empty_lines_in_values 640*cda5da8dSAndroid Build Coastguard Worker self.default_section=default_section 641*cda5da8dSAndroid Build Coastguard Worker self._interpolation = interpolation 642*cda5da8dSAndroid Build Coastguard Worker if self._interpolation is _UNSET: 643*cda5da8dSAndroid Build Coastguard Worker self._interpolation = self._DEFAULT_INTERPOLATION 644*cda5da8dSAndroid Build Coastguard Worker if self._interpolation is None: 645*cda5da8dSAndroid Build Coastguard Worker self._interpolation = Interpolation() 646*cda5da8dSAndroid Build Coastguard Worker if not isinstance(self._interpolation, Interpolation): 647*cda5da8dSAndroid Build Coastguard Worker raise TypeError( 648*cda5da8dSAndroid Build Coastguard Worker f"interpolation= must be None or an instance of Interpolation;" 649*cda5da8dSAndroid Build Coastguard Worker f" got an object of type {type(self._interpolation)}" 650*cda5da8dSAndroid Build Coastguard Worker ) 651*cda5da8dSAndroid Build Coastguard Worker if converters is not _UNSET: 652*cda5da8dSAndroid Build Coastguard Worker self._converters.update(converters) 653*cda5da8dSAndroid Build Coastguard Worker if defaults: 654*cda5da8dSAndroid Build Coastguard Worker self._read_defaults(defaults) 655*cda5da8dSAndroid Build Coastguard Worker 656*cda5da8dSAndroid Build Coastguard Worker def defaults(self): 657*cda5da8dSAndroid Build Coastguard Worker return self._defaults 658*cda5da8dSAndroid Build Coastguard Worker 659*cda5da8dSAndroid Build Coastguard Worker def sections(self): 660*cda5da8dSAndroid Build Coastguard Worker """Return a list of section names, excluding [DEFAULT]""" 661*cda5da8dSAndroid Build Coastguard Worker # self._sections will never have [DEFAULT] in it 662*cda5da8dSAndroid Build Coastguard Worker return list(self._sections.keys()) 663*cda5da8dSAndroid Build Coastguard Worker 664*cda5da8dSAndroid Build Coastguard Worker def add_section(self, section): 665*cda5da8dSAndroid Build Coastguard Worker """Create a new section in the configuration. 666*cda5da8dSAndroid Build Coastguard Worker 667*cda5da8dSAndroid Build Coastguard Worker Raise DuplicateSectionError if a section by the specified name 668*cda5da8dSAndroid Build Coastguard Worker already exists. Raise ValueError if name is DEFAULT. 669*cda5da8dSAndroid Build Coastguard Worker """ 670*cda5da8dSAndroid Build Coastguard Worker if section == self.default_section: 671*cda5da8dSAndroid Build Coastguard Worker raise ValueError('Invalid section name: %r' % section) 672*cda5da8dSAndroid Build Coastguard Worker 673*cda5da8dSAndroid Build Coastguard Worker if section in self._sections: 674*cda5da8dSAndroid Build Coastguard Worker raise DuplicateSectionError(section) 675*cda5da8dSAndroid Build Coastguard Worker self._sections[section] = self._dict() 676*cda5da8dSAndroid Build Coastguard Worker self._proxies[section] = SectionProxy(self, section) 677*cda5da8dSAndroid Build Coastguard Worker 678*cda5da8dSAndroid Build Coastguard Worker def has_section(self, section): 679*cda5da8dSAndroid Build Coastguard Worker """Indicate whether the named section is present in the configuration. 680*cda5da8dSAndroid Build Coastguard Worker 681*cda5da8dSAndroid Build Coastguard Worker The DEFAULT section is not acknowledged. 682*cda5da8dSAndroid Build Coastguard Worker """ 683*cda5da8dSAndroid Build Coastguard Worker return section in self._sections 684*cda5da8dSAndroid Build Coastguard Worker 685*cda5da8dSAndroid Build Coastguard Worker def options(self, section): 686*cda5da8dSAndroid Build Coastguard Worker """Return a list of option names for the given section name.""" 687*cda5da8dSAndroid Build Coastguard Worker try: 688*cda5da8dSAndroid Build Coastguard Worker opts = self._sections[section].copy() 689*cda5da8dSAndroid Build Coastguard Worker except KeyError: 690*cda5da8dSAndroid Build Coastguard Worker raise NoSectionError(section) from None 691*cda5da8dSAndroid Build Coastguard Worker opts.update(self._defaults) 692*cda5da8dSAndroid Build Coastguard Worker return list(opts.keys()) 693*cda5da8dSAndroid Build Coastguard Worker 694*cda5da8dSAndroid Build Coastguard Worker def read(self, filenames, encoding=None): 695*cda5da8dSAndroid Build Coastguard Worker """Read and parse a filename or an iterable of filenames. 696*cda5da8dSAndroid Build Coastguard Worker 697*cda5da8dSAndroid Build Coastguard Worker Files that cannot be opened are silently ignored; this is 698*cda5da8dSAndroid Build Coastguard Worker designed so that you can specify an iterable of potential 699*cda5da8dSAndroid Build Coastguard Worker configuration file locations (e.g. current directory, user's 700*cda5da8dSAndroid Build Coastguard Worker home directory, systemwide directory), and all existing 701*cda5da8dSAndroid Build Coastguard Worker configuration files in the iterable will be read. A single 702*cda5da8dSAndroid Build Coastguard Worker filename may also be given. 703*cda5da8dSAndroid Build Coastguard Worker 704*cda5da8dSAndroid Build Coastguard Worker Return list of successfully read files. 705*cda5da8dSAndroid Build Coastguard Worker """ 706*cda5da8dSAndroid Build Coastguard Worker if isinstance(filenames, (str, bytes, os.PathLike)): 707*cda5da8dSAndroid Build Coastguard Worker filenames = [filenames] 708*cda5da8dSAndroid Build Coastguard Worker encoding = io.text_encoding(encoding) 709*cda5da8dSAndroid Build Coastguard Worker read_ok = [] 710*cda5da8dSAndroid Build Coastguard Worker for filename in filenames: 711*cda5da8dSAndroid Build Coastguard Worker try: 712*cda5da8dSAndroid Build Coastguard Worker with open(filename, encoding=encoding) as fp: 713*cda5da8dSAndroid Build Coastguard Worker self._read(fp, filename) 714*cda5da8dSAndroid Build Coastguard Worker except OSError: 715*cda5da8dSAndroid Build Coastguard Worker continue 716*cda5da8dSAndroid Build Coastguard Worker if isinstance(filename, os.PathLike): 717*cda5da8dSAndroid Build Coastguard Worker filename = os.fspath(filename) 718*cda5da8dSAndroid Build Coastguard Worker read_ok.append(filename) 719*cda5da8dSAndroid Build Coastguard Worker return read_ok 720*cda5da8dSAndroid Build Coastguard Worker 721*cda5da8dSAndroid Build Coastguard Worker def read_file(self, f, source=None): 722*cda5da8dSAndroid Build Coastguard Worker """Like read() but the argument must be a file-like object. 723*cda5da8dSAndroid Build Coastguard Worker 724*cda5da8dSAndroid Build Coastguard Worker The `f` argument must be iterable, returning one line at a time. 725*cda5da8dSAndroid Build Coastguard Worker Optional second argument is the `source` specifying the name of the 726*cda5da8dSAndroid Build Coastguard Worker file being read. If not given, it is taken from f.name. If `f` has no 727*cda5da8dSAndroid Build Coastguard Worker `name` attribute, `<???>` is used. 728*cda5da8dSAndroid Build Coastguard Worker """ 729*cda5da8dSAndroid Build Coastguard Worker if source is None: 730*cda5da8dSAndroid Build Coastguard Worker try: 731*cda5da8dSAndroid Build Coastguard Worker source = f.name 732*cda5da8dSAndroid Build Coastguard Worker except AttributeError: 733*cda5da8dSAndroid Build Coastguard Worker source = '<???>' 734*cda5da8dSAndroid Build Coastguard Worker self._read(f, source) 735*cda5da8dSAndroid Build Coastguard Worker 736*cda5da8dSAndroid Build Coastguard Worker def read_string(self, string, source='<string>'): 737*cda5da8dSAndroid Build Coastguard Worker """Read configuration from a given string.""" 738*cda5da8dSAndroid Build Coastguard Worker sfile = io.StringIO(string) 739*cda5da8dSAndroid Build Coastguard Worker self.read_file(sfile, source) 740*cda5da8dSAndroid Build Coastguard Worker 741*cda5da8dSAndroid Build Coastguard Worker def read_dict(self, dictionary, source='<dict>'): 742*cda5da8dSAndroid Build Coastguard Worker """Read configuration from a dictionary. 743*cda5da8dSAndroid Build Coastguard Worker 744*cda5da8dSAndroid Build Coastguard Worker Keys are section names, values are dictionaries with keys and values 745*cda5da8dSAndroid Build Coastguard Worker that should be present in the section. If the used dictionary type 746*cda5da8dSAndroid Build Coastguard Worker preserves order, sections and their keys will be added in order. 747*cda5da8dSAndroid Build Coastguard Worker 748*cda5da8dSAndroid Build Coastguard Worker All types held in the dictionary are converted to strings during 749*cda5da8dSAndroid Build Coastguard Worker reading, including section names, option names and keys. 750*cda5da8dSAndroid Build Coastguard Worker 751*cda5da8dSAndroid Build Coastguard Worker Optional second argument is the `source` specifying the name of the 752*cda5da8dSAndroid Build Coastguard Worker dictionary being read. 753*cda5da8dSAndroid Build Coastguard Worker """ 754*cda5da8dSAndroid Build Coastguard Worker elements_added = set() 755*cda5da8dSAndroid Build Coastguard Worker for section, keys in dictionary.items(): 756*cda5da8dSAndroid Build Coastguard Worker section = str(section) 757*cda5da8dSAndroid Build Coastguard Worker try: 758*cda5da8dSAndroid Build Coastguard Worker self.add_section(section) 759*cda5da8dSAndroid Build Coastguard Worker except (DuplicateSectionError, ValueError): 760*cda5da8dSAndroid Build Coastguard Worker if self._strict and section in elements_added: 761*cda5da8dSAndroid Build Coastguard Worker raise 762*cda5da8dSAndroid Build Coastguard Worker elements_added.add(section) 763*cda5da8dSAndroid Build Coastguard Worker for key, value in keys.items(): 764*cda5da8dSAndroid Build Coastguard Worker key = self.optionxform(str(key)) 765*cda5da8dSAndroid Build Coastguard Worker if value is not None: 766*cda5da8dSAndroid Build Coastguard Worker value = str(value) 767*cda5da8dSAndroid Build Coastguard Worker if self._strict and (section, key) in elements_added: 768*cda5da8dSAndroid Build Coastguard Worker raise DuplicateOptionError(section, key, source) 769*cda5da8dSAndroid Build Coastguard Worker elements_added.add((section, key)) 770*cda5da8dSAndroid Build Coastguard Worker self.set(section, key, value) 771*cda5da8dSAndroid Build Coastguard Worker 772*cda5da8dSAndroid Build Coastguard Worker def readfp(self, fp, filename=None): 773*cda5da8dSAndroid Build Coastguard Worker """Deprecated, use read_file instead.""" 774*cda5da8dSAndroid Build Coastguard Worker warnings.warn( 775*cda5da8dSAndroid Build Coastguard Worker "This method will be removed in Python 3.12. " 776*cda5da8dSAndroid Build Coastguard Worker "Use 'parser.read_file()' instead.", 777*cda5da8dSAndroid Build Coastguard Worker DeprecationWarning, stacklevel=2 778*cda5da8dSAndroid Build Coastguard Worker ) 779*cda5da8dSAndroid Build Coastguard Worker self.read_file(fp, source=filename) 780*cda5da8dSAndroid Build Coastguard Worker 781*cda5da8dSAndroid Build Coastguard Worker def get(self, section, option, *, raw=False, vars=None, fallback=_UNSET): 782*cda5da8dSAndroid Build Coastguard Worker """Get an option value for a given section. 783*cda5da8dSAndroid Build Coastguard Worker 784*cda5da8dSAndroid Build Coastguard Worker If `vars` is provided, it must be a dictionary. The option is looked up 785*cda5da8dSAndroid Build Coastguard Worker in `vars` (if provided), `section`, and in `DEFAULTSECT` in that order. 786*cda5da8dSAndroid Build Coastguard Worker If the key is not found and `fallback` is provided, it is used as 787*cda5da8dSAndroid Build Coastguard Worker a fallback value. `None` can be provided as a `fallback` value. 788*cda5da8dSAndroid Build Coastguard Worker 789*cda5da8dSAndroid Build Coastguard Worker If interpolation is enabled and the optional argument `raw` is False, 790*cda5da8dSAndroid Build Coastguard Worker all interpolations are expanded in the return values. 791*cda5da8dSAndroid Build Coastguard Worker 792*cda5da8dSAndroid Build Coastguard Worker Arguments `raw`, `vars`, and `fallback` are keyword only. 793*cda5da8dSAndroid Build Coastguard Worker 794*cda5da8dSAndroid Build Coastguard Worker The section DEFAULT is special. 795*cda5da8dSAndroid Build Coastguard Worker """ 796*cda5da8dSAndroid Build Coastguard Worker try: 797*cda5da8dSAndroid Build Coastguard Worker d = self._unify_values(section, vars) 798*cda5da8dSAndroid Build Coastguard Worker except NoSectionError: 799*cda5da8dSAndroid Build Coastguard Worker if fallback is _UNSET: 800*cda5da8dSAndroid Build Coastguard Worker raise 801*cda5da8dSAndroid Build Coastguard Worker else: 802*cda5da8dSAndroid Build Coastguard Worker return fallback 803*cda5da8dSAndroid Build Coastguard Worker option = self.optionxform(option) 804*cda5da8dSAndroid Build Coastguard Worker try: 805*cda5da8dSAndroid Build Coastguard Worker value = d[option] 806*cda5da8dSAndroid Build Coastguard Worker except KeyError: 807*cda5da8dSAndroid Build Coastguard Worker if fallback is _UNSET: 808*cda5da8dSAndroid Build Coastguard Worker raise NoOptionError(option, section) 809*cda5da8dSAndroid Build Coastguard Worker else: 810*cda5da8dSAndroid Build Coastguard Worker return fallback 811*cda5da8dSAndroid Build Coastguard Worker 812*cda5da8dSAndroid Build Coastguard Worker if raw or value is None: 813*cda5da8dSAndroid Build Coastguard Worker return value 814*cda5da8dSAndroid Build Coastguard Worker else: 815*cda5da8dSAndroid Build Coastguard Worker return self._interpolation.before_get(self, section, option, value, 816*cda5da8dSAndroid Build Coastguard Worker d) 817*cda5da8dSAndroid Build Coastguard Worker 818*cda5da8dSAndroid Build Coastguard Worker def _get(self, section, conv, option, **kwargs): 819*cda5da8dSAndroid Build Coastguard Worker return conv(self.get(section, option, **kwargs)) 820*cda5da8dSAndroid Build Coastguard Worker 821*cda5da8dSAndroid Build Coastguard Worker def _get_conv(self, section, option, conv, *, raw=False, vars=None, 822*cda5da8dSAndroid Build Coastguard Worker fallback=_UNSET, **kwargs): 823*cda5da8dSAndroid Build Coastguard Worker try: 824*cda5da8dSAndroid Build Coastguard Worker return self._get(section, conv, option, raw=raw, vars=vars, 825*cda5da8dSAndroid Build Coastguard Worker **kwargs) 826*cda5da8dSAndroid Build Coastguard Worker except (NoSectionError, NoOptionError): 827*cda5da8dSAndroid Build Coastguard Worker if fallback is _UNSET: 828*cda5da8dSAndroid Build Coastguard Worker raise 829*cda5da8dSAndroid Build Coastguard Worker return fallback 830*cda5da8dSAndroid Build Coastguard Worker 831*cda5da8dSAndroid Build Coastguard Worker # getint, getfloat and getboolean provided directly for backwards compat 832*cda5da8dSAndroid Build Coastguard Worker def getint(self, section, option, *, raw=False, vars=None, 833*cda5da8dSAndroid Build Coastguard Worker fallback=_UNSET, **kwargs): 834*cda5da8dSAndroid Build Coastguard Worker return self._get_conv(section, option, int, raw=raw, vars=vars, 835*cda5da8dSAndroid Build Coastguard Worker fallback=fallback, **kwargs) 836*cda5da8dSAndroid Build Coastguard Worker 837*cda5da8dSAndroid Build Coastguard Worker def getfloat(self, section, option, *, raw=False, vars=None, 838*cda5da8dSAndroid Build Coastguard Worker fallback=_UNSET, **kwargs): 839*cda5da8dSAndroid Build Coastguard Worker return self._get_conv(section, option, float, raw=raw, vars=vars, 840*cda5da8dSAndroid Build Coastguard Worker fallback=fallback, **kwargs) 841*cda5da8dSAndroid Build Coastguard Worker 842*cda5da8dSAndroid Build Coastguard Worker def getboolean(self, section, option, *, raw=False, vars=None, 843*cda5da8dSAndroid Build Coastguard Worker fallback=_UNSET, **kwargs): 844*cda5da8dSAndroid Build Coastguard Worker return self._get_conv(section, option, self._convert_to_boolean, 845*cda5da8dSAndroid Build Coastguard Worker raw=raw, vars=vars, fallback=fallback, **kwargs) 846*cda5da8dSAndroid Build Coastguard Worker 847*cda5da8dSAndroid Build Coastguard Worker def items(self, section=_UNSET, raw=False, vars=None): 848*cda5da8dSAndroid Build Coastguard Worker """Return a list of (name, value) tuples for each option in a section. 849*cda5da8dSAndroid Build Coastguard Worker 850*cda5da8dSAndroid Build Coastguard Worker All % interpolations are expanded in the return values, based on the 851*cda5da8dSAndroid Build Coastguard Worker defaults passed into the constructor, unless the optional argument 852*cda5da8dSAndroid Build Coastguard Worker `raw` is true. Additional substitutions may be provided using the 853*cda5da8dSAndroid Build Coastguard Worker `vars` argument, which must be a dictionary whose contents overrides 854*cda5da8dSAndroid Build Coastguard Worker any pre-existing defaults. 855*cda5da8dSAndroid Build Coastguard Worker 856*cda5da8dSAndroid Build Coastguard Worker The section DEFAULT is special. 857*cda5da8dSAndroid Build Coastguard Worker """ 858*cda5da8dSAndroid Build Coastguard Worker if section is _UNSET: 859*cda5da8dSAndroid Build Coastguard Worker return super().items() 860*cda5da8dSAndroid Build Coastguard Worker d = self._defaults.copy() 861*cda5da8dSAndroid Build Coastguard Worker try: 862*cda5da8dSAndroid Build Coastguard Worker d.update(self._sections[section]) 863*cda5da8dSAndroid Build Coastguard Worker except KeyError: 864*cda5da8dSAndroid Build Coastguard Worker if section != self.default_section: 865*cda5da8dSAndroid Build Coastguard Worker raise NoSectionError(section) 866*cda5da8dSAndroid Build Coastguard Worker orig_keys = list(d.keys()) 867*cda5da8dSAndroid Build Coastguard Worker # Update with the entry specific variables 868*cda5da8dSAndroid Build Coastguard Worker if vars: 869*cda5da8dSAndroid Build Coastguard Worker for key, value in vars.items(): 870*cda5da8dSAndroid Build Coastguard Worker d[self.optionxform(key)] = value 871*cda5da8dSAndroid Build Coastguard Worker value_getter = lambda option: self._interpolation.before_get(self, 872*cda5da8dSAndroid Build Coastguard Worker section, option, d[option], d) 873*cda5da8dSAndroid Build Coastguard Worker if raw: 874*cda5da8dSAndroid Build Coastguard Worker value_getter = lambda option: d[option] 875*cda5da8dSAndroid Build Coastguard Worker return [(option, value_getter(option)) for option in orig_keys] 876*cda5da8dSAndroid Build Coastguard Worker 877*cda5da8dSAndroid Build Coastguard Worker def popitem(self): 878*cda5da8dSAndroid Build Coastguard Worker """Remove a section from the parser and return it as 879*cda5da8dSAndroid Build Coastguard Worker a (section_name, section_proxy) tuple. If no section is present, raise 880*cda5da8dSAndroid Build Coastguard Worker KeyError. 881*cda5da8dSAndroid Build Coastguard Worker 882*cda5da8dSAndroid Build Coastguard Worker The section DEFAULT is never returned because it cannot be removed. 883*cda5da8dSAndroid Build Coastguard Worker """ 884*cda5da8dSAndroid Build Coastguard Worker for key in self.sections(): 885*cda5da8dSAndroid Build Coastguard Worker value = self[key] 886*cda5da8dSAndroid Build Coastguard Worker del self[key] 887*cda5da8dSAndroid Build Coastguard Worker return key, value 888*cda5da8dSAndroid Build Coastguard Worker raise KeyError 889*cda5da8dSAndroid Build Coastguard Worker 890*cda5da8dSAndroid Build Coastguard Worker def optionxform(self, optionstr): 891*cda5da8dSAndroid Build Coastguard Worker return optionstr.lower() 892*cda5da8dSAndroid Build Coastguard Worker 893*cda5da8dSAndroid Build Coastguard Worker def has_option(self, section, option): 894*cda5da8dSAndroid Build Coastguard Worker """Check for the existence of a given option in a given section. 895*cda5da8dSAndroid Build Coastguard Worker If the specified `section` is None or an empty string, DEFAULT is 896*cda5da8dSAndroid Build Coastguard Worker assumed. If the specified `section` does not exist, returns False.""" 897*cda5da8dSAndroid Build Coastguard Worker if not section or section == self.default_section: 898*cda5da8dSAndroid Build Coastguard Worker option = self.optionxform(option) 899*cda5da8dSAndroid Build Coastguard Worker return option in self._defaults 900*cda5da8dSAndroid Build Coastguard Worker elif section not in self._sections: 901*cda5da8dSAndroid Build Coastguard Worker return False 902*cda5da8dSAndroid Build Coastguard Worker else: 903*cda5da8dSAndroid Build Coastguard Worker option = self.optionxform(option) 904*cda5da8dSAndroid Build Coastguard Worker return (option in self._sections[section] 905*cda5da8dSAndroid Build Coastguard Worker or option in self._defaults) 906*cda5da8dSAndroid Build Coastguard Worker 907*cda5da8dSAndroid Build Coastguard Worker def set(self, section, option, value=None): 908*cda5da8dSAndroid Build Coastguard Worker """Set an option.""" 909*cda5da8dSAndroid Build Coastguard Worker if value: 910*cda5da8dSAndroid Build Coastguard Worker value = self._interpolation.before_set(self, section, option, 911*cda5da8dSAndroid Build Coastguard Worker value) 912*cda5da8dSAndroid Build Coastguard Worker if not section or section == self.default_section: 913*cda5da8dSAndroid Build Coastguard Worker sectdict = self._defaults 914*cda5da8dSAndroid Build Coastguard Worker else: 915*cda5da8dSAndroid Build Coastguard Worker try: 916*cda5da8dSAndroid Build Coastguard Worker sectdict = self._sections[section] 917*cda5da8dSAndroid Build Coastguard Worker except KeyError: 918*cda5da8dSAndroid Build Coastguard Worker raise NoSectionError(section) from None 919*cda5da8dSAndroid Build Coastguard Worker sectdict[self.optionxform(option)] = value 920*cda5da8dSAndroid Build Coastguard Worker 921*cda5da8dSAndroid Build Coastguard Worker def write(self, fp, space_around_delimiters=True): 922*cda5da8dSAndroid Build Coastguard Worker """Write an .ini-format representation of the configuration state. 923*cda5da8dSAndroid Build Coastguard Worker 924*cda5da8dSAndroid Build Coastguard Worker If `space_around_delimiters` is True (the default), delimiters 925*cda5da8dSAndroid Build Coastguard Worker between keys and values are surrounded by spaces. 926*cda5da8dSAndroid Build Coastguard Worker 927*cda5da8dSAndroid Build Coastguard Worker Please note that comments in the original configuration file are not 928*cda5da8dSAndroid Build Coastguard Worker preserved when writing the configuration back. 929*cda5da8dSAndroid Build Coastguard Worker """ 930*cda5da8dSAndroid Build Coastguard Worker if space_around_delimiters: 931*cda5da8dSAndroid Build Coastguard Worker d = " {} ".format(self._delimiters[0]) 932*cda5da8dSAndroid Build Coastguard Worker else: 933*cda5da8dSAndroid Build Coastguard Worker d = self._delimiters[0] 934*cda5da8dSAndroid Build Coastguard Worker if self._defaults: 935*cda5da8dSAndroid Build Coastguard Worker self._write_section(fp, self.default_section, 936*cda5da8dSAndroid Build Coastguard Worker self._defaults.items(), d) 937*cda5da8dSAndroid Build Coastguard Worker for section in self._sections: 938*cda5da8dSAndroid Build Coastguard Worker self._write_section(fp, section, 939*cda5da8dSAndroid Build Coastguard Worker self._sections[section].items(), d) 940*cda5da8dSAndroid Build Coastguard Worker 941*cda5da8dSAndroid Build Coastguard Worker def _write_section(self, fp, section_name, section_items, delimiter): 942*cda5da8dSAndroid Build Coastguard Worker """Write a single section to the specified `fp`.""" 943*cda5da8dSAndroid Build Coastguard Worker fp.write("[{}]\n".format(section_name)) 944*cda5da8dSAndroid Build Coastguard Worker for key, value in section_items: 945*cda5da8dSAndroid Build Coastguard Worker value = self._interpolation.before_write(self, section_name, key, 946*cda5da8dSAndroid Build Coastguard Worker value) 947*cda5da8dSAndroid Build Coastguard Worker if value is not None or not self._allow_no_value: 948*cda5da8dSAndroid Build Coastguard Worker value = delimiter + str(value).replace('\n', '\n\t') 949*cda5da8dSAndroid Build Coastguard Worker else: 950*cda5da8dSAndroid Build Coastguard Worker value = "" 951*cda5da8dSAndroid Build Coastguard Worker fp.write("{}{}\n".format(key, value)) 952*cda5da8dSAndroid Build Coastguard Worker fp.write("\n") 953*cda5da8dSAndroid Build Coastguard Worker 954*cda5da8dSAndroid Build Coastguard Worker def remove_option(self, section, option): 955*cda5da8dSAndroid Build Coastguard Worker """Remove an option.""" 956*cda5da8dSAndroid Build Coastguard Worker if not section or section == self.default_section: 957*cda5da8dSAndroid Build Coastguard Worker sectdict = self._defaults 958*cda5da8dSAndroid Build Coastguard Worker else: 959*cda5da8dSAndroid Build Coastguard Worker try: 960*cda5da8dSAndroid Build Coastguard Worker sectdict = self._sections[section] 961*cda5da8dSAndroid Build Coastguard Worker except KeyError: 962*cda5da8dSAndroid Build Coastguard Worker raise NoSectionError(section) from None 963*cda5da8dSAndroid Build Coastguard Worker option = self.optionxform(option) 964*cda5da8dSAndroid Build Coastguard Worker existed = option in sectdict 965*cda5da8dSAndroid Build Coastguard Worker if existed: 966*cda5da8dSAndroid Build Coastguard Worker del sectdict[option] 967*cda5da8dSAndroid Build Coastguard Worker return existed 968*cda5da8dSAndroid Build Coastguard Worker 969*cda5da8dSAndroid Build Coastguard Worker def remove_section(self, section): 970*cda5da8dSAndroid Build Coastguard Worker """Remove a file section.""" 971*cda5da8dSAndroid Build Coastguard Worker existed = section in self._sections 972*cda5da8dSAndroid Build Coastguard Worker if existed: 973*cda5da8dSAndroid Build Coastguard Worker del self._sections[section] 974*cda5da8dSAndroid Build Coastguard Worker del self._proxies[section] 975*cda5da8dSAndroid Build Coastguard Worker return existed 976*cda5da8dSAndroid Build Coastguard Worker 977*cda5da8dSAndroid Build Coastguard Worker def __getitem__(self, key): 978*cda5da8dSAndroid Build Coastguard Worker if key != self.default_section and not self.has_section(key): 979*cda5da8dSAndroid Build Coastguard Worker raise KeyError(key) 980*cda5da8dSAndroid Build Coastguard Worker return self._proxies[key] 981*cda5da8dSAndroid Build Coastguard Worker 982*cda5da8dSAndroid Build Coastguard Worker def __setitem__(self, key, value): 983*cda5da8dSAndroid Build Coastguard Worker # To conform with the mapping protocol, overwrites existing values in 984*cda5da8dSAndroid Build Coastguard Worker # the section. 985*cda5da8dSAndroid Build Coastguard Worker if key in self and self[key] is value: 986*cda5da8dSAndroid Build Coastguard Worker return 987*cda5da8dSAndroid Build Coastguard Worker # XXX this is not atomic if read_dict fails at any point. Then again, 988*cda5da8dSAndroid Build Coastguard Worker # no update method in configparser is atomic in this implementation. 989*cda5da8dSAndroid Build Coastguard Worker if key == self.default_section: 990*cda5da8dSAndroid Build Coastguard Worker self._defaults.clear() 991*cda5da8dSAndroid Build Coastguard Worker elif key in self._sections: 992*cda5da8dSAndroid Build Coastguard Worker self._sections[key].clear() 993*cda5da8dSAndroid Build Coastguard Worker self.read_dict({key: value}) 994*cda5da8dSAndroid Build Coastguard Worker 995*cda5da8dSAndroid Build Coastguard Worker def __delitem__(self, key): 996*cda5da8dSAndroid Build Coastguard Worker if key == self.default_section: 997*cda5da8dSAndroid Build Coastguard Worker raise ValueError("Cannot remove the default section.") 998*cda5da8dSAndroid Build Coastguard Worker if not self.has_section(key): 999*cda5da8dSAndroid Build Coastguard Worker raise KeyError(key) 1000*cda5da8dSAndroid Build Coastguard Worker self.remove_section(key) 1001*cda5da8dSAndroid Build Coastguard Worker 1002*cda5da8dSAndroid Build Coastguard Worker def __contains__(self, key): 1003*cda5da8dSAndroid Build Coastguard Worker return key == self.default_section or self.has_section(key) 1004*cda5da8dSAndroid Build Coastguard Worker 1005*cda5da8dSAndroid Build Coastguard Worker def __len__(self): 1006*cda5da8dSAndroid Build Coastguard Worker return len(self._sections) + 1 # the default section 1007*cda5da8dSAndroid Build Coastguard Worker 1008*cda5da8dSAndroid Build Coastguard Worker def __iter__(self): 1009*cda5da8dSAndroid Build Coastguard Worker # XXX does it break when underlying container state changed? 1010*cda5da8dSAndroid Build Coastguard Worker return itertools.chain((self.default_section,), self._sections.keys()) 1011*cda5da8dSAndroid Build Coastguard Worker 1012*cda5da8dSAndroid Build Coastguard Worker def _read(self, fp, fpname): 1013*cda5da8dSAndroid Build Coastguard Worker """Parse a sectioned configuration file. 1014*cda5da8dSAndroid Build Coastguard Worker 1015*cda5da8dSAndroid Build Coastguard Worker Each section in a configuration file contains a header, indicated by 1016*cda5da8dSAndroid Build Coastguard Worker a name in square brackets (`[]`), plus key/value options, indicated by 1017*cda5da8dSAndroid Build Coastguard Worker `name` and `value` delimited with a specific substring (`=` or `:` by 1018*cda5da8dSAndroid Build Coastguard Worker default). 1019*cda5da8dSAndroid Build Coastguard Worker 1020*cda5da8dSAndroid Build Coastguard Worker Values can span multiple lines, as long as they are indented deeper 1021*cda5da8dSAndroid Build Coastguard Worker than the first line of the value. Depending on the parser's mode, blank 1022*cda5da8dSAndroid Build Coastguard Worker lines may be treated as parts of multiline values or ignored. 1023*cda5da8dSAndroid Build Coastguard Worker 1024*cda5da8dSAndroid Build Coastguard Worker Configuration files may include comments, prefixed by specific 1025*cda5da8dSAndroid Build Coastguard Worker characters (`#` and `;` by default). Comments may appear on their own 1026*cda5da8dSAndroid Build Coastguard Worker in an otherwise empty line or may be entered in lines holding values or 1027*cda5da8dSAndroid Build Coastguard Worker section names. Please note that comments get stripped off when reading configuration files. 1028*cda5da8dSAndroid Build Coastguard Worker """ 1029*cda5da8dSAndroid Build Coastguard Worker elements_added = set() 1030*cda5da8dSAndroid Build Coastguard Worker cursect = None # None, or a dictionary 1031*cda5da8dSAndroid Build Coastguard Worker sectname = None 1032*cda5da8dSAndroid Build Coastguard Worker optname = None 1033*cda5da8dSAndroid Build Coastguard Worker lineno = 0 1034*cda5da8dSAndroid Build Coastguard Worker indent_level = 0 1035*cda5da8dSAndroid Build Coastguard Worker e = None # None, or an exception 1036*cda5da8dSAndroid Build Coastguard Worker for lineno, line in enumerate(fp, start=1): 1037*cda5da8dSAndroid Build Coastguard Worker comment_start = sys.maxsize 1038*cda5da8dSAndroid Build Coastguard Worker # strip inline comments 1039*cda5da8dSAndroid Build Coastguard Worker inline_prefixes = {p: -1 for p in self._inline_comment_prefixes} 1040*cda5da8dSAndroid Build Coastguard Worker while comment_start == sys.maxsize and inline_prefixes: 1041*cda5da8dSAndroid Build Coastguard Worker next_prefixes = {} 1042*cda5da8dSAndroid Build Coastguard Worker for prefix, index in inline_prefixes.items(): 1043*cda5da8dSAndroid Build Coastguard Worker index = line.find(prefix, index+1) 1044*cda5da8dSAndroid Build Coastguard Worker if index == -1: 1045*cda5da8dSAndroid Build Coastguard Worker continue 1046*cda5da8dSAndroid Build Coastguard Worker next_prefixes[prefix] = index 1047*cda5da8dSAndroid Build Coastguard Worker if index == 0 or (index > 0 and line[index-1].isspace()): 1048*cda5da8dSAndroid Build Coastguard Worker comment_start = min(comment_start, index) 1049*cda5da8dSAndroid Build Coastguard Worker inline_prefixes = next_prefixes 1050*cda5da8dSAndroid Build Coastguard Worker # strip full line comments 1051*cda5da8dSAndroid Build Coastguard Worker for prefix in self._comment_prefixes: 1052*cda5da8dSAndroid Build Coastguard Worker if line.strip().startswith(prefix): 1053*cda5da8dSAndroid Build Coastguard Worker comment_start = 0 1054*cda5da8dSAndroid Build Coastguard Worker break 1055*cda5da8dSAndroid Build Coastguard Worker if comment_start == sys.maxsize: 1056*cda5da8dSAndroid Build Coastguard Worker comment_start = None 1057*cda5da8dSAndroid Build Coastguard Worker value = line[:comment_start].strip() 1058*cda5da8dSAndroid Build Coastguard Worker if not value: 1059*cda5da8dSAndroid Build Coastguard Worker if self._empty_lines_in_values: 1060*cda5da8dSAndroid Build Coastguard Worker # add empty line to the value, but only if there was no 1061*cda5da8dSAndroid Build Coastguard Worker # comment on the line 1062*cda5da8dSAndroid Build Coastguard Worker if (comment_start is None and 1063*cda5da8dSAndroid Build Coastguard Worker cursect is not None and 1064*cda5da8dSAndroid Build Coastguard Worker optname and 1065*cda5da8dSAndroid Build Coastguard Worker cursect[optname] is not None): 1066*cda5da8dSAndroid Build Coastguard Worker cursect[optname].append('') # newlines added at join 1067*cda5da8dSAndroid Build Coastguard Worker else: 1068*cda5da8dSAndroid Build Coastguard Worker # empty line marks end of value 1069*cda5da8dSAndroid Build Coastguard Worker indent_level = sys.maxsize 1070*cda5da8dSAndroid Build Coastguard Worker continue 1071*cda5da8dSAndroid Build Coastguard Worker # continuation line? 1072*cda5da8dSAndroid Build Coastguard Worker first_nonspace = self.NONSPACECRE.search(line) 1073*cda5da8dSAndroid Build Coastguard Worker cur_indent_level = first_nonspace.start() if first_nonspace else 0 1074*cda5da8dSAndroid Build Coastguard Worker if (cursect is not None and optname and 1075*cda5da8dSAndroid Build Coastguard Worker cur_indent_level > indent_level): 1076*cda5da8dSAndroid Build Coastguard Worker cursect[optname].append(value) 1077*cda5da8dSAndroid Build Coastguard Worker # a section header or option header? 1078*cda5da8dSAndroid Build Coastguard Worker else: 1079*cda5da8dSAndroid Build Coastguard Worker indent_level = cur_indent_level 1080*cda5da8dSAndroid Build Coastguard Worker # is it a section header? 1081*cda5da8dSAndroid Build Coastguard Worker mo = self.SECTCRE.match(value) 1082*cda5da8dSAndroid Build Coastguard Worker if mo: 1083*cda5da8dSAndroid Build Coastguard Worker sectname = mo.group('header') 1084*cda5da8dSAndroid Build Coastguard Worker if sectname in self._sections: 1085*cda5da8dSAndroid Build Coastguard Worker if self._strict and sectname in elements_added: 1086*cda5da8dSAndroid Build Coastguard Worker raise DuplicateSectionError(sectname, fpname, 1087*cda5da8dSAndroid Build Coastguard Worker lineno) 1088*cda5da8dSAndroid Build Coastguard Worker cursect = self._sections[sectname] 1089*cda5da8dSAndroid Build Coastguard Worker elements_added.add(sectname) 1090*cda5da8dSAndroid Build Coastguard Worker elif sectname == self.default_section: 1091*cda5da8dSAndroid Build Coastguard Worker cursect = self._defaults 1092*cda5da8dSAndroid Build Coastguard Worker else: 1093*cda5da8dSAndroid Build Coastguard Worker cursect = self._dict() 1094*cda5da8dSAndroid Build Coastguard Worker self._sections[sectname] = cursect 1095*cda5da8dSAndroid Build Coastguard Worker self._proxies[sectname] = SectionProxy(self, sectname) 1096*cda5da8dSAndroid Build Coastguard Worker elements_added.add(sectname) 1097*cda5da8dSAndroid Build Coastguard Worker # So sections can't start with a continuation line 1098*cda5da8dSAndroid Build Coastguard Worker optname = None 1099*cda5da8dSAndroid Build Coastguard Worker # no section header in the file? 1100*cda5da8dSAndroid Build Coastguard Worker elif cursect is None: 1101*cda5da8dSAndroid Build Coastguard Worker raise MissingSectionHeaderError(fpname, lineno, line) 1102*cda5da8dSAndroid Build Coastguard Worker # an option line? 1103*cda5da8dSAndroid Build Coastguard Worker else: 1104*cda5da8dSAndroid Build Coastguard Worker mo = self._optcre.match(value) 1105*cda5da8dSAndroid Build Coastguard Worker if mo: 1106*cda5da8dSAndroid Build Coastguard Worker optname, vi, optval = mo.group('option', 'vi', 'value') 1107*cda5da8dSAndroid Build Coastguard Worker if not optname: 1108*cda5da8dSAndroid Build Coastguard Worker e = self._handle_error(e, fpname, lineno, line) 1109*cda5da8dSAndroid Build Coastguard Worker optname = self.optionxform(optname.rstrip()) 1110*cda5da8dSAndroid Build Coastguard Worker if (self._strict and 1111*cda5da8dSAndroid Build Coastguard Worker (sectname, optname) in elements_added): 1112*cda5da8dSAndroid Build Coastguard Worker raise DuplicateOptionError(sectname, optname, 1113*cda5da8dSAndroid Build Coastguard Worker fpname, lineno) 1114*cda5da8dSAndroid Build Coastguard Worker elements_added.add((sectname, optname)) 1115*cda5da8dSAndroid Build Coastguard Worker # This check is fine because the OPTCRE cannot 1116*cda5da8dSAndroid Build Coastguard Worker # match if it would set optval to None 1117*cda5da8dSAndroid Build Coastguard Worker if optval is not None: 1118*cda5da8dSAndroid Build Coastguard Worker optval = optval.strip() 1119*cda5da8dSAndroid Build Coastguard Worker cursect[optname] = [optval] 1120*cda5da8dSAndroid Build Coastguard Worker else: 1121*cda5da8dSAndroid Build Coastguard Worker # valueless option handling 1122*cda5da8dSAndroid Build Coastguard Worker cursect[optname] = None 1123*cda5da8dSAndroid Build Coastguard Worker else: 1124*cda5da8dSAndroid Build Coastguard Worker # a non-fatal parsing error occurred. set up the 1125*cda5da8dSAndroid Build Coastguard Worker # exception but keep going. the exception will be 1126*cda5da8dSAndroid Build Coastguard Worker # raised at the end of the file and will contain a 1127*cda5da8dSAndroid Build Coastguard Worker # list of all bogus lines 1128*cda5da8dSAndroid Build Coastguard Worker e = self._handle_error(e, fpname, lineno, line) 1129*cda5da8dSAndroid Build Coastguard Worker self._join_multiline_values() 1130*cda5da8dSAndroid Build Coastguard Worker # if any parsing errors occurred, raise an exception 1131*cda5da8dSAndroid Build Coastguard Worker if e: 1132*cda5da8dSAndroid Build Coastguard Worker raise e 1133*cda5da8dSAndroid Build Coastguard Worker 1134*cda5da8dSAndroid Build Coastguard Worker def _join_multiline_values(self): 1135*cda5da8dSAndroid Build Coastguard Worker defaults = self.default_section, self._defaults 1136*cda5da8dSAndroid Build Coastguard Worker all_sections = itertools.chain((defaults,), 1137*cda5da8dSAndroid Build Coastguard Worker self._sections.items()) 1138*cda5da8dSAndroid Build Coastguard Worker for section, options in all_sections: 1139*cda5da8dSAndroid Build Coastguard Worker for name, val in options.items(): 1140*cda5da8dSAndroid Build Coastguard Worker if isinstance(val, list): 1141*cda5da8dSAndroid Build Coastguard Worker val = '\n'.join(val).rstrip() 1142*cda5da8dSAndroid Build Coastguard Worker options[name] = self._interpolation.before_read(self, 1143*cda5da8dSAndroid Build Coastguard Worker section, 1144*cda5da8dSAndroid Build Coastguard Worker name, val) 1145*cda5da8dSAndroid Build Coastguard Worker 1146*cda5da8dSAndroid Build Coastguard Worker def _read_defaults(self, defaults): 1147*cda5da8dSAndroid Build Coastguard Worker """Read the defaults passed in the initializer. 1148*cda5da8dSAndroid Build Coastguard Worker Note: values can be non-string.""" 1149*cda5da8dSAndroid Build Coastguard Worker for key, value in defaults.items(): 1150*cda5da8dSAndroid Build Coastguard Worker self._defaults[self.optionxform(key)] = value 1151*cda5da8dSAndroid Build Coastguard Worker 1152*cda5da8dSAndroid Build Coastguard Worker def _handle_error(self, exc, fpname, lineno, line): 1153*cda5da8dSAndroid Build Coastguard Worker if not exc: 1154*cda5da8dSAndroid Build Coastguard Worker exc = ParsingError(fpname) 1155*cda5da8dSAndroid Build Coastguard Worker exc.append(lineno, repr(line)) 1156*cda5da8dSAndroid Build Coastguard Worker return exc 1157*cda5da8dSAndroid Build Coastguard Worker 1158*cda5da8dSAndroid Build Coastguard Worker def _unify_values(self, section, vars): 1159*cda5da8dSAndroid Build Coastguard Worker """Create a sequence of lookups with 'vars' taking priority over 1160*cda5da8dSAndroid Build Coastguard Worker the 'section' which takes priority over the DEFAULTSECT. 1161*cda5da8dSAndroid Build Coastguard Worker 1162*cda5da8dSAndroid Build Coastguard Worker """ 1163*cda5da8dSAndroid Build Coastguard Worker sectiondict = {} 1164*cda5da8dSAndroid Build Coastguard Worker try: 1165*cda5da8dSAndroid Build Coastguard Worker sectiondict = self._sections[section] 1166*cda5da8dSAndroid Build Coastguard Worker except KeyError: 1167*cda5da8dSAndroid Build Coastguard Worker if section != self.default_section: 1168*cda5da8dSAndroid Build Coastguard Worker raise NoSectionError(section) from None 1169*cda5da8dSAndroid Build Coastguard Worker # Update with the entry specific variables 1170*cda5da8dSAndroid Build Coastguard Worker vardict = {} 1171*cda5da8dSAndroid Build Coastguard Worker if vars: 1172*cda5da8dSAndroid Build Coastguard Worker for key, value in vars.items(): 1173*cda5da8dSAndroid Build Coastguard Worker if value is not None: 1174*cda5da8dSAndroid Build Coastguard Worker value = str(value) 1175*cda5da8dSAndroid Build Coastguard Worker vardict[self.optionxform(key)] = value 1176*cda5da8dSAndroid Build Coastguard Worker return _ChainMap(vardict, sectiondict, self._defaults) 1177*cda5da8dSAndroid Build Coastguard Worker 1178*cda5da8dSAndroid Build Coastguard Worker def _convert_to_boolean(self, value): 1179*cda5da8dSAndroid Build Coastguard Worker """Return a boolean value translating from other types if necessary. 1180*cda5da8dSAndroid Build Coastguard Worker """ 1181*cda5da8dSAndroid Build Coastguard Worker if value.lower() not in self.BOOLEAN_STATES: 1182*cda5da8dSAndroid Build Coastguard Worker raise ValueError('Not a boolean: %s' % value) 1183*cda5da8dSAndroid Build Coastguard Worker return self.BOOLEAN_STATES[value.lower()] 1184*cda5da8dSAndroid Build Coastguard Worker 1185*cda5da8dSAndroid Build Coastguard Worker def _validate_value_types(self, *, section="", option="", value=""): 1186*cda5da8dSAndroid Build Coastguard Worker """Raises a TypeError for non-string values. 1187*cda5da8dSAndroid Build Coastguard Worker 1188*cda5da8dSAndroid Build Coastguard Worker The only legal non-string value if we allow valueless 1189*cda5da8dSAndroid Build Coastguard Worker options is None, so we need to check if the value is a 1190*cda5da8dSAndroid Build Coastguard Worker string if: 1191*cda5da8dSAndroid Build Coastguard Worker - we do not allow valueless options, or 1192*cda5da8dSAndroid Build Coastguard Worker - we allow valueless options but the value is not None 1193*cda5da8dSAndroid Build Coastguard Worker 1194*cda5da8dSAndroid Build Coastguard Worker For compatibility reasons this method is not used in classic set() 1195*cda5da8dSAndroid Build Coastguard Worker for RawConfigParsers. It is invoked in every case for mapping protocol 1196*cda5da8dSAndroid Build Coastguard Worker access and in ConfigParser.set(). 1197*cda5da8dSAndroid Build Coastguard Worker """ 1198*cda5da8dSAndroid Build Coastguard Worker if not isinstance(section, str): 1199*cda5da8dSAndroid Build Coastguard Worker raise TypeError("section names must be strings") 1200*cda5da8dSAndroid Build Coastguard Worker if not isinstance(option, str): 1201*cda5da8dSAndroid Build Coastguard Worker raise TypeError("option keys must be strings") 1202*cda5da8dSAndroid Build Coastguard Worker if not self._allow_no_value or value: 1203*cda5da8dSAndroid Build Coastguard Worker if not isinstance(value, str): 1204*cda5da8dSAndroid Build Coastguard Worker raise TypeError("option values must be strings") 1205*cda5da8dSAndroid Build Coastguard Worker 1206*cda5da8dSAndroid Build Coastguard Worker @property 1207*cda5da8dSAndroid Build Coastguard Worker def converters(self): 1208*cda5da8dSAndroid Build Coastguard Worker return self._converters 1209*cda5da8dSAndroid Build Coastguard Worker 1210*cda5da8dSAndroid Build Coastguard Worker 1211*cda5da8dSAndroid Build Coastguard Workerclass ConfigParser(RawConfigParser): 1212*cda5da8dSAndroid Build Coastguard Worker """ConfigParser implementing interpolation.""" 1213*cda5da8dSAndroid Build Coastguard Worker 1214*cda5da8dSAndroid Build Coastguard Worker _DEFAULT_INTERPOLATION = BasicInterpolation() 1215*cda5da8dSAndroid Build Coastguard Worker 1216*cda5da8dSAndroid Build Coastguard Worker def set(self, section, option, value=None): 1217*cda5da8dSAndroid Build Coastguard Worker """Set an option. Extends RawConfigParser.set by validating type and 1218*cda5da8dSAndroid Build Coastguard Worker interpolation syntax on the value.""" 1219*cda5da8dSAndroid Build Coastguard Worker self._validate_value_types(option=option, value=value) 1220*cda5da8dSAndroid Build Coastguard Worker super().set(section, option, value) 1221*cda5da8dSAndroid Build Coastguard Worker 1222*cda5da8dSAndroid Build Coastguard Worker def add_section(self, section): 1223*cda5da8dSAndroid Build Coastguard Worker """Create a new section in the configuration. Extends 1224*cda5da8dSAndroid Build Coastguard Worker RawConfigParser.add_section by validating if the section name is 1225*cda5da8dSAndroid Build Coastguard Worker a string.""" 1226*cda5da8dSAndroid Build Coastguard Worker self._validate_value_types(section=section) 1227*cda5da8dSAndroid Build Coastguard Worker super().add_section(section) 1228*cda5da8dSAndroid Build Coastguard Worker 1229*cda5da8dSAndroid Build Coastguard Worker def _read_defaults(self, defaults): 1230*cda5da8dSAndroid Build Coastguard Worker """Reads the defaults passed in the initializer, implicitly converting 1231*cda5da8dSAndroid Build Coastguard Worker values to strings like the rest of the API. 1232*cda5da8dSAndroid Build Coastguard Worker 1233*cda5da8dSAndroid Build Coastguard Worker Does not perform interpolation for backwards compatibility. 1234*cda5da8dSAndroid Build Coastguard Worker """ 1235*cda5da8dSAndroid Build Coastguard Worker try: 1236*cda5da8dSAndroid Build Coastguard Worker hold_interpolation = self._interpolation 1237*cda5da8dSAndroid Build Coastguard Worker self._interpolation = Interpolation() 1238*cda5da8dSAndroid Build Coastguard Worker self.read_dict({self.default_section: defaults}) 1239*cda5da8dSAndroid Build Coastguard Worker finally: 1240*cda5da8dSAndroid Build Coastguard Worker self._interpolation = hold_interpolation 1241*cda5da8dSAndroid Build Coastguard Worker 1242*cda5da8dSAndroid Build Coastguard Worker 1243*cda5da8dSAndroid Build Coastguard Workerclass SafeConfigParser(ConfigParser): 1244*cda5da8dSAndroid Build Coastguard Worker """ConfigParser alias for backwards compatibility purposes.""" 1245*cda5da8dSAndroid Build Coastguard Worker 1246*cda5da8dSAndroid Build Coastguard Worker def __init__(self, *args, **kwargs): 1247*cda5da8dSAndroid Build Coastguard Worker super().__init__(*args, **kwargs) 1248*cda5da8dSAndroid Build Coastguard Worker warnings.warn( 1249*cda5da8dSAndroid Build Coastguard Worker "The SafeConfigParser class has been renamed to ConfigParser " 1250*cda5da8dSAndroid Build Coastguard Worker "in Python 3.2. This alias will be removed in Python 3.12." 1251*cda5da8dSAndroid Build Coastguard Worker " Use ConfigParser directly instead.", 1252*cda5da8dSAndroid Build Coastguard Worker DeprecationWarning, stacklevel=2 1253*cda5da8dSAndroid Build Coastguard Worker ) 1254*cda5da8dSAndroid Build Coastguard Worker 1255*cda5da8dSAndroid Build Coastguard Worker 1256*cda5da8dSAndroid Build Coastguard Workerclass SectionProxy(MutableMapping): 1257*cda5da8dSAndroid Build Coastguard Worker """A proxy for a single section from a parser.""" 1258*cda5da8dSAndroid Build Coastguard Worker 1259*cda5da8dSAndroid Build Coastguard Worker def __init__(self, parser, name): 1260*cda5da8dSAndroid Build Coastguard Worker """Creates a view on a section of the specified `name` in `parser`.""" 1261*cda5da8dSAndroid Build Coastguard Worker self._parser = parser 1262*cda5da8dSAndroid Build Coastguard Worker self._name = name 1263*cda5da8dSAndroid Build Coastguard Worker for conv in parser.converters: 1264*cda5da8dSAndroid Build Coastguard Worker key = 'get' + conv 1265*cda5da8dSAndroid Build Coastguard Worker getter = functools.partial(self.get, _impl=getattr(parser, key)) 1266*cda5da8dSAndroid Build Coastguard Worker setattr(self, key, getter) 1267*cda5da8dSAndroid Build Coastguard Worker 1268*cda5da8dSAndroid Build Coastguard Worker def __repr__(self): 1269*cda5da8dSAndroid Build Coastguard Worker return '<Section: {}>'.format(self._name) 1270*cda5da8dSAndroid Build Coastguard Worker 1271*cda5da8dSAndroid Build Coastguard Worker def __getitem__(self, key): 1272*cda5da8dSAndroid Build Coastguard Worker if not self._parser.has_option(self._name, key): 1273*cda5da8dSAndroid Build Coastguard Worker raise KeyError(key) 1274*cda5da8dSAndroid Build Coastguard Worker return self._parser.get(self._name, key) 1275*cda5da8dSAndroid Build Coastguard Worker 1276*cda5da8dSAndroid Build Coastguard Worker def __setitem__(self, key, value): 1277*cda5da8dSAndroid Build Coastguard Worker self._parser._validate_value_types(option=key, value=value) 1278*cda5da8dSAndroid Build Coastguard Worker return self._parser.set(self._name, key, value) 1279*cda5da8dSAndroid Build Coastguard Worker 1280*cda5da8dSAndroid Build Coastguard Worker def __delitem__(self, key): 1281*cda5da8dSAndroid Build Coastguard Worker if not (self._parser.has_option(self._name, key) and 1282*cda5da8dSAndroid Build Coastguard Worker self._parser.remove_option(self._name, key)): 1283*cda5da8dSAndroid Build Coastguard Worker raise KeyError(key) 1284*cda5da8dSAndroid Build Coastguard Worker 1285*cda5da8dSAndroid Build Coastguard Worker def __contains__(self, key): 1286*cda5da8dSAndroid Build Coastguard Worker return self._parser.has_option(self._name, key) 1287*cda5da8dSAndroid Build Coastguard Worker 1288*cda5da8dSAndroid Build Coastguard Worker def __len__(self): 1289*cda5da8dSAndroid Build Coastguard Worker return len(self._options()) 1290*cda5da8dSAndroid Build Coastguard Worker 1291*cda5da8dSAndroid Build Coastguard Worker def __iter__(self): 1292*cda5da8dSAndroid Build Coastguard Worker return self._options().__iter__() 1293*cda5da8dSAndroid Build Coastguard Worker 1294*cda5da8dSAndroid Build Coastguard Worker def _options(self): 1295*cda5da8dSAndroid Build Coastguard Worker if self._name != self._parser.default_section: 1296*cda5da8dSAndroid Build Coastguard Worker return self._parser.options(self._name) 1297*cda5da8dSAndroid Build Coastguard Worker else: 1298*cda5da8dSAndroid Build Coastguard Worker return self._parser.defaults() 1299*cda5da8dSAndroid Build Coastguard Worker 1300*cda5da8dSAndroid Build Coastguard Worker @property 1301*cda5da8dSAndroid Build Coastguard Worker def parser(self): 1302*cda5da8dSAndroid Build Coastguard Worker # The parser object of the proxy is read-only. 1303*cda5da8dSAndroid Build Coastguard Worker return self._parser 1304*cda5da8dSAndroid Build Coastguard Worker 1305*cda5da8dSAndroid Build Coastguard Worker @property 1306*cda5da8dSAndroid Build Coastguard Worker def name(self): 1307*cda5da8dSAndroid Build Coastguard Worker # The name of the section on a proxy is read-only. 1308*cda5da8dSAndroid Build Coastguard Worker return self._name 1309*cda5da8dSAndroid Build Coastguard Worker 1310*cda5da8dSAndroid Build Coastguard Worker def get(self, option, fallback=None, *, raw=False, vars=None, 1311*cda5da8dSAndroid Build Coastguard Worker _impl=None, **kwargs): 1312*cda5da8dSAndroid Build Coastguard Worker """Get an option value. 1313*cda5da8dSAndroid Build Coastguard Worker 1314*cda5da8dSAndroid Build Coastguard Worker Unless `fallback` is provided, `None` will be returned if the option 1315*cda5da8dSAndroid Build Coastguard Worker is not found. 1316*cda5da8dSAndroid Build Coastguard Worker 1317*cda5da8dSAndroid Build Coastguard Worker """ 1318*cda5da8dSAndroid Build Coastguard Worker # If `_impl` is provided, it should be a getter method on the parser 1319*cda5da8dSAndroid Build Coastguard Worker # object that provides the desired type conversion. 1320*cda5da8dSAndroid Build Coastguard Worker if not _impl: 1321*cda5da8dSAndroid Build Coastguard Worker _impl = self._parser.get 1322*cda5da8dSAndroid Build Coastguard Worker return _impl(self._name, option, raw=raw, vars=vars, 1323*cda5da8dSAndroid Build Coastguard Worker fallback=fallback, **kwargs) 1324*cda5da8dSAndroid Build Coastguard Worker 1325*cda5da8dSAndroid Build Coastguard Worker 1326*cda5da8dSAndroid Build Coastguard Workerclass ConverterMapping(MutableMapping): 1327*cda5da8dSAndroid Build Coastguard Worker """Enables reuse of get*() methods between the parser and section proxies. 1328*cda5da8dSAndroid Build Coastguard Worker 1329*cda5da8dSAndroid Build Coastguard Worker If a parser class implements a getter directly, the value for the given 1330*cda5da8dSAndroid Build Coastguard Worker key will be ``None``. The presence of the converter name here enables 1331*cda5da8dSAndroid Build Coastguard Worker section proxies to find and use the implementation on the parser class. 1332*cda5da8dSAndroid Build Coastguard Worker """ 1333*cda5da8dSAndroid Build Coastguard Worker 1334*cda5da8dSAndroid Build Coastguard Worker GETTERCRE = re.compile(r"^get(?P<name>.+)$") 1335*cda5da8dSAndroid Build Coastguard Worker 1336*cda5da8dSAndroid Build Coastguard Worker def __init__(self, parser): 1337*cda5da8dSAndroid Build Coastguard Worker self._parser = parser 1338*cda5da8dSAndroid Build Coastguard Worker self._data = {} 1339*cda5da8dSAndroid Build Coastguard Worker for getter in dir(self._parser): 1340*cda5da8dSAndroid Build Coastguard Worker m = self.GETTERCRE.match(getter) 1341*cda5da8dSAndroid Build Coastguard Worker if not m or not callable(getattr(self._parser, getter)): 1342*cda5da8dSAndroid Build Coastguard Worker continue 1343*cda5da8dSAndroid Build Coastguard Worker self._data[m.group('name')] = None # See class docstring. 1344*cda5da8dSAndroid Build Coastguard Worker 1345*cda5da8dSAndroid Build Coastguard Worker def __getitem__(self, key): 1346*cda5da8dSAndroid Build Coastguard Worker return self._data[key] 1347*cda5da8dSAndroid Build Coastguard Worker 1348*cda5da8dSAndroid Build Coastguard Worker def __setitem__(self, key, value): 1349*cda5da8dSAndroid Build Coastguard Worker try: 1350*cda5da8dSAndroid Build Coastguard Worker k = 'get' + key 1351*cda5da8dSAndroid Build Coastguard Worker except TypeError: 1352*cda5da8dSAndroid Build Coastguard Worker raise ValueError('Incompatible key: {} (type: {})' 1353*cda5da8dSAndroid Build Coastguard Worker ''.format(key, type(key))) 1354*cda5da8dSAndroid Build Coastguard Worker if k == 'get': 1355*cda5da8dSAndroid Build Coastguard Worker raise ValueError('Incompatible key: cannot use "" as a name') 1356*cda5da8dSAndroid Build Coastguard Worker self._data[key] = value 1357*cda5da8dSAndroid Build Coastguard Worker func = functools.partial(self._parser._get_conv, conv=value) 1358*cda5da8dSAndroid Build Coastguard Worker func.converter = value 1359*cda5da8dSAndroid Build Coastguard Worker setattr(self._parser, k, func) 1360*cda5da8dSAndroid Build Coastguard Worker for proxy in self._parser.values(): 1361*cda5da8dSAndroid Build Coastguard Worker getter = functools.partial(proxy.get, _impl=func) 1362*cda5da8dSAndroid Build Coastguard Worker setattr(proxy, k, getter) 1363*cda5da8dSAndroid Build Coastguard Worker 1364*cda5da8dSAndroid Build Coastguard Worker def __delitem__(self, key): 1365*cda5da8dSAndroid Build Coastguard Worker try: 1366*cda5da8dSAndroid Build Coastguard Worker k = 'get' + (key or None) 1367*cda5da8dSAndroid Build Coastguard Worker except TypeError: 1368*cda5da8dSAndroid Build Coastguard Worker raise KeyError(key) 1369*cda5da8dSAndroid Build Coastguard Worker del self._data[key] 1370*cda5da8dSAndroid Build Coastguard Worker for inst in itertools.chain((self._parser,), self._parser.values()): 1371*cda5da8dSAndroid Build Coastguard Worker try: 1372*cda5da8dSAndroid Build Coastguard Worker delattr(inst, k) 1373*cda5da8dSAndroid Build Coastguard Worker except AttributeError: 1374*cda5da8dSAndroid Build Coastguard Worker # don't raise since the entry was present in _data, silently 1375*cda5da8dSAndroid Build Coastguard Worker # clean up 1376*cda5da8dSAndroid Build Coastguard Worker continue 1377*cda5da8dSAndroid Build Coastguard Worker 1378*cda5da8dSAndroid Build Coastguard Worker def __iter__(self): 1379*cda5da8dSAndroid Build Coastguard Worker return iter(self._data) 1380*cda5da8dSAndroid Build Coastguard Worker 1381*cda5da8dSAndroid Build Coastguard Worker def __len__(self): 1382*cda5da8dSAndroid Build Coastguard Worker return len(self._data) 1383