Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/backports/configparser/__init__.py: 52%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

724 statements  

1"""Configuration file parser. 

2 

3A configuration file consists of sections, lead by a "[section]" header, 

4and followed by "name: value" entries, with continuations and such in 

5the style of RFC 822. 

6 

7Intrinsic defaults can be specified by passing them into the 

8ConfigParser constructor as a dictionary. 

9 

10class: 

11 

12ConfigParser -- responsible for parsing a list of 

13 configuration files, and managing the parsed database. 

14 

15 methods: 

16 

17 __init__(defaults=None, dict_type=_default_dict, allow_no_value=False, 

18 delimiters=('=', ':'), comment_prefixes=('#', ';'), 

19 inline_comment_prefixes=None, strict=True, 

20 empty_lines_in_values=True, default_section='DEFAULT', 

21 interpolation=<unset>, converters=<unset>, 

22 allow_unnamed_section=False): 

23 Create the parser. When `defaults` is given, it is initialized into the 

24 dictionary or intrinsic defaults. The keys must be strings, the values 

25 must be appropriate for %()s string interpolation. 

26 

27 When `dict_type` is given, it will be used to create the dictionary 

28 objects for the list of sections, for the options within a section, and 

29 for the default values. 

30 

31 When `delimiters` is given, it will be used as the set of substrings 

32 that divide keys from values. 

33 

34 When `comment_prefixes` is given, it will be used as the set of 

35 substrings that prefix comments in empty lines. Comments can be 

36 indented. 

37 

38 When `inline_comment_prefixes` is given, it will be used as the set of 

39 substrings that prefix comments in non-empty lines. 

40 

41 When `strict` is True, the parser won't allow for any section or option 

42 duplicates while reading from a single source (file, string or 

43 dictionary). Default is True. 

44 

45 When `empty_lines_in_values` is False (default: True), each empty line 

46 marks the end of an option. Otherwise, internal empty lines of 

47 a multiline option are kept as part of the value. 

48 

49 When `allow_no_value` is True (default: False), options without 

50 values are accepted; the value presented for these is None. 

51 

52 When `default_section` is given, the name of the special section is 

53 named accordingly. By default it is called ``"DEFAULT"`` but this can 

54 be customized to point to any other valid section name. Its current 

55 value can be retrieved using the ``parser_instance.default_section`` 

56 attribute and may be modified at runtime. 

57 

58 When `interpolation` is given, it should be an Interpolation subclass 

59 instance. It will be used as the handler for option value 

60 pre-processing when using getters. RawConfigParser objects don't do 

61 any sort of interpolation, whereas ConfigParser uses an instance of 

62 BasicInterpolation. The library also provides a ``zc.buildout`` 

63 inspired ExtendedInterpolation implementation. 

64 

65 When `converters` is given, it should be a dictionary where each key 

66 represents the name of a type converter and each value is a callable 

67 implementing the conversion from string to the desired datatype. Every 

68 converter gets its corresponding get*() method on the parser object and 

69 section proxies. 

70 

71 When `allow_unnamed_section` is True (default: False), options 

72 without section are accepted: the section for these is 

73 ``configparser.UNNAMED_SECTION``. 

74 

75 sections() 

76 Return all the configuration section names, sans DEFAULT. 

77 

78 has_section(section) 

79 Return whether the given section exists. 

80 

81 has_option(section, option) 

82 Return whether the given option exists in the given section. 

83 

84 options(section) 

85 Return list of configuration options for the named section. 

86 

87 read(filenames, encoding=None) 

88 Read and parse the iterable of named configuration files, given by 

89 name. A single filename is also allowed. Non-existing files 

90 are ignored. Return list of successfully read files. 

91 

92 read_file(f, filename=None) 

93 Read and parse one configuration file, given as a file object. 

94 The filename defaults to f.name; it is only used in error 

95 messages (if f has no `name` attribute, the string `<???>` is used). 

96 

97 read_string(string) 

98 Read configuration from a given string. 

99 

100 read_dict(dictionary) 

101 Read configuration from a dictionary. Keys are section names, 

102 values are dictionaries with keys and values that should be present 

103 in the section. If the used dictionary type preserves order, sections 

104 and their keys will be added in order. Values are automatically 

105 converted to strings. 

106 

107 get(section, option, raw=False, vars=None, fallback=_UNSET) 

108 Return a string value for the named option. All % interpolations are 

109 expanded in the return values, based on the defaults passed into the 

110 constructor and the DEFAULT section. Additional substitutions may be 

111 provided using the `vars` argument, which must be a dictionary whose 

112 contents override any pre-existing defaults. If `option` is a key in 

113 `vars`, the value from `vars` is used. 

114 

115 getint(section, options, raw=False, vars=None, fallback=_UNSET) 

116 Like get(), but convert value to an integer. 

117 

118 getfloat(section, options, raw=False, vars=None, fallback=_UNSET) 

119 Like get(), but convert value to a float. 

120 

121 getboolean(section, options, raw=False, vars=None, fallback=_UNSET) 

122 Like get(), but convert value to a boolean (currently case 

123 insensitively defined as 0, false, no, off for False, and 1, true, 

124 yes, on for True). Returns False or True. 

125 

126 items(section=_UNSET, raw=False, vars=None) 

127 If section is given, return a list of tuples with (name, value) for 

128 each option in the section. Otherwise, return a list of tuples with 

129 (section_name, section_proxy) for each section, including DEFAULTSECT. 

130 

131 remove_section(section) 

132 Remove the given file section and all its options. 

133 

134 remove_option(section, option) 

135 Remove the given option from the given section. 

136 

137 set(section, option, value) 

138 Set the given option. 

139 

140 write(fp, space_around_delimiters=True) 

141 Write the configuration state in .ini format. If 

142 `space_around_delimiters` is True (the default), delimiters 

143 between keys and values are surrounded by spaces. 

144""" 

145 

146from __future__ import annotations 

147 

148import contextlib 

149import functools 

150import io 

151import itertools 

152import os 

153import re 

154import sys 

155from collections import ChainMap as _ChainMap 

156 

157# Do not import dataclasses; overhead is unacceptable (gh-117703) 

158from collections.abc import Iterable, MutableMapping 

159 

160__all__ = ( 

161 "NoSectionError", 

162 "DuplicateOptionError", 

163 "DuplicateSectionError", 

164 "NoOptionError", 

165 "InterpolationError", 

166 "InterpolationDepthError", 

167 "InterpolationMissingOptionError", 

168 "InterpolationSyntaxError", 

169 "ParsingError", 

170 "MissingSectionHeaderError", 

171 "MultilineContinuationError", 

172 "UnnamedSectionDisabledError", 

173 "InvalidWriteError", 

174 "ConfigParser", 

175 "RawConfigParser", 

176 "Interpolation", 

177 "BasicInterpolation", 

178 "ExtendedInterpolation", 

179 "SectionProxy", 

180 "ConverterMapping", 

181 "DEFAULTSECT", 

182 "MAX_INTERPOLATION_DEPTH", 

183 "UNNAMED_SECTION", 

184) 

