0001"""
0002This implements the public `ConfigParser` interface, but with some
0003additional enhancements.
0004
0005
0006"""
0007
0008import codecs
0009import os
0010from UserDict import DictMixin
0011import string
0012from initools import iniparser
0013
0014class Error(Exception):
0015 pass
0016
0017class NoSectionError(Error):
0018 """Exception raised when a specified section is not found."""
0019
0020class DuplicateSectionError(Error):
0021 """Exception raised if add_section() is called with the name of a
0022 section that is already present."""
0023
0024class NoOptionError(Error):
0025 """Exception raised when a specified option is not found in the
0026 specified section."""
0027
0028class InterpolationError(Error):
0029 """Base class for exceptions raised when problems occur performing
0030 string interpolation."""
0031
0032 def __init__(self, option, section, msg):
0033 Error.__init__(self, msg)
0034 self.option = option
0035 self.section = section
0036
0037class InterpolationDepthError(InterpolationError):
0038 """Exception raised when string interpolation cannot be completed
0039 because the number of iterations exceeds
0040 MAX_INTERPOLATION_DEPTH. Subclass of InterpolationError."""
0041
0042class InterpolationMissingOptionError(InterpolationError):
0043 """Exception raised when an option referenced from a value does
0044 not exist. Subclass of InterpolationError."""
0045
0046 def __init__(self, option, section, rawval, reference, msg=None):
0047 if msg is None:
0048 msg = ("Bad value substitution:\n"
0049 "\tsection: [%s]\n"
0050 "\toption : %s\n"
0051 "\tkey : %s\n"
0052 "\trawval : %s\n"
0053 % (section, option, reference, rawval))
0054 InterpolationError.__init__(self, option, section, msg)
0055 self.reference = reference
0056
0057class InterpolationSyntaxError(InterpolationError):
0058 """Exception raised when the source text into which substitutions
0059 are made does not conform to the required syntax. Subclass of
0060 InterpolationError."""
0061
0062"""Exception raised when errors occur attempting to parse a file."""
0063ParsingError = iniparser.ParseError
0064
0065class MissingSectionHeaderError(ParsingError):
0066 """Exception raised when attempting to parse a file which has no
0067 section headers."""
0068
0069
0070
0071
0072MAX_INTERPOLATION_DEPTH = 10
0073
0074class _NoDefault: pass
0075
0076class RawConfigParser(object):
0077
0078
0079
0080 percent_expand = False
0081
0082
0083 dollar_expand = False
0084
0085
0086 safe_set = False
0087
0088
0089 global_section = False
0090
0091 case_sensitive = False
0092
0093 section_case_sensitive = True
0094
0095 encoding = 'utf8'
0096
0097
0098
0099 inline_comments = True
0100
0101
0102 continuation_indent = '\t'
0103
0104
0105 inherit_defaults = True
0106
0107
0108 extendable = False
0109
0110 default_extend_name = 'extends'
0111
0112
0113
0114 global_extend_section_names = ['', 'global', 'DEFAULT']
0115
0116
0117
0118
0119 default_extend_section_name = 'main'
0120
0121 def __init__(self, defaults=None,
0122 encoding=_NoDefault,
0123 percent_expand=_NoDefault,
0124 safe_set=_NoDefault,
0125 dollar_expand=_NoDefault,
0126 case_sensitive=_NoDefault,
0127 section_case_sensitive=_NoDefault,
0128 global_section=_NoDefault,
0129 inline_comments=_NoDefault,
0130 inherit_defaults=_NoDefault,
0131 extendable=_NoDefault):
0132 if encoding is not _NoDefault:
0133 self.encoding = encoding
0134 if percent_expand is not _NoDefault:
0135 self.percent_expand = percent_expand
0136 if safe_set is not _NoDefault:
0137 self.safe_set = safe_set
0138 if dollar_expand is not _NoDefault:
0139 self.dollar_expand = dollar_expand
0140 if case_sensitive is not _NoDefault:
0141 self.case_sensitive = case_sensitive
0142 if section_case_sensitive is not _NoDefault:
0143 self.section_case_sensitive = section_case_sensitive
0144 if global_section is not _NoDefault:
0145 self.global_section = global_section
0146 if inline_comments is not _NoDefault:
0147 self.inline_comments = inline_comments
0148 if inherit_defaults is not _NoDefault:
0149 self.inherit_defaults = inherit_defaults
0150 if extendable is not _NoDefault:
0151 self.extendable = extendable
0152 if self.extendable:
0153 if isinstance(self.extendable, basestring):
0154 self._extends_name = self.extendable
0155 else:
0156 self._extends_name = self.default_extend_name
0157 self._pre_normalized_keys = {}
0158 self._pre_normalized_sections = {}
0159 self._key_file_positions = {}
0160 self._key_comments = {}
0161 self._section_order = []
0162 self._section_key_order = {}
0163 self._section_comments = {}
0164 self._values = {}
0165 self.add_section('DEFAULT')
0166 if defaults is not None:
0167 for name, value in defaults.items():
0168 self.set('DEFAULT', name, value)
0169
0170 def defaults(self):
0171 """Return a dictionary containing the instance-wide defaults."""
0172 default = self.sectionxform('DEFAULT')
0173 return self._values.get(default, {})
0174
0175 def sections(self):
0176 """Return a list of the sections available; DEFAULT is not
0177 included in the list."""
0178 return [self._pre_normalized_sections[sec]
0179 for sec in self._section_order
0180 if sec != self.sectionxform('DEFAULT')]
0181
0182 def add_section(self, section, comment=None):
0183 """Add a section named section to the instance.
0184
0185 If a section by the given name already exists,
0186 DuplicateSectionError is raised.
0187 """
0188 sec = self.sectionxform(section)
0189 if sec in self._values:
0190 if self.sectionxform('DEFAULT') == sec:
0191
0192 return
0193 raise DuplicateSectionError(
0194 "A section [%s] already exists"
0195 % sec)
0196 self._pre_normalized_sections[sec] = section
0197 if sec not in self._section_order:
0198 self._section_order.append(sec)
0199 self._section_key_order[sec] = []
0200 self._section_comments[sec] = comment
0201 self._values[sec] = {}
0202
0203 def has_section(self, section):
0204 """Indicates whether the named section is present in the
0205 configuration.
0206
0207 The DEFAULT section is not acknowledged.
0208 """
0209 if section == 'DEFAULT':
0210 return False
0211 return self.sectionxform(section) in self._values
0212
0213 def options(self, section):
0214 """Returns a list of options available in the specified
0215 section."""
0216 sec = self.sectionxform(section)
0217 if sec not in self._values:
0218 raise NoSectionError(
0219 "Section [%s] does not exist" % sec)
0220 v = [self._pre_normalized_keys[(sec, op)]
0221 for op in self._section_key_order[sec]]
0222 if self.inherit_defaults and self.sectionxform('DEFAULT') != sec:
0223 v = v[:]
0224 v.extend(self.options('DEFAULT'))
0225 return v
0226
0227 def has_option(self, section, option):
0228 """If the given section exists, and contains the given option,
0229 return True; otherwise return False."""
0230 sec = self.sectionxform(section)
0231 if self.inherit_defaults and self.sectionxform('DEFAULT') != sec:
0232 if self.has_option('DEFAULT', option):
0233 return True
0234 if sec not in self._values:
0235 return False
0236 return self.optionxform(option) in self._values[sec]
0237
0238 def read(self, filenames, extending=False, map_sections=None):
0239 """Attempt to read and parse a list of filenames, returning a
0240 list of filenames which were successfully parsed.
0241
0242 If filenames is a string or Unicode string, it is treated as a
0243 single filename. If a file named in filenames cannot be
0244 opened, that file will be ignored. This is designed so that
0245 you can specify a list of potential configuration file
0246 locations (for example, the current directory, the user's home
0247 directory, and some system-wide directory), and all existing
0248 configuration files in the list will be read. If none of the
0249 named files exist, the ConfigParser instance will contain an
0250 empty dataset. An application which requires initial values to
0251 be loaded from a file should load the required file or files
0252 using readfp() before calling read() for any optional files:
0253
0254 import ConfigParser, os
0255
0256 config = ConfigParser.ConfigParser()
0257 config.readfp(open('defaults.cfg'))
0258 config.read(['site.cfg', os.path.expanduser('~/.myapp.cfg')])
0259
0260 If ``extending`` is true (default false) then the values
0261 picked up from the file will *not* override the values already
0262 present (that means that the file being loaded is extended by
0263 the already loaded file).
0264 """
0265 found = []
0266 if isinstance(filenames, basestring):
0267 filenames = [filenames]
0268 for fn in filenames:
0269 if not os.path.exists(fn):
0270 continue
0271 found.append(fn)
0272 fp = open(fn)
0273 try:
0274 self.readfp(fp, fn, extending=extending,
0275 map_sections=map_sections)
0276 finally:
0277 fp.close()
0278 return found
0279
0280 def readfp(self, fp, filename='<???>', extending=False,
0281 map_sections=None):
0282 """Read and parse configuration data from the file or
0283 file-like object in fp
0284
0285 Only the readline() method is used. If filename is omitted and
0286 fp has a name attribute, that is used for filename; the
0287 default is '<???>'.
0288 """
0289 parser = _ConfigParserParser(self, extending=extending,
0290 map_sections=map_sections)
0291 parser.loadfile(fp, filename=filename,
0292 encoding=self.encoding)
0293 if self.extendable:
0294 self._process_extends()
0295
0296 def _process_extends(self):
0297 """
0298 Figure out if there's any extends options in the config
0299 file, and if so then process those options and remove them.
0300 """
0301 extend = self.optionxform(self._extends_name)
0302 glob_sections = map(
0303 self.sectionxform, self.global_extend_section_names)
0304 reads = []
0305 for section, values in self._values.iteritems():
0306 if extend in values:
0307 value = self.getfilename(section, extend)
0308 if self.sectionxform(section) in glob_sections:
0309 reads.append((value, None))
0310 else:
0311 if '#' in value:
0312 value, inc_section = value.split('#', 1)
0313 else:
0314 inc_section = self.default_extend_section_name
0315 if inc_section == '__name__':
0316 inc_section = section
0317 reads.append((value, {inc_section: section}))
0318 self.remove_option(section, extend)
0319 for filename, map_sections in reads:
0320 self.read(filename, extending=True,
0321 map_sections=map_sections)
0322
0323 def get(self, section, option, raw=False, vars=None, _recursion=0):
0324 """Get an option value for the named section.
0325
0326 If self.percent_expand is true, then all the '%'
0327 interpolations are expanded, using the optional vars. If raw
0328 is True, then no interpolation is done."""
0329 if _recursion > MAX_INTERPOLATION_DEPTH:
0330 raise InterpolationDepthError(
0331 section, option,
0332 "Maximum recursion depth for interpolation exceded")
0333 sec = self.sectionxform(section)
0334 op = self.optionxform(option)
0335 if sec not in self._values:
0336 raise NoSectionError(
0337 "Section [%s] not found (when looking for option %r)"
0338 % (section, option))
0339 values = self._values[sec]
0340 if op not in values:
0341 if (self.inherit_defaults
0342 and self.sectionxform('DEFAULT') != sec
0343 and self.has_option('DEFAULT', op)):
0344 value = self.get('DEFAULT', op, raw=True)
0345 else:
0346 raise NoOptionError(
0347 "Option %r not found (in section [%s])"
0348 % (option, section))
0349 else:
0350 value = values[op]
0351 if raw:
0352 return value
0353 if self.percent_expand:
0354 value = self._do_percent_expansion(
0355 section, option, value, vars, _recursion)
0356 if self.dollar_expand:
0357 value = self._do_dollar_expansion(
0358 section, option, value, vars, _recursion)
0359 return value
0360
0361 def _do_percent_expansion(self, section, option, value,
0362 vars, _recursion):
0363 if vars is None:
0364 vars = {}
0365 vars = _OptionWrapper(self, vars, section, option, _recursion)
0366 if not isinstance(value, basestring):
0367 raise TypeError(
0368 "Cannot interpolate a non-string [%s] option %s=%r"
0369 % (section, option, value))
0370 try:
0371 return value % vars
0372 except KeyError, e:
0373 var = e.args[0]
0374 raise InterpolationMissingOptionError(
0375 option, section, value, var,
0376 "Variable %s not found in [%s] option %s=%s%s"
0377 % (var, section, option, value,
0378 self._get_location_info(section, option)))
0379 except ValueError, e:
0380 raise InterpolationSyntaxError(
0381 option, section,
0382 "%s in [%s] option %s=%s%s"
0383 % (e, section, option, value,
0384 self._get_location_info(section, option)))
0385
0386 def _do_dollar_expansion(self, section, option, value,
0387 vars, _recursion):
0388 if vars is None:
0389 vars = {}
0390 vars = _SectionOptionWrapper(self, vars, section, option, _recursion)
0391 if not isinstance(value, basestring):
0392 raise TypeError(
0393 "Cannot interpolate a non-string [%s] option %s=%r"
0394 % (section, option, value))
0395 try:
0396 tmpl = string.Template(value)
0397 return tmpl.substitute(vars)
0398 except KeyError, e:
0399 var = e.args[0]
0400 raise InterpolationMissingOptionError(
0401 option, section, value, var,
0402 "Variable %s not found in [%s] option %s=%s%s"
0403 % (var, section, option, value,
0404 self._get_location_info(section, option)))
0405 except ValueError, e:
0406 raise InterpolationSyntaxError(
0407 option, section,
0408 "%s in [%s] option %s=%s%s"
0409 % (e, section, option, value,
0410 self._get_location_info(section, option)))
0411
0412 def _get_location_info(self, section, option):
0413 location = self._key_file_positions.get(
0414 (self.sectionxform(section), self.optionxform(option)),
0415 (None, None))
0416 if location[0]:
0417 extra = ' (located at %s' % location[0]
0418 if location[1]:
0419 extra += ':%s' % location[1]
0420 extra += ')'
0421 else:
0422 extra = ''
0423 return extra
0424
0425 def getint(self, section, option):
0426 """A convenience method which coerces the option in the
0427 specified section to an integer."""
0428 value = self.get(section, option)
0429 try:
0430 return int(value)
0431 except ValueError:
0432 loc_info = self._get_location_info(section, option)
0433 raise ValueError(
0434 "Could not convert option %s=%s (in [%s]) to an integer%s"
0435 % (option, value, section, loc_info))
0436
0437 def getfloat(self, section, option):
0438 """A convenience method which coerces the option in the
0439 specified section to a floating point number."""
0440 value = self.get(section, option)
0441 try:
0442 return float(value)
0443 except ValueError:
0444 raise ValueError(
0445 "Could not convert options %s=%s (in [%s]) to a float%s"
0446 % (option, value, section,
0447 self._get_location_info(section, option)))
0448
0449 def getboolean(self, section, option):
0450 """A convenience method which coerces the option in the
0451 specified section to a Boolean value.
0452
0453 Note that the accepted values for the option are "1", "yes",
0454 "true", and "on", which cause this method to return True, and
0455 "0", "no", "false", and "off", which cause it to return
0456 False. These string values are checked in a case-insensitive
0457 manner. Any other value will cause it to raise ValueError.
0458 """
0459 value = self.get(section, option)
0460 value = value.strip().lower()
0461 if value in ('1', 'y', 'yes', 't', 'true', 'on'):
0462 return True
0463 elif value in ('0', 'n', 'no', 'f', 'false', 'off'):
0464 return False
0465 raise ValueError(
0466 "Could not convert option %s=%s (in [%s] to a boolean "
0467 "(use true/false)%s"
0468 % (option, value, section,
0469 self._get_location_info(section, option)))
0470
0471 def getfilename(self, section, option):
0472 """Returns the value of the option, interpreted as a filename
0473 relative to the location of where the option was defined.
0474
0475 Raises a ValueError if the option doesn't have an associated
0476 filename and the path is not absolute.
0477 """
0478 value = self.get(section, option)
0479 filename = self._key_file_positions.get(
0480 (self.sectionxform(section), self.optionxform(option)),
0481 (None, None))[0]
0482 if filename is None:
0483 if os.path.isabs(value):
0484 return value
0485 raise ValueError(
0486 "Getting relative filename [%s] option %s=%s , but there "
0487 "is no recorded config file for option"
0488 % (section, option, value))
0489 value = os.path.join(os.path.dirname(filename), value)
0490 return value
0491
0492 def items(self, section, raw=False, vars=None):
0493 """Return a list of (name, value) pairs for each option in the
0494 given section.
0495
0496 If self.percent_expand is true, then all the '%'
0497 interpolations are expanded, using the optional vars. If raw
0498 is True, then no interpolation is done.
0499 """
0500 result = []
0501 for op in self.options(section):
0502 result.append((op, self.get(section, op, raw=raw, vars=vars)))
0503 return result
0504
0505 def allitems(self, raw=False, vars=None):
0506 result = {}
0507 for section in self.sections():
0508 result[section] = self.items(section, raw=raw, vars=vars)
0509 return result
0510
0511 def set(self, section, option, value, filename=None,
0512 line_number=None, comments=None):
0513 """If the given section exists, set the given option to the
0514 specified value; otherwise raise NoSectionError.
0515
0516 While it is possible to use RawConfigParser (or ConfigParser
0517 with raw parameters set to true) for internal storage of
0518 non-string values, full functionality (including interpolation
0519 and output to files) can only be achieved using string values.
0520
0521 If self.safe_set is true, then only string values will be
0522 allowed.
0523 """
0524 if not isinstance(value, basestring) and self.safe_set:
0525 raise TypeError(
0526 "You can only set options to string values "
0527 "(you tried to set %s=%r in [%s])"
0528 % (option, value, section))
0529 sec = self.sectionxform(section)
0530 if sec not in self._values:
0531 raise NoSectionError(
0532 'There is no section [%s] (when setting option %r)'
0533 % (sec, option))
0534 op = self.optionxform(option)
0535 self._pre_normalized_keys[(sec, op)] = option
0536 if op in self._section_key_order[sec]:
0537 self._section_key_order[sec].remove(op)
0538 self._section_key_order[sec].append(op)
0539 if comments is None:
0540 if (sec, op) in self._key_comments:
0541 del self._key_comments[(sec, op)]
0542 else:
0543 self._key_comments[(sec, op)] = value
0544 if filename is None:
0545 if (sec, op) in self._key_file_positions:
0546 del self._key_file_positions[(sec, op)]
0547 else:
0548 self._key_file_positions[(sec, op)] = (filename, line_number)
0549 self._values[sec][op] = value
0550
0551 def write(self, fileobject):
0552 """Write a representation of the configuration to the
0553 specified file object.
0554
0555 This representation can be parsed by a future read() call.
0556 """
0557 f = fileobject
0558 if self.encoding:
0559
0560 f = codecs.EncodedFile(f, self.encoding)
0561 for sec in self._section_order:
0562 section = self._pre_normalized_sections[sec]
0563 comment = self._section_comments.get(sec)
0564 if comment:
0565 f.write(comment+'\n')
0566 f.write('[%s]\n' % section)
0567 for op in self._section_key_order[sec]:
0568 option = self._pre_normalized_keys[(sec, op)]
0569 comment = self._key_comments.get((sec, op))
0570 if comment:
0571 f.write(comment+'\n')
0572 f.write('%s = ' % option)
0573 lines = self._values[sec][op].splitlines()
0574 f.write(lines[0])
0575 for line in lines[1:]:
0576 f.write('\n%s%s' % (self.continuation_indent, line))
0577 f.write('\n')
0578 if comment:
0579 f.write('\n')
0580 f.write('\n')
0581
0582 def remove_option(self, section, option):
0583 """Remove the specified option from the specified section.
0584
0585 If the section does not exist, raise NoSectionError. If the
0586 option existed to be removed, return True; otherwise return
0587 False.
0588 """
0589 sec = self.sectionxform(section)
0590 if sec not in self._values:
0591 raise NoSectionError(
0592 'No section [%s] (while trying to remove %r)'
0593 % (section, option))
0594 op = self.optionxform(option)
0595 if op in self._values[sec]:
0596 del self._values[sec][op]
0597 del self._pre_normalized_keys[(sec, op)]
0598 if (sec, op) in self._key_comments:
0599 del self._key_comments[(sec, op)]
0600 if (sec, op) in self._key_file_positions:
0601 del self._key_file_positions[(sec, op)]
0602 self._section_key_order[sec].remove(op)
0603 return True
0604 else:
0605 return False
0606
0607 def remove_section(self, section):
0608 """Remove the specified section from the configuration.
0609
0610 If the section in fact existed, return True. Otherwise return
0611 False.
0612 """
0613 sec = self.sectionxform(section)
0614 if sec not in self._values:
0615 return False
0616 for key in self._pre_normalized_keys:
0617 if key[0] == sec:
0618 del self._pre_normalized_keys[key]
0619 del self._pre_normalized_sections[sec]
0620 for key in self._key_file_positions:
0621 if key[0] == sec:
0622 del self._key_file_positions[key]
0623 for key in self._key_comments:
0624 if key[0] == sec:
0625 del self._key_comments[key]
0626 self._section_order.remove(sec)
0627 del self._section_key_order[sec]
0628 if sec in self._section_comments:
0629 del self._section_comments[sec]
0630 del self._values[sec]
0631
0632 def optionxform(self, option):
0633 """Transforms the option name option as found in an input file
0634 or as passed in by client code to the form that should be used
0635 in the internal structures.
0636
0637 The default implementation returns a lower-case version of
0638 option; subclasses may override this or client code can set an
0639 attribute of this name on instances to affect this
0640 behavior. Setting this to str(), for example, would make
0641 option names case sensitive.
0642 """
0643 if not self.case_sensitive:
0644 return option.lower()
0645 else:
0646 return option
0647
0648 def sectionxform(self, option):
0649 """Transforms the section name option as found in an input file
0650 or as passed in by client code to the form that should be used
0651 in the internal structures.
0652
0653 The default implementation returns a lower-case version of
0654 option; subclasses may override this or client code can set an
0655 attribute of this name on instances to affect this
0656 behavior. Setting this to str(), for example, would make
0657 option names case sensitive.
0658 """
0659 if not self.section_case_sensitive:
0660 return option.lower()
0661 else:
0662 return option
0663
0664 def asdict(self):
0665 return self._values.copy()
0666
0667 def sectiondict(self, section):
0668 return self._values.get(self.sectionxform(section), {}).copy()
0669
0670class ConfigParser(RawConfigParser):
0671
0672 percent_expand = True
0673
0674class SafeConfigParser(ConfigParser):
0675
0676 safe_set = True
0677
0678class _ConfigParserParser(iniparser.INIParser):
0679 """
0680 This parser feeds the parsed values into a ConfigParser object
0681 """
0682
0683 def __init__(self, cp, extending, map_sections=None):
0684 self.cp = cp
0685 self.extending = extending
0686 self.map_sections = map_sections
0687 self.last_comment = None
0688 self.section = None
0689
0690 def assignment(self, name, content):
0691 if self.section is None and not self.cp.global_section:
0692 self.parse_error("Missing Section",
0693 exception=MissingSectionHeaderError)
0694 content = content.strip(' \t')
0695 if not name:
0696 self.parse_error('No name given for option')
0697 if self.cp.inline_comments:
0698 lines = content.splitlines()
0699 result = []
0700 for line in lines:
0701 semi_pos = line.find(';')
0702 hash_pos = line.find('#')
0703 if semi_pos == -1 and hash_pos == -1:
0704 comment = None
0705 elif semi_pos == -1:
0706 line, comment = line.split('#', 1)
0707 elif hash_pos == -1:
0708 line, comment = line.split(';', 1)
0709 elif hash_pos < semi_pos:
0710 line, comment = line.split('#', 1)
0711 else:
0712 line, comment = line.split(';', 1)
0713 if comment is not None:
0714 line = line.rstrip()
0715 comment = comment.lstrip()
0716 self.add_comment(comment)
0717 result.append(line)
0718 content = '\n'.join(result)
0719 if self.extending:
0720 if self.cp.has_option(self.section, name):
0721
0722 return
0723 if self.map_sections is not None:
0724 if self.section in self.map_sections:
0725 section = self.map_sections[self.section]
0726 else:
0727 return
0728 else:
0729 section = self.section
0730 self.cp.set(
0731 section, name, content,
0732 filename=self.filename,
0733 line_number=self.lineno,
0734 comments=self.last_comment)
0735 self.last_comment = None
0736
0737 def new_section(self, section):
0738 if not self.cp.has_section(section):
0739 self.cp.add_section(section, comment=self.last_comment)
0740 self.last_comment = None
0741 if not section:
0742 self.parse_error('Empty section name ([])')
0743 self.section = section
0744
0745 def add_comment(self, comment):
0746 if self.last_comment is None:
0747 self.last_comment = comment
0748 else:
0749 self.last_comment = self.last_comment + '\n' + comment
0750
0751
0752class _OptionWrapper(DictMixin):
0753
0754 """
0755 This produces the dictionary used for percent substitution in
0756 values.
0757
0758 Substitution is recursive.
0759 """
0760
0761 def __init__(self, parser, extra_vars, section, option,
0762 _recursion):
0763 self.parser = parser
0764 self.extra_vars = extra_vars
0765 self.normal_extra_vars = {}
0766 for name, value in extra_vars.iteritems():
0767 self.normal_extra_vars[parser.optionxform(name)] = value
0768 self.section = section
0769 self.option = option
0770 self._recursion = _recursion
0771
0772 def __getitem__(self, item):
0773 if item == '__name__':
0774 return self.section
0775 item = self.parser.optionxform(item)
0776 if item in self.normal_extra_vars:
0777 return self.normal_extra_vars[item]
0778 if self.parser.has_option(self.section, item):
0779 return self.parser.get(self.section, item,
0780 vars=self.extra_vars,
0781 _recursion=self._recursion+1)
0782 if item in self.parser.defaults():
0783 return self.parser.defaults()[item]
0784 raise KeyError(item)
0785
0786class _SectionOptionWrapper(_OptionWrapper):
0787 """
0788 This provides the dict wrapper for dollar substitution. Unlike
0789 percent substitution, you can reference values from arbitrary
0790 sections.
0791 """
0792
0793 def __getitem__(self, item):
0794 if ':' in item:
0795
0796 section, item = item.split(':', 1)
0797 return self.parser.get(section, item, vars=self.extra_vars,
0798 _recursion=self._recursion+1)
0799 return _OptionWrapper.__getitem__(self, item)