185 

186_default_dict = dict 

187DEFAULTSECT = "DEFAULT" 

188 

189MAX_INTERPOLATION_DEPTH = 10 

190 

191 

192# exception classes 

193class Error(Exception): 

194 """Base class for ConfigParser exceptions.""" 

195 

196 def __init__(self, msg=''): 

197 self.message = msg 

198 Exception.__init__(self, msg) 

199 

200 def __repr__(self): 

201 return self.message 

202 

203 __str__ = __repr__ 

204 

205 

206class NoSectionError(Error): 

207 """Raised when no section matches a requested option.""" 

208 

209 def __init__(self, section): 

210 Error.__init__(self, 'No section: %r' % (section,)) 

211 self.section = section 

212 self.args = (section,) 

213 

214 

215class DuplicateSectionError(Error): 

216 """Raised when a section is repeated in an input source. 

217 

218 Possible repetitions that raise this exception are: multiple creation 

219 using the API or in strict parsers when a section is found more than once 

220 in a single input file, string or dictionary. 

221 """ 

222 

223 def __init__(self, section, source=None, lineno=None): 

224 msg = [repr(section), " already exists"] 

225 if source is not None: 

226 message = ["While reading from ", repr(source)] 

227 if lineno is not None: 

228 message.append(" [line {0:2d}]".format(lineno)) 

229 message.append(": section ") 

230 message.extend(msg) 

231 msg = message 

232 else: 

233 msg.insert(0, "Section ") 

234 Error.__init__(self, "".join(msg)) 

235 self.section = section 

236 self.source = source 

237 self.lineno = lineno 

238 self.args = (section, source, lineno) 

239 

240 

241class DuplicateOptionError(Error): 

242 """Raised by strict parsers when an option is repeated in an input source. 

243 

244 Current implementation raises this exception only when an option is found 

245 more than once in a single file, string or dictionary. 

246 """ 

247 

248 def __init__(self, section, option, source=None, lineno=None): 

249 msg = [repr(option), " in section ", repr(section), " already exists"] 

250 if source is not None: 

251 message = ["While reading from ", repr(source)] 

252 if lineno is not None: 

253 message.append(" [line {0:2d}]".format(lineno)) 

254 message.append(": option ") 

255 message.extend(msg) 

256 msg = message 

257 else: 

258 msg.insert(0, "Option ") 

259 Error.__init__(self, "".join(msg)) 

260 self.section = section 

261 self.option = option 

262 self.source = source 

263 self.lineno = lineno 

264 self.args = (section, option, source, lineno) 

265 

266 

267class NoOptionError(Error): 

268 """A requested option was not found.""" 

269 

270 def __init__(self, option, section): 

271 Error.__init__(self, "No option %r in section: %r" % (option, section)) 

272 self.option = option 

273 self.section = section 

274 self.args = (option, section) 

275 

276 

277class InterpolationError(Error): 

278 """Base class for interpolation-related exceptions.""" 

279 

280 def __init__(self, option, section, msg): 

281 Error.__init__(self, msg) 

282 self.option = option 

283 self.section = section 

284 self.args = (option, section, msg) 

285 

286 

287class InterpolationMissingOptionError(InterpolationError): 

288 """A string substitution required a setting which was not available.""" 

289 

290 def __init__(self, option, section, rawval, reference): 

291 msg = ( 

292 "Bad value substitution: option {!r} in section {!r} contains " 

293 "an interpolation key {!r} which is not a valid option name. " 

294 "Raw value: {!r}".format(option, section, reference, rawval) 

295 ) 

296 InterpolationError.__init__(self, option, section, msg) 

297 self.reference = reference 

298 self.args = (option, section, rawval, reference) 

299 

300 

301class InterpolationSyntaxError(InterpolationError): 

302 """Raised when the source text contains invalid syntax. 

303 

304 Current implementation raises this exception when the source text into 

305 which substitutions are made does not conform to the required syntax. 

306 """ 

307 

308 

309class InterpolationDepthError(InterpolationError): 

310 """Raised when substitutions are nested too deeply.""" 

311 

312 def __init__(self, option, section, rawval): 

313 msg = ( 

314 "Recursion limit exceeded in value substitution: option {!r} " 

315 "in section {!r} contains an interpolation key which " 

316 "cannot be substituted in {} steps. Raw value: {!r}" 

317 "".format(option, section, MAX_INTERPOLATION_DEPTH, rawval) 

318 ) 

319 InterpolationError.__init__(self, option, section, msg) 

320 self.args = (option, section, rawval) 

321 

322 

323class ParsingError(Error): 

324 """Raised when a configuration file does not follow legal syntax.""" 

325 

326 def __init__(self, source, *args): 

327 super().__init__(f'Source contains parsing errors: {source!r}') 

328 self.source = source 

329 self.errors = [] 

330 self.args = (source,) 

331 if args: 

332 self.append(*args) 

333 

334 def append(self, lineno, line): 

335 self.errors.append((lineno, line)) 

336 self.message += f'\n\t[line {lineno:2d}]: {line!r}' 

337 

338 def combine(self, others): 

339 messages = [self.message] 

340 for other in others: 

341 for lineno, line in other.errors: 

342 self.errors.append((lineno, line)) 

343 messages.append(f'\n\t[line {lineno:2d}]: {line!r}') 

344 self.message = "".join(messages) 

345 return self 

346 

347 @staticmethod 

348 def _raise_all(exceptions: Iterable[ParsingError]): 

349 """ 

350 Combine any number of ParsingErrors into one and raise it. 

351 """ 

352 exceptions = iter(exceptions) 

353 with contextlib.suppress(StopIteration): 

354 raise next(exceptions).combine(exceptions) 

355 

356 

357class MissingSectionHeaderError(ParsingError): 

358 """Raised when a key-value pair is found before any section header.""" 

359 

360 def __init__(self, filename, lineno, line): 

361 Error.__init__( 

362 self, 

363 'File contains no section headers.\nfile: %r, line: %d\n%r' 

364 % (filename, lineno, line), 

365 ) 

366 self.source = filename 

367 self.lineno = lineno 

368 self.line = line 

369 self.args = (filename, lineno, line) 

370 

371 

372class MultilineContinuationError(ParsingError): 

373 """Raised when a key without value is followed by continuation line""" 

374 

375 def __init__(self, filename, lineno, line): 

376 Error.__init__( 

377 self, 

378 "Key without value continued with an indented line.\n" 

379 "file: %r, line: %d\n%r" % (filename, lineno, line), 

380 ) 

381 self.source = filename 

382 self.lineno = lineno 

383 self.line = line 

384 self.args = (filename, lineno, line) 

385 

386 

387class UnnamedSectionDisabledError(Error): 

388 """Raised when an attempt to use UNNAMED_SECTION is made with the 

389 feature disabled.""" 

390 

391 def __init__(self): 

392 Error.__init__(self, "Support for UNNAMED_SECTION is disabled.") 

393 

394 

395class _UnnamedSection: 

396 def __repr__(self): 

397 return "<UNNAMED_SECTION>" 

398 

399 

400class InvalidWriteError(Error): 

401 """Raised when attempting to write data that the parser would read back differently. 

402 ex: writing a key which begins with the section header pattern would read back as a 

403 new section""" 

404 

405 def __init__(self, msg=''): 

406 Error.__init__(self, msg) 

407 

408 

409UNNAMED_SECTION = _UnnamedSection() 

410 

411 

412# Used in parser getters to indicate the default behaviour when a specific 

413# option is not found it to raise an exception. Created to enable `None` as 

414# a valid fallback value. 

415_UNSET = object() 

416 

417 

418class Interpolation: 

419 """Dummy interpolation that passes the value through with no changes.""" 

420 

421 def before_get(self, parser, section, option, value, defaults): 

422 return value 

423 

424 def before_set(self, parser, section, option, value): 

425 return value 

426 

427 def before_read(self, parser, section, option, value): 

428 return value 

429 

430 def before_write(self, parser, section, option, value): 

431 return value 

432 

433 

434class BasicInterpolation(Interpolation): 

435 """Interpolation as implemented in the classic ConfigParser. 

436 

437 The option values can contain format strings which refer to other values in 

438 the same section, or values in the special default section. 

439 

440 For example: 

441 

442 something: %(dir)s/whatever 

443 

444 would resolve the "%(dir)s" to the value of dir. All reference 

445 expansions are done late, on demand. If a user needs to use a bare % in 

446 a configuration file, she can escape it by writing %%. Other % usage 

447 is considered a user error and raises `InterpolationSyntaxError`.""" 

448 

449 _KEYCRE = re.compile(r"%\(([^)]+)\)s") 

450 

451 def before_get(self, parser, section, option, value, defaults): 

452 L = [] 

453 self._interpolate_some(parser, option, L, value, section, defaults, 1) 

454 return ''.join(L) 

455 

456 def before_set(self, parser, section, option, value): 

457 tmp_value = value.replace('%%', '') # escaped percent signs 

458 tmp_value = self._KEYCRE.sub('', tmp_value) # valid syntax 

459 if '%' in tmp_value: 

460 raise ValueError( 

461 "invalid interpolation syntax in %r at " 

462 "position %d" % (value, tmp_value.find('%')) 

463 ) 

464 return value 

465 

466 def _interpolate_some(self, parser, option, accum, rest, section, map, depth): 

467 rawval = parser.get(section, option, raw=True, fallback=rest) 

468 if depth > MAX_INTERPOLATION_DEPTH: 

469 raise InterpolationDepthError(option, section, rawval) 

470 while rest: 

471 p = rest.find("%") 

472 if p < 0: 

473 accum.append(rest) 

474 return 

475 if p > 0: 

476 accum.append(rest[:p]) 

477 rest = rest[p:] 

478 # p is no longer used 

479 c = rest[1:2] 

480 if c == "%": 

481 accum.append("%") 

482 rest = rest[2:] 

483 elif c == "(": 

484 m = self._KEYCRE.match(rest) 

485 if m is None: 

486 raise InterpolationSyntaxError( 

487 option, 

488 section, 

489 "bad interpolation variable reference %r" % rest, 

490 ) 

491 var = parser.optionxform(m.group(1)) 

492 rest = rest[m.end() :] 

493 try: 

494 v = map[var] 

495 except KeyError: 

496 raise InterpolationMissingOptionError( 

497 option, section, rawval, var 

498 ) from None 

499 if "%" in v: 

500 self._interpolate_some( 

501 parser, option, accum, v, section, map, depth + 1 

502 ) 

503 else: 

504 accum.append(v) 

505 else: 

506 raise InterpolationSyntaxError( 

507 option, 

508 section, 

509 "'%%' must be followed by '%%' or '(', found: %r" % (rest,), 

510 ) 

511 

512 

513class ExtendedInterpolation(Interpolation): 

514 """Advanced variant of interpolation, supports the syntax used by 

515 `zc.buildout`. Enables interpolation between sections.""" 

516 

517 _KEYCRE = re.compile(r"\$\{([^}]+)\}") 

518 

519 def before_get(self, parser, section, option, value, defaults): 

520 L = [] 

521 self._interpolate_some(parser, option, L, value, section, defaults, 1) 

522 return ''.join(L) 

523 

524 def before_set(self, parser, section, option, value): 

525 tmp_value = value.replace('$$', '') # escaped dollar signs 

526 tmp_value = self._KEYCRE.sub('', tmp_value) # valid syntax 

527 if '$' in tmp_value: 

528 raise ValueError( 

529 "invalid interpolation syntax in %r at " 

530 "position %d" % (value, tmp_value.find('$')) 

531 ) 

532 return value 

533 

534 def _interpolate_some( # noqa: C901 

535 self, parser, option, accum, rest, section, map, depth 

536 ): 

537 rawval = parser.get(section, option, raw=True, fallback=rest) 

538 if depth > MAX_INTERPOLATION_DEPTH: 

539 raise InterpolationDepthError(option, section, rawval) 

540 while rest: 

541 p = rest.find("$") 

542 if p < 0: 

543 accum.append(rest) 

544 return 

545 if p > 0: 

546 accum.append(rest[:p]) 

547 rest = rest[p:] 

548 # p is no longer used 

549 c = rest[1:2] 

550 if c == "$": 

551 accum.append("$") 

552 rest = rest[2:] 

553 elif c == "{": 

554 m = self._KEYCRE.match(rest) 

555 if m is None: 

556 raise InterpolationSyntaxError( 

557 option, 

558 section, 

559 "bad interpolation variable reference %r" % rest, 

560 ) 

561 path = m.group(1).split(':') 

562 rest = rest[m.end() :] 

563 sect = section 

564 opt = option 

565 try: 

566 if len(path) == 1: 

567 opt = parser.optionxform(path[0]) 

568 v = map[opt] 

569 elif len(path) == 2: 

570 sect = path[0] 

571 opt = parser.optionxform(path[1]) 

572 v = parser.get(sect, opt, raw=True) 

573 else: 

574 raise InterpolationSyntaxError( 

575 option, section, "More than one ':' found: %r" % (rest,) 

576 ) 

577 except (KeyError, NoSectionError, NoOptionError): 

578 raise InterpolationMissingOptionError( 

579 option, section, rawval, ":".join(path) 

580 ) from None 

581 if v is None: 

582 continue 

583 if "$" in v: 

584 self._interpolate_some( 

585 parser, 

586 opt, 

587 accum, 

588 v, 

589 sect, 

590 dict(parser.items(sect, raw=True)), 

591 depth + 1, 

592 ) 

593 else: 

594 accum.append(v) 

595 else: 

596 raise InterpolationSyntaxError( 

597 option, 

598 section, 

599 "'$' must be followed by '$' or '{', found: %r" % (rest,), 

600 ) 

601 

602 

603class _ReadState: 

604 elements_added: set[str] 

605 cursect: dict[str, str] | None = None 

606 sectname: str | None = None 

607 optname: str | None = None 

608 lineno: int = 0 

609 indent_level: int = 0 

610 errors: list[ParsingError] 

611 

612 def __init__(self): 

613 self.elements_added = set() 

614 self.errors = list() 

615 

616 

617class _Line(str): 

618 __slots__ = 'clean', 'has_comments' 

619 

620 def __new__(cls, val, *args, **kwargs): 

621 return super().__new__(cls, val) 

622 

623 def __init__(self, val, comments): 

624 trimmed = val.strip() 

625 self.clean = comments.strip(trimmed) 

626 self.has_comments = trimmed != self.clean 

627 

628 

629class _CommentSpec: 

630 def __init__(self, full_prefixes, inline_prefixes): 

631 full_patterns = ( 

632 # prefix at the beginning of a line 

633 rf'^({re.escape(prefix)}).*' 

634 for prefix in full_prefixes 

635 ) 

636 inline_patterns = ( 

637 # prefix at the beginning of the line or following a space 

638 rf'(^|\s)({re.escape(prefix)}.*)' 

639 for prefix in inline_prefixes 

640 ) 

641 self.pattern = re.compile( 

642 '|'.join(itertools.chain(full_patterns, inline_patterns)) 

643 ) 

644 

645 def strip(self, text): 

646 return self.pattern.sub('', text).rstrip() 

647 

648 def wrap(self, text): 

649 return _Line(text, self) 

650 

651 

652class RawConfigParser(MutableMapping): 

653 """ConfigParser that does not do interpolation.""" 

654 

655 # Regular expressions for parsing section headers and options 

656 _SECT_TMPL = r""" 

657 \[ # [ 

658 (?P<header>.+) # very permissive! 

659 \] # ] 

660 """ 

661 _OPT_TMPL = r""" 

662 (?P<option> # very permissive! 

663 (?:(?!{delim})\S)* # non-delimiter non-whitespace 

664 (?:(?:(?!{delim})\s)+ # optionally more 

665 (?:(?!{delim})\S)+)*) # space-separated words 

666 \s*(?P<vi>{delim})\s* # any number of space/tab, 

667 # followed by any of the 

668 # allowed delimiters, 

669 # followed by any space/tab 

670 (?P<value>.*)$ # everything up to eol 

671 """ 

672 _OPT_NV_TMPL = r""" 

673 (?P<option> # very permissive! 

674 (?:(?!{delim})\S)* # non-delimiter non-whitespace 

675 (?:(?:(?!{delim})\s)+ # optionally more 

676 (?:(?!{delim})\S)+)*) # space-separated words 

677 \s*(?: # any number of space/tab, 

678 (?P<vi>{delim})\s* # optionally followed by 

679 # any of the allowed 

680 # delimiters, followed by any 

681 # space/tab 

682 (?P<value>.*))?$ # everything up to eol 

683 """ 

684 # Interpolation algorithm to be used if the user does not specify another 

685 _DEFAULT_INTERPOLATION = Interpolation() 

686 # Compiled regular expression for matching sections 

687 SECTCRE = re.compile(_SECT_TMPL, re.VERBOSE) 

688 # Compiled regular expression for matching options with typical separators 

689 OPTCRE = re.compile(_OPT_TMPL.format(delim="=|:"), re.VERBOSE) 

690 # Compiled regular expression for matching options with optional values 

691 # delimited using typical separators 

692 OPTCRE_NV = re.compile(_OPT_NV_TMPL.format(delim="=|:"), re.VERBOSE) 

693 # Compiled regular expression for matching leading whitespace in a line 

694 NONSPACECRE = re.compile(r"\S") 

695 # Possible boolean values in the configuration. 

696 BOOLEAN_STATES = { 

697 '1': True, 

698 'yes': True, 

699 'true': True, 

700 'on': True, 

701 '0': False, 

702 'no': False, 

703 'false': False, 

704 'off': False, 

705 } 

706 

707 def __init__( 

708 self, 

709 defaults=None, 

710 dict_type=_default_dict, 

711 allow_no_value=False, 

712 *, 

713 delimiters=('=', ':'), 

714 comment_prefixes=('#', ';'), 

715 inline_comment_prefixes=None, 

716 strict=True, 

717 empty_lines_in_values=True, 

718 default_section=DEFAULTSECT, 

719 interpolation=_UNSET, 

720 converters=_UNSET, 

721 allow_unnamed_section=False, 

722 ): 

723 self._dict = dict_type 

724 self._sections = self._dict() 

725 self._defaults = self._dict() 

726 self._converters = ConverterMapping(self) 

727 self._proxies = self._dict() 

728 self._proxies[default_section] = SectionProxy(self, default_section) 

729 self._delimiters = tuple(delimiters) 

730 if delimiters == ('=', ':'): 

731 self._optcre = self.OPTCRE_NV if allow_no_value else self.OPTCRE 

732 else: 

733 d = "|".join(re.escape(d) for d in delimiters) 

734 if allow_no_value: 

735 self._optcre = re.compile(self._OPT_NV_TMPL.format(delim=d), re.VERBOSE) 

736 else: 

737 self._optcre = re.compile(self._OPT_TMPL.format(delim=d), re.VERBOSE) 

738 self._comments = _CommentSpec( 

739 comment_prefixes or (), inline_comment_prefixes or () 

740 ) 

741 self._strict = strict 

742 self._allow_no_value = allow_no_value 

743 self._empty_lines_in_values = empty_lines_in_values 

744 self.default_section = default_section 

745 self._interpolation = interpolation 

746 if self._interpolation is _UNSET: 

747 self._interpolation = self._DEFAULT_INTERPOLATION 

748 if self._interpolation is None: 

749 self._interpolation = Interpolation() 

750 if not isinstance(self._interpolation, Interpolation): 

751 raise TypeError( 

752 f"interpolation= must be None or an instance of Interpolation;" 

753 f" got an object of type {type(self._interpolation)}" 

754 ) 

755 if converters is not _UNSET: 

756 self._converters.update(converters) 

757 if defaults: 

758 self._read_defaults(defaults) 

759 self._allow_unnamed_section = allow_unnamed_section 

760 

761 def defaults(self): 

762 return self._defaults 

763 

764 def sections(self): 

765 """Return a list of section names, excluding [DEFAULT]""" 

766 # self._sections will never have [DEFAULT] in it 

767 return list(self._sections.keys()) 

768 

769 def add_section(self, section): 

770 """Create a new section in the configuration. 

771 

772 Raise DuplicateSectionError if a section by the specified name 

773 already exists. Raise ValueError if name is DEFAULT. 

774 """ 

775 if section == self.default_section: 

776 raise ValueError('Invalid section name: %r' % section) 

777 

778 if section is UNNAMED_SECTION: 

779 if not self._allow_unnamed_section: 

780 raise UnnamedSectionDisabledError 

781 

782 if section in self._sections: 

783 raise DuplicateSectionError(section) 

784 self._sections[section] = self._dict() 

785 self._proxies[section] = SectionProxy(self, section) 

786 

787 def has_section(self, section): 

788 """Indicate whether the named section is present in the configuration. 

789 

790 The DEFAULT section is not acknowledged. 

791 """ 

792 return section in self._sections 

793 

794 def options(self, section): 

795 """Return a list of option names for the given section name.""" 

796 try: 

797 opts = self._sections[section].copy() 

798 except KeyError: 

799 raise NoSectionError(section) from None 

800 opts.update(self._defaults) 

801 return list(opts.keys()) 

802 

803 def read(self, filenames, encoding=None): 

804 """Read and parse a filename or an iterable of filenames. 

805 

806 Files that cannot be opened are silently ignored; this is 

807 designed so that you can specify an iterable of potential 

808 configuration file locations (e.g. current directory, user's 

809 home directory, systemwide directory), and all existing 

810 configuration files in the iterable will be read. A single 

811 filename may also be given. 

812 

813 Return list of successfully read files. 

814 """ 

815 if isinstance(filenames, (str, bytes, os.PathLike)): 

816 filenames = [filenames] 

817 encoding = io.text_encoding(encoding) 

818 read_ok = [] 

819 for filename in filenames: 

820 try: 

821 with open(filename, encoding=encoding) as fp: 

822 self._read(fp, filename) 

823 except OSError: 

824 continue 

825 if isinstance(filename, os.PathLike): 

826 filename = os.fspath(filename) 

827 read_ok.append(filename) 

828 return read_ok 

829 

830 def read_file(self, f, source=None): 

831 """Like read() but the argument must be a file-like object. 

832 

833 The `f` argument must be iterable, returning one line at a time. 

834 Optional second argument is the `source` specifying the name of the 

835 file being read. If not given, it is taken from f.name. If `f` has no 

836 `name` attribute, `<???>` is used. 

837 """ 

838 if source is None: 

839 try: 

840 source = f.name 

841 except AttributeError: 

842 source = '<???>' 

843 self._read(f, source) 

844 

845 def read_string(self, string, source='<string>'): 

846 """Read configuration from a given string.""" 

847 sfile = io.StringIO(string) 

848 self.read_file(sfile, source) 

849 

850 def read_dict(self, dictionary, source='<dict>'): 

851 """Read configuration from a dictionary. 

852 

853 Keys are section names, values are dictionaries with keys and values 

854 that should be present in the section. If the used dictionary type 

855 preserves order, sections and their keys will be added in order. 

856 

857 All types held in the dictionary are converted to strings during 

858 reading, including section names, option names and keys. 

859 

860 Optional second argument is the `source` specifying the name of the 

861 dictionary being read. 

862 """ 

863 elements_added = set() 

864 for section, keys in dictionary.items(): 

865 if section is not UNNAMED_SECTION: 

866 section = str(section) 

867 try: 

868 self.add_section(section) 

869 except (DuplicateSectionError, ValueError): 

870 if self._strict and section in elements_added: 

871 raise 

872 elements_added.add(section) 

873 for key, value in keys.items(): 

874 key = self.optionxform(str(key)) 

875 if value is not None: 

876 value = str(value) 

877 if self._strict and (section, key) in elements_added: 

878 raise DuplicateOptionError(section, key, source) 

879 elements_added.add((section, key)) 

880 self.set(section, key, value) 

881 

882 def get(self, section, option, *, raw=False, vars=None, fallback=_UNSET): 

883 """Get an option value for a given section. 

884 

885 If `vars` is provided, it must be a dictionary. The option is looked up 

886 in `vars` (if provided), `section`, and in `DEFAULTSECT` in that order. 

887 If the key is not found and `fallback` is provided, it is used as 

888 a fallback value. `None` can be provided as a `fallback` value. 

889 

890 If interpolation is enabled and the optional argument `raw` is False, 

891 all interpolations are expanded in the return values. 

892 

893 Arguments `raw`, `vars`, and `fallback` are keyword only. 

894 

895 The section DEFAULT is special. 

896 """ 

897 try: 

898 d = self._unify_values(section, vars) 

899 except NoSectionError: 

900 if fallback is _UNSET: 

901 raise 

902 else: 

903 return fallback 

904 option = self.optionxform(option) 

905 try: 

906 value = d[option] 

907 except KeyError: 

908 if fallback is _UNSET: 

909 raise NoOptionError(option, section) 

910 else: 

911 return fallback 

912 

913 if raw or value is None: 

914 return value 

915 else: 

916 return self._interpolation.before_get(self, section, option, value, d) 

917 

918 def _get(self, section, conv, option, **kwargs): 

919 return conv(self.get(section, option, **kwargs)) 

920 

921 def _get_conv( 

922 self, section, option, conv, *, raw=False, vars=None, fallback=_UNSET, **kwargs 

923 ): 

924 try: 

925 return self._get(section, conv, option, raw=raw, vars=vars, **kwargs) 

926 except (NoSectionError, NoOptionError): 

927 if fallback is _UNSET: 

928 raise 

929 return fallback 

930 

931 # getint, getfloat and getboolean provided directly for backwards compat 

932 def getint( 

933 self, section, option, *, raw=False, vars=None, fallback=_UNSET, **kwargs 

934 ): 

935 return self._get_conv( 

936 section, option, int, raw=raw, vars=vars, fallback=fallback, **kwargs 

937 ) 

938 

939 def getfloat( 

940 self, section, option, *, raw=False, vars=None, fallback=_UNSET, **kwargs 

941 ): 

942 return self._get_conv( 

943 section, option, float, raw=raw, vars=vars, fallback=fallback, **kwargs 

944 ) 

945 

946 def getboolean( 

947 self, section, option, *, raw=False, vars=None, fallback=_UNSET, **kwargs 

948 ): 

949 return self._get_conv( 

950 section, 

951 option, 

952 self._convert_to_boolean, 

953 raw=raw, 

954 vars=vars, 

955 fallback=fallback, 

956 **kwargs, 

957 ) 

958 

959 def items(self, section=_UNSET, raw=False, vars=None): 

960 """Return a list of (name, value) tuples for each option in a section. 

961 

962 All % interpolations are expanded in the return values, based on the 

963 defaults passed into the constructor, unless the optional argument 

964 `raw` is true. Additional substitutions may be provided using the 

965 `vars` argument, which must be a dictionary whose contents overrides 

966 any pre-existing defaults. 

967 

968 The section DEFAULT is special. 

969 """ 

970 if section is _UNSET: 

971 return super(RawConfigParser, self).items() 

972 d = self._defaults.copy() 

973 try: 

974 d.update(self._sections[section]) 

975 except KeyError: 

976 if section != self.default_section: 

977 raise NoSectionError(section) 

978 orig_keys = list(d.keys()) 

979 # Update with the entry specific variables 

980 if vars: 

981 for key, value in vars.items(): 

982 d[self.optionxform(key)] = value 

983 

984 def value_getter_interp(option): 

985 return self._interpolation.before_get(self, section, option, d[option], d) 

986 

987 def value_getter_raw(option): 

988 return d[option] 

989 

990 value_getter = value_getter_raw if raw else value_getter_interp 

991 

992 return [(option, value_getter(option)) for option in orig_keys] 

993 

994 def popitem(self): 

995 """Remove a section from the parser and return it as 

996 a (section_name, section_proxy) tuple. If no section is present, raise 

997 KeyError. 

998 

999 The section DEFAULT is never returned because it cannot be removed. 

1000 """ 

1001 for key in self.sections(): 

1002 value = self[key] 

1003 del self[key] 

1004 return key, value 

1005 raise KeyError 

1006 

1007 def optionxform(self, optionstr): 

1008 return optionstr.lower() 

1009 

1010 def has_option(self, section, option): 

1011 """Check for the existence of a given option in a given section. 

1012 If the specified `section` is None or an empty string, DEFAULT is 

1013 assumed. If the specified `section` does not exist, returns False.""" 

1014 if not section or section == self.default_section: 

1015 option = self.optionxform(option) 

1016 return option in self._defaults 

1017 elif section not in self._sections: 

1018 return False 

1019 else: 

1020 option = self.optionxform(option) 

1021 return option in self._sections[section] or option in self._defaults 

1022 

1023 def set(self, section, option, value=None): 

1024 """Set an option.""" 

1025 if value: 

1026 value = self._interpolation.before_set(self, section, option, value) 

1027 if not section or section == self.default_section: 

1028 sectdict = self._defaults 

1029 else: 

1030 try: 

1031 sectdict = self._sections[section] 

1032 except KeyError: 

1033 raise NoSectionError(section) from None 

1034 sectdict[self.optionxform(option)] = value 

1035 

1036 def write(self, fp, space_around_delimiters=True): 

1037 """Write an .ini-format representation of the configuration state. 

1038 

1039 If `space_around_delimiters` is True (the default), delimiters 

1040 between keys and values are surrounded by spaces. 

1041 

1042 Please note that comments in the original configuration file are not 

1043 preserved when writing the configuration back. 

1044 """ 

1045 if space_around_delimiters: 

1046 d = " {} ".format(self._delimiters[0]) 

1047 else: 

1048 d = self._delimiters[0] 

1049 if self._defaults: 

1050 self._write_section(fp, self.default_section, self._defaults.items(), d) 

1051 if UNNAMED_SECTION in self._sections and self._sections[UNNAMED_SECTION]: 

1052 self._write_section( 

1053 fp, 

1054 UNNAMED_SECTION, 

1055 self._sections[UNNAMED_SECTION].items(), 

1056 d, 

1057 unnamed=True, 

1058 ) 

1059 

1060 for section in self._sections: 

1061 if section is UNNAMED_SECTION: 

1062 continue 

1063 self._write_section(fp, section, self._sections[section].items(), d) 

1064 

1065 def _write_section(self, fp, section_name, section_items, delimiter, unnamed=False): 

1066 """Write a single section to the specified 'fp'.""" 

1067 if not unnamed: 

1068 fp.write("[{}]\n".format(section_name)) 

1069 for key, value in section_items: 

1070 self._validate_key_contents(key) 

1071 value = self._interpolation.before_write(self, section_name, key, value) 

1072 if value is not None or not self._allow_no_value: 

1073 # Convert all possible line-endings into '\n\t' 

1074 value = delimiter + str(value).replace('\r\n', '\n').replace( 

1075 '\r', '\n' 

1076 ).replace('\n', '\n\t') 

1077 else: 

1078 value = "" 

1079 fp.write("{}{}\n".format(key, value)) 

1080 fp.write("\n") 

1081 

1082 def remove_option(self, section, option): 

1083 """Remove an option.""" 

1084 if not section or section == self.default_section: 

1085 sectdict = self._defaults 

1086 else: 

1087 try: 

1088 sectdict = self._sections[section] 

1089 except KeyError: 

1090 raise NoSectionError(section) from None 

1091 option = self.optionxform(option) 

1092 existed = option in sectdict 

1093 if existed: 

1094 del sectdict[option] 

1095 return existed 

1096 

1097 def remove_section(self, section): 

1098 """Remove a file section.""" 

1099 existed = section in self._sections 

1100 if existed: 

1101 del self._sections[section] 

1102 del self._proxies[section] 

1103 return existed 

1104 

1105 def __getitem__(self, key): 

1106 if key != self.default_section and not self.has_section(key): 

1107 raise KeyError(key) 

1108 return self._proxies[key] 

1109 

1110 def __setitem__(self, key, value): 

1111 # To conform with the mapping protocol, overwrites existing values in 

1112 # the section. 

1113 if key in self and self[key] is value: 

1114 return 

1115 # XXX this is not atomic if read_dict fails at any point. Then again, 

1116 # no update method in configparser is atomic in this implementation. 

1117 if key == self.default_section: 

1118 self._defaults.clear() 

1119 elif key in self._sections: 

1120 self._sections[key].clear() 

1121 self.read_dict({key: value}) 

1122 

1123 def __delitem__(self, key): 

1124 if key == self.default_section: 

1125 raise ValueError("Cannot remove the default section.") 

1126 if not self.has_section(key): 

1127 raise KeyError(key) 

1128 self.remove_section(key) 

1129 

1130 def __contains__(self, key): 

1131 return key == self.default_section or self.has_section(key) 

1132 

1133 def __len__(self): 

1134 return len(self._sections) + 1 # the default section 

1135 

1136 def __iter__(self): 

1137 # XXX does it break when underlying container state changed? 

1138 return itertools.chain((self.default_section,), self._sections.keys()) 

1139 

1140 def _read(self, fp, fpname): 

1141 """Parse a sectioned configuration file. 

1142 

1143 Each section in a configuration file contains a header, indicated by 

1144 a name in square brackets (`[]`), plus key/value options, indicated by 

1145 `name` and `value` delimited with a specific substring (`=` or `:` by 

1146 default). 

1147 

1148 Values can span multiple lines, as long as they are indented deeper 

1149 than the first line of the value. Depending on the parser's mode, blank 

1150 lines may be treated as parts of multiline values or ignored. 

1151 

1152 Configuration files may include comments, prefixed by specific 

1153 characters (`#` and `;` by default). Comments may appear on their own 

1154 in an otherwise empty line or may be entered in lines holding values or 

1155 section names. Please note that comments get stripped off when reading 

1156 configuration files. 

1157 """ 

1158 try: 

1159 ParsingError._raise_all(self._read_inner(fp, fpname)) 

1160 finally: 

1161 self._join_multiline_values() 

1162 

1163 def _read_inner(self, fp, fpname): 

1164 st = _ReadState() 

1165 

1166 for st.lineno, line in enumerate(map(self._comments.wrap, fp), start=1): 

1167 if not line.clean: 

1168 if self._empty_lines_in_values: 

1169 # add empty line to the value, but only if there was no 

1170 # comment on the line 

1171 if ( 

1172 not line.has_comments 

1173 and st.cursect is not None 

1174 and st.optname 

1175 and st.cursect[st.optname] is not None 

1176 ): 

1177 st.cursect[st.optname].append('') # newlines added at join 

1178 else: 

1179 # empty line marks end of value 

1180 st.indent_level = sys.maxsize 

1181 continue 

1182 

1183 first_nonspace = self.NONSPACECRE.search(line) 

1184 st.cur_indent_level = first_nonspace.start() if first_nonspace else 0 

1185 

1186 if self._handle_continuation_line(st, line, fpname): 

1187 continue 

1188 

1189 self._handle_rest(st, line, fpname) 

1190 

1191 return st.errors 

1192 

1193 def _handle_continuation_line(self, st, line, fpname): 

1194 # continuation line? 

1195 is_continue = ( 

1196 st.cursect is not None 

1197 and st.optname 

1198 and st.cur_indent_level > st.indent_level 

1199 ) 

1200 if is_continue: 

1201 if st.cursect[st.optname] is None: 

1202 raise MultilineContinuationError(fpname, st.lineno, line) 

1203 st.cursect[st.optname].append(line.clean) 

1204 return is_continue 

1205 

1206 def _handle_rest(self, st, line, fpname): 

1207 # a section header or option header? 

1208 if self._allow_unnamed_section and st.cursect is None: 

1209 self._handle_header(st, UNNAMED_SECTION, fpname) 

1210 

1211 st.indent_level = st.cur_indent_level 

1212 # is it a section header? 

1213 mo = self.SECTCRE.match(line.clean) 

1214 

1215 if not mo and st.cursect is None: 

1216 raise MissingSectionHeaderError(fpname, st.lineno, line) 

1217 

1218 self._handle_header( 

1219 st, mo.group('header'), fpname 

1220 ) if mo else self._handle_option(st, line, fpname) 

1221 

1222 def _handle_header(self, st, sectname, fpname): 

1223 st.sectname = sectname 

1224 if st.sectname in self._sections: 

1225 if self._strict and st.sectname in st.elements_added: 

1226 raise DuplicateSectionError(st.sectname, fpname, st.lineno) 

1227 st.cursect = self._sections[st.sectname] 

1228 st.elements_added.add(st.sectname) 

1229 elif st.sectname == self.default_section: 

1230 st.cursect = self._defaults 

1231 else: 

1232 st.cursect = self._dict() 

1233 self._sections[st.sectname] = st.cursect 

1234 self._proxies[st.sectname] = SectionProxy(self, st.sectname) 

1235 st.elements_added.add(st.sectname) 

1236 # So sections can't start with a continuation line 

1237 st.optname = None 

1238 

1239 def _handle_option(self, st, line, fpname): 

1240 # an option line? 

1241 st.indent_level = st.cur_indent_level 

1242 

1243 mo = self._optcre.match(line.clean) 

1244 if not mo: 

1245 # a non-fatal parsing error occurred. set up the 

1246 # exception but keep going. the exception will be 

1247 # raised at the end of the file and will contain a 

1248 # list of all bogus lines 

1249 st.errors.append(ParsingError(fpname, st.lineno, line)) 

1250 return 

1251 

1252 st.optname, vi, optval = mo.group('option', 'vi', 'value') 

1253 if not st.optname: 

1254 st.errors.append(ParsingError(fpname, st.lineno, line)) 

1255 st.optname = self.optionxform(st.optname.rstrip()) 

1256 if self._strict and (st.sectname, st.optname) in st.elements_added: 

1257 raise DuplicateOptionError(st.sectname, st.optname, fpname, st.lineno) 

1258 st.elements_added.add((st.sectname, st.optname)) 

1259 # This check is fine because the OPTCRE cannot 

1260 # match if it would set optval to None 

1261 if optval is not None: 

1262 optval = optval.strip() 

1263 st.cursect[st.optname] = [optval] 

1264 else: 

1265 # valueless option handling 

1266 st.cursect[st.optname] = None 

1267 

1268 def _join_multiline_values(self): 

1269 defaults = self.default_section, self._defaults 

1270 all_sections = itertools.chain((defaults,), self._sections.items()) 

1271 for section, options in all_sections: 

1272 for name, val in options.items(): 

1273 if isinstance(val, list): 

1274 val = '\n'.join(val).rstrip() 

1275 options[name] = self._interpolation.before_read( 

1276 self, section, name, val 

1277 ) 

1278 

1279 def _read_defaults(self, defaults): 

1280 """Read the defaults passed in the initializer. 

1281 Note: values can be non-string.""" 

1282 for key, value in defaults.items(): 

1283 self._defaults[self.optionxform(key)] = value 

1284 

1285 def _unify_values(self, section, vars): 

1286 """Create a sequence of lookups with 'vars' taking priority over 

1287 the 'section' which takes priority over the DEFAULTSECT. 

1288 

1289 """ 

1290 sectiondict = {} 

1291 try: 

1292 sectiondict = self._sections[section] 

1293 except KeyError: 

1294 if section != self.default_section: 

1295 raise NoSectionError(section) 

1296 # Update with the entry specific variables 

1297 vardict = {} 

1298 if vars: 

1299 for key, value in vars.items(): 

1300 if value is not None: 

1301 value = str(value) 

1302 vardict[self.optionxform(key)] = value 

1303 return _ChainMap(vardict, sectiondict, self._defaults) 

1304 

1305 def _convert_to_boolean(self, value): 

1306 """Return a boolean value translating from other types if necessary.""" 

1307 if value.lower() not in self.BOOLEAN_STATES: 

1308 raise ValueError('Not a boolean: %s' % value) 

1309 return self.BOOLEAN_STATES[value.lower()] 

1310 

1311 def _validate_key_contents(self, key): 

1312 """Raises an InvalidWriteError for any keys containing 

1313 delimiters or that begins with the section header pattern""" 

1314 if re.match(self.SECTCRE, key): 

1315 raise InvalidWriteError( 

1316 f"Cannot write key {key}; begins with section pattern" 

1317 ) 

1318 for delim in self._delimiters: 

1319 if delim in key: 

1320 raise InvalidWriteError( 

1321 f"Cannot write key {key}; contains delimiter {delim}" 

1322 ) 

1323 

1324 def _validate_value_types(self, *, section="", option="", value=""): 

1325 """Raises a TypeError for illegal non-string values. 

1326 

1327 Legal non-string values are UNNAMED_SECTION and falsey values if 

1328 they are allowed. 

1329 

1330 For compatibility reasons this method is not used in classic set() 

1331 for RawConfigParsers. It is invoked in every case for mapping protocol 

1332 access and in ConfigParser.set(). 

1333 """ 

1334 if section is UNNAMED_SECTION: 

1335 if not self._allow_unnamed_section: 

1336 raise UnnamedSectionDisabledError 

1337 elif not isinstance(section, str): 

1338 raise TypeError("section names must be strings or UNNAMED_SECTION") 

1339 if not isinstance(option, str): 

1340 raise TypeError("option keys must be strings") 

1341 if not self._allow_no_value or value: 

1342 if not isinstance(value, str): 

1343 raise TypeError("option values must be strings") 

1344 

1345 @property 

1346 def converters(self): 

1347 return self._converters 

1348 

1349 

1350class ConfigParser(RawConfigParser): 

1351 """ConfigParser implementing interpolation.""" 

1352 

1353 _DEFAULT_INTERPOLATION = BasicInterpolation() 

1354 

1355 def set(self, section, option, value=None): 

1356 """Set an option. Extends RawConfigParser.set by validating type and 

1357 interpolation syntax on the value.""" 

1358 self._validate_value_types(option=option, value=value) 

1359 super().set(section, option, value) 

1360 

1361 def add_section(self, section): 

1362 """Create a new section in the configuration. Extends 

1363 RawConfigParser.add_section by validating if the section name is 

1364 a string.""" 

1365 self._validate_value_types(section=section) 

1366 super().add_section(section) 

1367 

1368 def _read_defaults(self, defaults): 

1369 """Reads the defaults passed in the initializer, implicitly converting 

1370 values to strings like the rest of the API. 

1371 

1372 Does not perform interpolation for backwards compatibility. 

1373 """ 

1374 try: 

1375 hold_interpolation = self._interpolation 

1376 self._interpolation = Interpolation() 

1377 self.read_dict({self.default_section: defaults}) 

1378 finally: 

1379 self._interpolation = hold_interpolation 

1380 

1381 

1382class SectionProxy(MutableMapping): 

1383 """A proxy for a single section from a parser.""" 

1384 

1385 def __init__(self, parser, name): 

1386 """Creates a view on a section of the specified `name` in `parser`.""" 

1387 self._parser = parser 

1388 self._name = name 

1389 for conv in parser.converters: 

1390 key = 'get' + conv 

1391 getter = functools.partial(self.get, _impl=getattr(parser, key)) 

1392 setattr(self, key, getter) 

1393 

1394 def __repr__(self): 

1395 return '<Section: {}>'.format(self._name) 

1396 

1397 def __getitem__(self, key): 

1398 if not self._parser.has_option(self._name, key): 

1399 raise KeyError(key) 

1400 return self._parser.get(self._name, key) 

1401 

1402 def __setitem__(self, key, value): 

1403 self._parser._validate_value_types(option=key, value=value) 

1404 return self._parser.set(self._name, key, value) 

1405 

1406 def __delitem__(self, key): 

1407 if not ( 

1408 self._parser.has_option(self._name, key) 

1409 and self._parser.remove_option(self._name, key) 

1410 ): 

1411 raise KeyError(key) 

1412 

1413 def __contains__(self, key): 

1414 return self._parser.has_option(self._name, key) 

1415 

1416 def __len__(self): 

1417 return len(self._options()) 

1418 

1419 def __iter__(self): 

1420 return self._options().__iter__() 

1421 

1422 def _options(self): 

1423 if self._name != self._parser.default_section: 

1424 return self._parser.options(self._name) 

1425 else: 

1426 return self._parser.defaults() 

1427 

1428 @property 

1429 def parser(self): 

1430 # The parser object of the proxy is read-only. 

1431 return self._parser 

1432 

1433 @property 

1434 def name(self): 

1435 # The name of the section on a proxy is read-only. 

1436 return self._name 

1437 

1438 def get(self, option, fallback=None, *, raw=False, vars=None, _impl=None, **kwargs): 

1439 """Get an option value. 

1440 

1441 Unless `fallback` is provided, `None` will be returned if the option 

1442 is not found. 

1443 

1444 """ 

1445 # If `_impl` is provided, it should be a getter method on the parser 

1446 # object that provides the desired type conversion. 

1447 if not _impl: 

1448 _impl = self._parser.get 

1449 return _impl( 

1450 self._name, option, raw=raw, vars=vars, fallback=fallback, **kwargs 

1451 ) 

1452 

1453 

1454class ConverterMapping(MutableMapping): 

1455 """Enables reuse of get*() methods between the parser and section proxies. 

1456 

1457 If a parser class implements a getter directly, the value for the given 

1458 key will be ``None``. The presence of the converter name here enables 

1459 section proxies to find and use the implementation on the parser class. 

1460 """ 

1461 

1462 GETTERCRE = re.compile(r"^get(?P<name>.+)$") 

1463 

1464 def __init__(self, parser): 

1465 self._parser = parser 

1466 self._data = {} 

1467 for getter in dir(self._parser): 

1468 m = self.GETTERCRE.match(getter) 

1469 if not m or not callable(getattr(self._parser, getter)): 

1470 continue 

1471 self._data[m.group('name')] = None # See class docstring. 

1472 

1473 def __getitem__(self, key): 

1474 return self._data[key] 

1475 

1476 def __setitem__(self, key, value): 

1477 try: 

1478 k = 'get' + key 

1479 except TypeError: 

1480 raise ValueError('Incompatible key: {} (type: {})'.format(key, type(key))) 

1481 if k == 'get': 

1482 raise ValueError('Incompatible key: cannot use "" as a name') 

1483 self._data[key] = value 

1484 func = functools.partial(self._parser._get_conv, conv=value) 

1485 func.converter = value 

1486 setattr(self._parser, k, func) 

1487 for proxy in self._parser.values(): 

1488 getter = functools.partial(proxy.get, _impl=func) 

1489 setattr(proxy, k, getter) 

1490 

1491 def __delitem__(self, key): 

1492 try: 

1493 k = 'get' + (key or None) 

1494 except TypeError: 

1495 raise KeyError(key) 

1496 del self._data[key] 

1497 for inst in itertools.chain((self._parser,), self._parser.values()): 

1498 try: 

1499 delattr(inst, k) 

1500 except AttributeError: 

1501 # don't raise since the entry was present in _data, silently 

1502 # clean up 

1503 continue 

1504 

1505 def __iter__(self): 

1506 return iter(self._data) 

1507 

1508 def __len__(self): 

1509 return len(self._data)