(root)/
Python-3.11.7/
Lib/
test/
test_configparser.py
       1  import collections
       2  import configparser
       3  import io
       4  import os
       5  import pathlib
       6  import textwrap
       7  import unittest
       8  import warnings
       9  
      10  from test import support
      11  from test.support import os_helper
      12  
      13  
      14  class ESC[4;38;5;81mSortedDict(ESC[4;38;5;149mcollectionsESC[4;38;5;149m.ESC[4;38;5;149mUserDict):
      15  
      16      def items(self):
      17          return sorted(self.data.items())
      18  
      19      def keys(self):
      20          return sorted(self.data.keys())
      21  
      22      def values(self):
      23          return [i[1] for i in self.items()]
      24  
      25      def iteritems(self):
      26          return iter(self.items())
      27  
      28      def iterkeys(self):
      29          return iter(self.keys())
      30  
      31      def itervalues(self):
      32          return iter(self.values())
      33  
      34      __iter__ = iterkeys
      35  
      36  
      37  class ESC[4;38;5;81mCfgParserTestCaseClass:
      38      allow_no_value = False
      39      delimiters = ('=', ':')
      40      comment_prefixes = (';', '#')
      41      inline_comment_prefixes = (';', '#')
      42      empty_lines_in_values = True
      43      dict_type = configparser._default_dict
      44      strict = False
      45      default_section = configparser.DEFAULTSECT
      46      interpolation = configparser._UNSET
      47  
      48      def newconfig(self, defaults=None):
      49          arguments = dict(
      50              defaults=defaults,
      51              allow_no_value=self.allow_no_value,
      52              delimiters=self.delimiters,
      53              comment_prefixes=self.comment_prefixes,
      54              inline_comment_prefixes=self.inline_comment_prefixes,
      55              empty_lines_in_values=self.empty_lines_in_values,
      56              dict_type=self.dict_type,
      57              strict=self.strict,
      58              default_section=self.default_section,
      59              interpolation=self.interpolation,
      60          )
      61          instance = self.config_class(**arguments)
      62          return instance
      63  
      64      def fromstring(self, string, defaults=None):
      65          cf = self.newconfig(defaults)
      66          cf.read_string(string)
      67          return cf
      68  
      69  
      70  class ESC[4;38;5;81mBasicTestCase(ESC[4;38;5;149mCfgParserTestCaseClass):
      71  
      72      def basic_test(self, cf):
      73          E = ['Commented Bar',
      74               'Foo Bar',
      75               'Internationalized Stuff',
      76               'Long Line',
      77               'Section\\with$weird%characters[\t',
      78               'Spaces',
      79               'Spacey Bar',
      80               'Spacey Bar From The Beginning',
      81               'Types',
      82               'This One Has A ] In It',
      83               ]
      84  
      85          if self.allow_no_value:
      86              E.append('NoValue')
      87          E.sort()
      88          F = [('baz', 'qwe'), ('foo', 'bar3')]
      89  
      90          # API access
      91          L = cf.sections()
      92          L.sort()
      93          eq = self.assertEqual
      94          eq(L, E)
      95          L = cf.items('Spacey Bar From The Beginning')
      96          L.sort()
      97          eq(L, F)
      98  
      99          # mapping access
     100          L = [section for section in cf]
     101          L.sort()
     102          E.append(self.default_section)
     103          E.sort()
     104          eq(L, E)
     105          L = cf['Spacey Bar From The Beginning'].items()
     106          L = sorted(list(L))
     107          eq(L, F)
     108          L = cf.items()
     109          L = sorted(list(L))
     110          self.assertEqual(len(L), len(E))
     111          for name, section in L:
     112              eq(name, section.name)
     113          eq(cf.defaults(), cf[self.default_section])
     114  
     115          # The use of spaces in the section names serves as a
     116          # regression test for SourceForge bug #583248:
     117          # http://www.python.org/sf/583248
     118  
     119          # API access
     120          eq(cf.get('Foo Bar', 'foo'), 'bar1')
     121          eq(cf.get('Spacey Bar', 'foo'), 'bar2')
     122          eq(cf.get('Spacey Bar From The Beginning', 'foo'), 'bar3')
     123          eq(cf.get('Spacey Bar From The Beginning', 'baz'), 'qwe')
     124          eq(cf.get('Commented Bar', 'foo'), 'bar4')
     125          eq(cf.get('Commented Bar', 'baz'), 'qwe')
     126          eq(cf.get('Spaces', 'key with spaces'), 'value')
     127          eq(cf.get('Spaces', 'another with spaces'), 'splat!')
     128          eq(cf.getint('Types', 'int'), 42)
     129          eq(cf.get('Types', 'int'), "42")
     130          self.assertAlmostEqual(cf.getfloat('Types', 'float'), 0.44)
     131          eq(cf.get('Types', 'float'), "0.44")
     132          eq(cf.getboolean('Types', 'boolean'), False)
     133          eq(cf.get('Types', '123'), 'strange but acceptable')
     134          eq(cf.get('This One Has A ] In It', 'forks'), 'spoons')
     135          if self.allow_no_value:
     136              eq(cf.get('NoValue', 'option-without-value'), None)
     137  
     138          # test vars= and fallback=
     139          eq(cf.get('Foo Bar', 'foo', fallback='baz'), 'bar1')
     140          eq(cf.get('Foo Bar', 'foo', vars={'foo': 'baz'}), 'baz')
     141          with self.assertRaises(configparser.NoSectionError):
     142              cf.get('No Such Foo Bar', 'foo')
     143          with self.assertRaises(configparser.NoOptionError):
     144              cf.get('Foo Bar', 'no-such-foo')
     145          eq(cf.get('No Such Foo Bar', 'foo', fallback='baz'), 'baz')
     146          eq(cf.get('Foo Bar', 'no-such-foo', fallback='baz'), 'baz')
     147          eq(cf.get('Spacey Bar', 'foo', fallback=None), 'bar2')
     148          eq(cf.get('No Such Spacey Bar', 'foo', fallback=None), None)
     149          eq(cf.getint('Types', 'int', fallback=18), 42)
     150          eq(cf.getint('Types', 'no-such-int', fallback=18), 18)
     151          eq(cf.getint('Types', 'no-such-int', fallback="18"), "18") # sic!
     152          with self.assertRaises(configparser.NoOptionError):
     153              cf.getint('Types', 'no-such-int')
     154          self.assertAlmostEqual(cf.getfloat('Types', 'float',
     155                                             fallback=0.0), 0.44)
     156          self.assertAlmostEqual(cf.getfloat('Types', 'no-such-float',
     157                                             fallback=0.0), 0.0)
     158          eq(cf.getfloat('Types', 'no-such-float', fallback="0.0"), "0.0") # sic!
     159          with self.assertRaises(configparser.NoOptionError):
     160              cf.getfloat('Types', 'no-such-float')
     161          eq(cf.getboolean('Types', 'boolean', fallback=True), False)
     162          eq(cf.getboolean('Types', 'no-such-boolean', fallback="yes"),
     163             "yes") # sic!
     164          eq(cf.getboolean('Types', 'no-such-boolean', fallback=True), True)
     165          with self.assertRaises(configparser.NoOptionError):
     166              cf.getboolean('Types', 'no-such-boolean')
     167          eq(cf.getboolean('No Such Types', 'boolean', fallback=True), True)
     168          if self.allow_no_value:
     169              eq(cf.get('NoValue', 'option-without-value', fallback=False), None)
     170              eq(cf.get('NoValue', 'no-such-option-without-value',
     171                        fallback=False), False)
     172  
     173          # mapping access
     174          eq(cf['Foo Bar']['foo'], 'bar1')
     175          eq(cf['Spacey Bar']['foo'], 'bar2')
     176          section = cf['Spacey Bar From The Beginning']
     177          eq(section.name, 'Spacey Bar From The Beginning')
     178          self.assertIs(section.parser, cf)
     179          with self.assertRaises(AttributeError):
     180              section.name = 'Name is read-only'
     181          with self.assertRaises(AttributeError):
     182              section.parser = 'Parser is read-only'
     183          eq(section['foo'], 'bar3')
     184          eq(section['baz'], 'qwe')
     185          eq(cf['Commented Bar']['foo'], 'bar4')
     186          eq(cf['Commented Bar']['baz'], 'qwe')
     187          eq(cf['Spaces']['key with spaces'], 'value')
     188          eq(cf['Spaces']['another with spaces'], 'splat!')
     189          eq(cf['Long Line']['foo'],
     190             'this line is much, much longer than my editor\nlikes it.')
     191          if self.allow_no_value:
     192              eq(cf['NoValue']['option-without-value'], None)
     193          # test vars= and fallback=
     194          eq(cf['Foo Bar'].get('foo', 'baz'), 'bar1')
     195          eq(cf['Foo Bar'].get('foo', fallback='baz'), 'bar1')
     196          eq(cf['Foo Bar'].get('foo', vars={'foo': 'baz'}), 'baz')
     197          with self.assertRaises(KeyError):
     198              cf['No Such Foo Bar']['foo']
     199          with self.assertRaises(KeyError):
     200              cf['Foo Bar']['no-such-foo']
     201          with self.assertRaises(KeyError):
     202              cf['No Such Foo Bar'].get('foo', fallback='baz')
     203          eq(cf['Foo Bar'].get('no-such-foo', 'baz'), 'baz')
     204          eq(cf['Foo Bar'].get('no-such-foo', fallback='baz'), 'baz')
     205          eq(cf['Foo Bar'].get('no-such-foo'), None)
     206          eq(cf['Spacey Bar'].get('foo', None), 'bar2')
     207          eq(cf['Spacey Bar'].get('foo', fallback=None), 'bar2')
     208          with self.assertRaises(KeyError):
     209              cf['No Such Spacey Bar'].get('foo', None)
     210          eq(cf['Types'].getint('int', 18), 42)
     211          eq(cf['Types'].getint('int', fallback=18), 42)
     212          eq(cf['Types'].getint('no-such-int', 18), 18)
     213          eq(cf['Types'].getint('no-such-int', fallback=18), 18)
     214          eq(cf['Types'].getint('no-such-int', "18"), "18") # sic!
     215          eq(cf['Types'].getint('no-such-int', fallback="18"), "18") # sic!
     216          eq(cf['Types'].getint('no-such-int'), None)
     217          self.assertAlmostEqual(cf['Types'].getfloat('float', 0.0), 0.44)
     218          self.assertAlmostEqual(cf['Types'].getfloat('float',
     219                                                      fallback=0.0), 0.44)
     220          self.assertAlmostEqual(cf['Types'].getfloat('no-such-float', 0.0), 0.0)
     221          self.assertAlmostEqual(cf['Types'].getfloat('no-such-float',
     222                                                      fallback=0.0), 0.0)
     223          eq(cf['Types'].getfloat('no-such-float', "0.0"), "0.0") # sic!
     224          eq(cf['Types'].getfloat('no-such-float', fallback="0.0"), "0.0") # sic!
     225          eq(cf['Types'].getfloat('no-such-float'), None)
     226          eq(cf['Types'].getboolean('boolean', True), False)
     227          eq(cf['Types'].getboolean('boolean', fallback=True), False)
     228          eq(cf['Types'].getboolean('no-such-boolean', "yes"), "yes") # sic!
     229          eq(cf['Types'].getboolean('no-such-boolean', fallback="yes"),
     230             "yes") # sic!
     231          eq(cf['Types'].getboolean('no-such-boolean', True), True)
     232          eq(cf['Types'].getboolean('no-such-boolean', fallback=True), True)
     233          eq(cf['Types'].getboolean('no-such-boolean'), None)
     234          if self.allow_no_value:
     235              eq(cf['NoValue'].get('option-without-value', False), None)
     236              eq(cf['NoValue'].get('option-without-value', fallback=False), None)
     237              eq(cf['NoValue'].get('no-such-option-without-value', False), False)
     238              eq(cf['NoValue'].get('no-such-option-without-value',
     239                        fallback=False), False)
     240  
     241          # Make sure the right things happen for remove_section() and
     242          # remove_option(); added to include check for SourceForge bug #123324.
     243  
     244          cf[self.default_section]['this_value'] = '1'
     245          cf[self.default_section]['that_value'] = '2'
     246  
     247          # API access
     248          self.assertTrue(cf.remove_section('Spaces'))
     249          self.assertFalse(cf.has_option('Spaces', 'key with spaces'))
     250          self.assertFalse(cf.remove_section('Spaces'))
     251          self.assertFalse(cf.remove_section(self.default_section))
     252          self.assertTrue(cf.remove_option('Foo Bar', 'foo'),
     253                          "remove_option() failed to report existence of option")
     254          self.assertFalse(cf.has_option('Foo Bar', 'foo'),
     255                      "remove_option() failed to remove option")
     256          self.assertFalse(cf.remove_option('Foo Bar', 'foo'),
     257                      "remove_option() failed to report non-existence of option"
     258                      " that was removed")
     259          self.assertTrue(cf.has_option('Foo Bar', 'this_value'))
     260          self.assertFalse(cf.remove_option('Foo Bar', 'this_value'))
     261          self.assertTrue(cf.remove_option(self.default_section, 'this_value'))
     262          self.assertFalse(cf.has_option('Foo Bar', 'this_value'))
     263          self.assertFalse(cf.remove_option(self.default_section, 'this_value'))
     264  
     265          with self.assertRaises(configparser.NoSectionError) as cm:
     266              cf.remove_option('No Such Section', 'foo')
     267          self.assertEqual(cm.exception.args, ('No Such Section',))
     268  
     269          eq(cf.get('Long Line', 'foo'),
     270             'this line is much, much longer than my editor\nlikes it.')
     271  
     272          # mapping access
     273          del cf['Types']
     274          self.assertFalse('Types' in cf)
     275          with self.assertRaises(KeyError):
     276              del cf['Types']
     277          with self.assertRaises(ValueError):
     278              del cf[self.default_section]
     279          del cf['Spacey Bar']['foo']
     280          self.assertFalse('foo' in cf['Spacey Bar'])
     281          with self.assertRaises(KeyError):
     282              del cf['Spacey Bar']['foo']
     283          self.assertTrue('that_value' in cf['Spacey Bar'])
     284          with self.assertRaises(KeyError):
     285              del cf['Spacey Bar']['that_value']
     286          del cf[self.default_section]['that_value']
     287          self.assertFalse('that_value' in cf['Spacey Bar'])
     288          with self.assertRaises(KeyError):
     289              del cf[self.default_section]['that_value']
     290          with self.assertRaises(KeyError):
     291              del cf['No Such Section']['foo']
     292  
     293          # Don't add new asserts below in this method as most of the options
     294          # and sections are now removed.
     295  
     296      def test_basic(self):
     297          config_string = """\
     298  [Foo Bar]
     299  foo{0[0]}bar1
     300  [Spacey Bar]
     301  foo {0[0]} bar2
     302  [Spacey Bar From The Beginning]
     303    foo {0[0]} bar3
     304    baz {0[0]} qwe
     305  [Commented Bar]
     306  foo{0[1]} bar4 {1[1]} comment
     307  baz{0[0]}qwe {1[0]}another one
     308  [Long Line]
     309  foo{0[1]} this line is much, much longer than my editor
     310     likes it.
     311  [Section\\with$weird%characters[\t]
     312  [Internationalized Stuff]
     313  foo[bg]{0[1]} Bulgarian
     314  foo{0[0]}Default
     315  foo[en]{0[0]}English
     316  foo[de]{0[0]}Deutsch
     317  [Spaces]
     318  key with spaces {0[1]} value
     319  another with spaces {0[0]} splat!
     320  [Types]
     321  int {0[1]} 42
     322  float {0[0]} 0.44
     323  boolean {0[0]} NO
     324  123 {0[1]} strange but acceptable
     325  [This One Has A ] In It]
     326    forks {0[0]} spoons
     327  """.format(self.delimiters, self.comment_prefixes)
     328          if self.allow_no_value:
     329              config_string += (
     330                  "[NoValue]\n"
     331                  "option-without-value\n"
     332                  )
     333          cf = self.fromstring(config_string)
     334          self.basic_test(cf)
     335          if self.strict:
     336              with self.assertRaises(configparser.DuplicateOptionError):
     337                  cf.read_string(textwrap.dedent("""\
     338                      [Duplicate Options Here]
     339                      option {0[0]} with a value
     340                      option {0[1]} with another value
     341                  """.format(self.delimiters)))
     342              with self.assertRaises(configparser.DuplicateSectionError):
     343                  cf.read_string(textwrap.dedent("""\
     344                      [And Now For Something]
     345                      completely different {0[0]} True
     346                      [And Now For Something]
     347                      the larch {0[1]} 1
     348                  """.format(self.delimiters)))
     349          else:
     350              cf.read_string(textwrap.dedent("""\
     351                  [Duplicate Options Here]
     352                  option {0[0]} with a value
     353                  option {0[1]} with another value
     354              """.format(self.delimiters)))
     355  
     356              cf.read_string(textwrap.dedent("""\
     357                  [And Now For Something]
     358                  completely different {0[0]} True
     359                  [And Now For Something]
     360                  the larch {0[1]} 1
     361              """.format(self.delimiters)))
     362  
     363      def test_basic_from_dict(self):
     364          config = {
     365              "Foo Bar": {
     366                  "foo": "bar1",
     367              },
     368              "Spacey Bar": {
     369                  "foo": "bar2",
     370              },
     371              "Spacey Bar From The Beginning": {
     372                  "foo": "bar3",
     373                  "baz": "qwe",
     374              },
     375              "Commented Bar": {
     376                  "foo": "bar4",
     377                  "baz": "qwe",
     378              },
     379              "Long Line": {
     380                  "foo": "this line is much, much longer than my editor\nlikes "
     381                         "it.",
     382              },
     383              "Section\\with$weird%characters[\t": {
     384              },
     385              "Internationalized Stuff": {
     386                  "foo[bg]": "Bulgarian",
     387                  "foo": "Default",
     388                  "foo[en]": "English",
     389                  "foo[de]": "Deutsch",
     390              },
     391              "Spaces": {
     392                  "key with spaces": "value",
     393                  "another with spaces": "splat!",
     394              },
     395              "Types": {
     396                  "int": 42,
     397                  "float": 0.44,
     398                  "boolean": False,
     399                  123: "strange but acceptable",
     400              },
     401              "This One Has A ] In It": {
     402                  "forks": "spoons"
     403              },
     404          }
     405          if self.allow_no_value:
     406              config.update({
     407                  "NoValue": {
     408                      "option-without-value": None,
     409                  }
     410              })
     411          cf = self.newconfig()
     412          cf.read_dict(config)
     413          self.basic_test(cf)
     414          if self.strict:
     415              with self.assertRaises(configparser.DuplicateSectionError):
     416                  cf.read_dict({
     417                      '1': {'key': 'value'},
     418                      1: {'key2': 'value2'},
     419                  })
     420              with self.assertRaises(configparser.DuplicateOptionError):
     421                  cf.read_dict({
     422                      "Duplicate Options Here": {
     423                          'option': 'with a value',
     424                          'OPTION': 'with another value',
     425                      },
     426                  })
     427          else:
     428              cf.read_dict({
     429                  'section': {'key': 'value'},
     430                  'SECTION': {'key2': 'value2'},
     431              })
     432              cf.read_dict({
     433                  "Duplicate Options Here": {
     434                      'option': 'with a value',
     435                      'OPTION': 'with another value',
     436                  },
     437              })
     438  
     439      def test_case_sensitivity(self):
     440          cf = self.newconfig()
     441          cf.add_section("A")
     442          cf.add_section("a")
     443          cf.add_section("B")
     444          L = cf.sections()
     445          L.sort()
     446          eq = self.assertEqual
     447          eq(L, ["A", "B", "a"])
     448          cf.set("a", "B", "value")
     449          eq(cf.options("a"), ["b"])
     450          eq(cf.get("a", "b"), "value",
     451             "could not locate option, expecting case-insensitive option names")
     452          with self.assertRaises(configparser.NoSectionError):
     453              # section names are case-sensitive
     454              cf.set("b", "A", "value")
     455          self.assertTrue(cf.has_option("a", "b"))
     456          self.assertFalse(cf.has_option("b", "b"))
     457          cf.set("A", "A-B", "A-B value")
     458          for opt in ("a-b", "A-b", "a-B", "A-B"):
     459              self.assertTrue(
     460                  cf.has_option("A", opt),
     461                  "has_option() returned false for option which should exist")
     462          eq(cf.options("A"), ["a-b"])
     463          eq(cf.options("a"), ["b"])
     464          cf.remove_option("a", "B")
     465          eq(cf.options("a"), [])
     466  
     467          # SF bug #432369:
     468          cf = self.fromstring(
     469              "[MySection]\nOption{} first line   \n\tsecond line   \n".format(
     470                  self.delimiters[0]))
     471          eq(cf.options("MySection"), ["option"])
     472          eq(cf.get("MySection", "Option"), "first line\nsecond line")
     473  
     474          # SF bug #561822:
     475          cf = self.fromstring("[section]\n"
     476                               "nekey{}nevalue\n".format(self.delimiters[0]),
     477                               defaults={"key":"value"})
     478          self.assertTrue(cf.has_option("section", "Key"))
     479  
     480  
     481      def test_case_sensitivity_mapping_access(self):
     482          cf = self.newconfig()
     483          cf["A"] = {}
     484          cf["a"] = {"B": "value"}
     485          cf["B"] = {}
     486          L = [section for section in cf]
     487          L.sort()
     488          eq = self.assertEqual
     489          elem_eq = self.assertCountEqual
     490          eq(L, sorted(["A", "B", self.default_section, "a"]))
     491          eq(cf["a"].keys(), {"b"})
     492          eq(cf["a"]["b"], "value",
     493             "could not locate option, expecting case-insensitive option names")
     494          with self.assertRaises(KeyError):
     495              # section names are case-sensitive
     496              cf["b"]["A"] = "value"
     497          self.assertTrue("b" in cf["a"])
     498          cf["A"]["A-B"] = "A-B value"
     499          for opt in ("a-b", "A-b", "a-B", "A-B"):
     500              self.assertTrue(
     501                  opt in cf["A"],
     502                  "has_option() returned false for option which should exist")
     503          eq(cf["A"].keys(), {"a-b"})
     504          eq(cf["a"].keys(), {"b"})
     505          del cf["a"]["B"]
     506          elem_eq(cf["a"].keys(), {})
     507  
     508          # SF bug #432369:
     509          cf = self.fromstring(
     510              "[MySection]\nOption{} first line   \n\tsecond line   \n".format(
     511                  self.delimiters[0]))
     512          eq(cf["MySection"].keys(), {"option"})
     513          eq(cf["MySection"]["Option"], "first line\nsecond line")
     514  
     515          # SF bug #561822:
     516          cf = self.fromstring("[section]\n"
     517                               "nekey{}nevalue\n".format(self.delimiters[0]),
     518                               defaults={"key":"value"})
     519          self.assertTrue("Key" in cf["section"])
     520  
     521      def test_default_case_sensitivity(self):
     522          cf = self.newconfig({"foo": "Bar"})
     523          self.assertEqual(
     524              cf.get(self.default_section, "Foo"), "Bar",
     525              "could not locate option, expecting case-insensitive option names")
     526          cf = self.newconfig({"Foo": "Bar"})
     527          self.assertEqual(
     528              cf.get(self.default_section, "Foo"), "Bar",
     529              "could not locate option, expecting case-insensitive defaults")
     530  
     531      def test_parse_errors(self):
     532          cf = self.newconfig()
     533          self.parse_error(cf, configparser.ParsingError,
     534                           "[Foo]\n"
     535                           "{}val-without-opt-name\n".format(self.delimiters[0]))
     536          self.parse_error(cf, configparser.ParsingError,
     537                           "[Foo]\n"
     538                           "{}val-without-opt-name\n".format(self.delimiters[1]))
     539          e = self.parse_error(cf, configparser.MissingSectionHeaderError,
     540                               "No Section!\n")
     541          self.assertEqual(e.args, ('<???>', 1, "No Section!\n"))
     542          if not self.allow_no_value:
     543              e = self.parse_error(cf, configparser.ParsingError,
     544                                  "[Foo]\n  wrong-indent\n")
     545              self.assertEqual(e.args, ('<???>',))
     546              # read_file on a real file
     547              tricky = support.findfile("cfgparser.3", subdir="configdata")
     548              if self.delimiters[0] == '=':
     549                  error = configparser.ParsingError
     550                  expected = (tricky,)
     551              else:
     552                  error = configparser.MissingSectionHeaderError
     553                  expected = (tricky, 1,
     554                              '  # INI with as many tricky parts as possible\n')
     555              with open(tricky, encoding='utf-8') as f:
     556                  e = self.parse_error(cf, error, f)
     557              self.assertEqual(e.args, expected)
     558  
     559      def parse_error(self, cf, exc, src):
     560          if hasattr(src, 'readline'):
     561              sio = src
     562          else:
     563              sio = io.StringIO(src)
     564          with self.assertRaises(exc) as cm:
     565              cf.read_file(sio)
     566          return cm.exception
     567  
     568      def test_query_errors(self):
     569          cf = self.newconfig()
     570          self.assertEqual(cf.sections(), [],
     571                           "new ConfigParser should have no defined sections")
     572          self.assertFalse(cf.has_section("Foo"),
     573                           "new ConfigParser should have no acknowledged "
     574                           "sections")
     575          with self.assertRaises(configparser.NoSectionError):
     576              cf.options("Foo")
     577          with self.assertRaises(configparser.NoSectionError):
     578              cf.set("foo", "bar", "value")
     579          e = self.get_error(cf, configparser.NoSectionError, "foo", "bar")
     580          self.assertEqual(e.args, ("foo",))
     581          cf.add_section("foo")
     582          e = self.get_error(cf, configparser.NoOptionError, "foo", "bar")
     583          self.assertEqual(e.args, ("bar", "foo"))
     584  
     585      def get_error(self, cf, exc, section, option):
     586          try:
     587              cf.get(section, option)
     588          except exc as e:
     589              return e
     590          else:
     591              self.fail("expected exception type %s.%s"
     592                        % (exc.__module__, exc.__qualname__))
     593  
     594      def test_boolean(self):
     595          cf = self.fromstring(
     596              "[BOOLTEST]\n"
     597              "T1{equals}1\n"
     598              "T2{equals}TRUE\n"
     599              "T3{equals}True\n"
     600              "T4{equals}oN\n"
     601              "T5{equals}yes\n"
     602              "F1{equals}0\n"
     603              "F2{equals}FALSE\n"
     604              "F3{equals}False\n"
     605              "F4{equals}oFF\n"
     606              "F5{equals}nO\n"
     607              "E1{equals}2\n"
     608              "E2{equals}foo\n"
     609              "E3{equals}-1\n"
     610              "E4{equals}0.1\n"
     611              "E5{equals}FALSE AND MORE".format(equals=self.delimiters[0])
     612              )
     613          for x in range(1, 5):
     614              self.assertTrue(cf.getboolean('BOOLTEST', 't%d' % x))
     615              self.assertFalse(cf.getboolean('BOOLTEST', 'f%d' % x))
     616              self.assertRaises(ValueError,
     617                                cf.getboolean, 'BOOLTEST', 'e%d' % x)
     618  
     619      def test_weird_errors(self):
     620          cf = self.newconfig()
     621          cf.add_section("Foo")
     622          with self.assertRaises(configparser.DuplicateSectionError) as cm:
     623              cf.add_section("Foo")
     624          e = cm.exception
     625          self.assertEqual(str(e), "Section 'Foo' already exists")
     626          self.assertEqual(e.args, ("Foo", None, None))
     627  
     628          if self.strict:
     629              with self.assertRaises(configparser.DuplicateSectionError) as cm:
     630                  cf.read_string(textwrap.dedent("""\
     631                      [Foo]
     632                      will this be added{equals}True
     633                      [Bar]
     634                      what about this{equals}True
     635                      [Foo]
     636                      oops{equals}this won't
     637                  """.format(equals=self.delimiters[0])), source='<foo-bar>')
     638              e = cm.exception
     639              self.assertEqual(str(e), "While reading from '<foo-bar>' "
     640                                       "[line  5]: section 'Foo' already exists")
     641              self.assertEqual(e.args, ("Foo", '<foo-bar>', 5))
     642  
     643              with self.assertRaises(configparser.DuplicateOptionError) as cm:
     644                  cf.read_dict({'Bar': {'opt': 'val', 'OPT': 'is really `opt`'}})
     645              e = cm.exception
     646              self.assertEqual(str(e), "While reading from '<dict>': option "
     647                                       "'opt' in section 'Bar' already exists")
     648              self.assertEqual(e.args, ("Bar", "opt", "<dict>", None))
     649  
     650      def test_write(self):
     651          config_string = (
     652              "[Long Line]\n"
     653              "foo{0[0]} this line is much, much longer than my editor\n"
     654              "   likes it.\n"
     655              "[{default_section}]\n"
     656              "foo{0[1]} another very\n"
     657              " long line\n"
     658              "[Long Line - With Comments!]\n"
     659              "test {0[1]} we        {comment} can\n"
     660              "            also      {comment} place\n"
     661              "            comments  {comment} in\n"
     662              "            multiline {comment} values"
     663              "\n".format(self.delimiters, comment=self.comment_prefixes[0],
     664                          default_section=self.default_section)
     665              )
     666          if self.allow_no_value:
     667              config_string += (
     668              "[Valueless]\n"
     669              "option-without-value\n"
     670              )
     671  
     672          cf = self.fromstring(config_string)
     673          for space_around_delimiters in (True, False):
     674              output = io.StringIO()
     675              cf.write(output, space_around_delimiters=space_around_delimiters)
     676              delimiter = self.delimiters[0]
     677              if space_around_delimiters:
     678                  delimiter = " {} ".format(delimiter)
     679              expect_string = (
     680                  "[{default_section}]\n"
     681                  "foo{equals}another very\n"
     682                  "\tlong line\n"
     683                  "\n"
     684                  "[Long Line]\n"
     685                  "foo{equals}this line is much, much longer than my editor\n"
     686                  "\tlikes it.\n"
     687                  "\n"
     688                  "[Long Line - With Comments!]\n"
     689                  "test{equals}we\n"
     690                  "\talso\n"
     691                  "\tcomments\n"
     692                  "\tmultiline\n"
     693                  "\n".format(equals=delimiter,
     694                              default_section=self.default_section)
     695                  )
     696              if self.allow_no_value:
     697                  expect_string += (
     698                      "[Valueless]\n"
     699                      "option-without-value\n"
     700                      "\n"
     701                      )
     702              self.assertEqual(output.getvalue(), expect_string)
     703  
     704      def test_set_string_types(self):
     705          cf = self.fromstring("[sect]\n"
     706                               "option1{eq}foo\n".format(eq=self.delimiters[0]))
     707          # Check that we don't get an exception when setting values in
     708          # an existing section using strings:
     709          class ESC[4;38;5;81mmystr(ESC[4;38;5;149mstr):
     710              pass
     711          cf.set("sect", "option1", "splat")
     712          cf.set("sect", "option1", mystr("splat"))
     713          cf.set("sect", "option2", "splat")
     714          cf.set("sect", "option2", mystr("splat"))
     715          cf.set("sect", "option1", "splat")
     716          cf.set("sect", "option2", "splat")
     717  
     718      def test_read_returns_file_list(self):
     719          if self.delimiters[0] != '=':
     720              self.skipTest('incompatible format')
     721          file1 = support.findfile("cfgparser.1", subdir="configdata")
     722          # check when we pass a mix of readable and non-readable files:
     723          cf = self.newconfig()
     724          parsed_files = cf.read([file1, "nonexistent-file"], encoding="utf-8")
     725          self.assertEqual(parsed_files, [file1])
     726          self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
     727          # check when we pass only a filename:
     728          cf = self.newconfig()
     729          parsed_files = cf.read(file1, encoding="utf-8")
     730          self.assertEqual(parsed_files, [file1])
     731          self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
     732          # check when we pass only a Path object:
     733          cf = self.newconfig()
     734          parsed_files = cf.read(pathlib.Path(file1), encoding="utf-8")
     735          self.assertEqual(parsed_files, [file1])
     736          self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
     737          # check when we passed both a filename and a Path object:
     738          cf = self.newconfig()
     739          parsed_files = cf.read([pathlib.Path(file1), file1], encoding="utf-8")
     740          self.assertEqual(parsed_files, [file1, file1])
     741          self.assertEqual(cf.get("Foo Bar", "foo"), "newbar")
     742          # check when we pass only missing files:
     743          cf = self.newconfig()
     744          parsed_files = cf.read(["nonexistent-file"], encoding="utf-8")
     745          self.assertEqual(parsed_files, [])
     746          # check when we pass no files:
     747          cf = self.newconfig()
     748          parsed_files = cf.read([], encoding="utf-8")
     749          self.assertEqual(parsed_files, [])
     750  
     751      def test_read_returns_file_list_with_bytestring_path(self):
     752          if self.delimiters[0] != '=':
     753              self.skipTest('incompatible format')
     754          file1_bytestring = support.findfile("cfgparser.1", subdir="configdata").encode()
     755          # check when passing an existing bytestring path
     756          cf = self.newconfig()
     757          parsed_files = cf.read(file1_bytestring, encoding="utf-8")
     758          self.assertEqual(parsed_files, [file1_bytestring])
     759          # check when passing an non-existing bytestring path
     760          cf = self.newconfig()
     761          parsed_files = cf.read(b'nonexistent-file', encoding="utf-8")
     762          self.assertEqual(parsed_files, [])
     763          # check when passing both an existing and non-existing bytestring path
     764          cf = self.newconfig()
     765          parsed_files = cf.read([file1_bytestring, b'nonexistent-file'], encoding="utf-8")
     766          self.assertEqual(parsed_files, [file1_bytestring])
     767  
     768      # shared by subclasses
     769      def get_interpolation_config(self):
     770          return self.fromstring(
     771              "[Foo]\n"
     772              "bar{equals}something %(with1)s interpolation (1 step)\n"
     773              "bar9{equals}something %(with9)s lots of interpolation (9 steps)\n"
     774              "bar10{equals}something %(with10)s lots of interpolation (10 steps)\n"
     775              "bar11{equals}something %(with11)s lots of interpolation (11 steps)\n"
     776              "with11{equals}%(with10)s\n"
     777              "with10{equals}%(with9)s\n"
     778              "with9{equals}%(with8)s\n"
     779              "with8{equals}%(With7)s\n"
     780              "with7{equals}%(WITH6)s\n"
     781              "with6{equals}%(with5)s\n"
     782              "With5{equals}%(with4)s\n"
     783              "WITH4{equals}%(with3)s\n"
     784              "with3{equals}%(with2)s\n"
     785              "with2{equals}%(with1)s\n"
     786              "with1{equals}with\n"
     787              "\n"
     788              "[Mutual Recursion]\n"
     789              "foo{equals}%(bar)s\n"
     790              "bar{equals}%(foo)s\n"
     791              "\n"
     792              "[Interpolation Error]\n"
     793              # no definition for 'reference'
     794              "name{equals}%(reference)s\n".format(equals=self.delimiters[0]))
     795  
     796      def check_items_config(self, expected):
     797          cf = self.fromstring("""
     798              [section]
     799              name {0[0]} %(value)s
     800              key{0[1]} |%(name)s|
     801              getdefault{0[1]} |%(default)s|
     802          """.format(self.delimiters), defaults={"default": "<default>"})
     803          L = list(cf.items("section", vars={'value': 'value'}))
     804          L.sort()
     805          self.assertEqual(L, expected)
     806          with self.assertRaises(configparser.NoSectionError):
     807              cf.items("no such section")
     808  
     809      def test_popitem(self):
     810          cf = self.fromstring("""
     811              [section1]
     812              name1 {0[0]} value1
     813              [section2]
     814              name2 {0[0]} value2
     815              [section3]
     816              name3 {0[0]} value3
     817          """.format(self.delimiters), defaults={"default": "<default>"})
     818          self.assertEqual(cf.popitem()[0], 'section1')
     819          self.assertEqual(cf.popitem()[0], 'section2')
     820          self.assertEqual(cf.popitem()[0], 'section3')
     821          with self.assertRaises(KeyError):
     822              cf.popitem()
     823  
     824      def test_clear(self):
     825          cf = self.newconfig({"foo": "Bar"})
     826          self.assertEqual(
     827              cf.get(self.default_section, "Foo"), "Bar",
     828              "could not locate option, expecting case-insensitive option names")
     829          cf['zing'] = {'option1': 'value1', 'option2': 'value2'}
     830          self.assertEqual(cf.sections(), ['zing'])
     831          self.assertEqual(set(cf['zing'].keys()), {'option1', 'option2', 'foo'})
     832          cf.clear()
     833          self.assertEqual(set(cf.sections()), set())
     834          self.assertEqual(set(cf[self.default_section].keys()), {'foo'})
     835  
     836      def test_setitem(self):
     837          cf = self.fromstring("""
     838              [section1]
     839              name1 {0[0]} value1
     840              [section2]
     841              name2 {0[0]} value2
     842              [section3]
     843              name3 {0[0]} value3
     844          """.format(self.delimiters), defaults={"nameD": "valueD"})
     845          self.assertEqual(set(cf['section1'].keys()), {'name1', 'named'})
     846          self.assertEqual(set(cf['section2'].keys()), {'name2', 'named'})
     847          self.assertEqual(set(cf['section3'].keys()), {'name3', 'named'})
     848          self.assertEqual(cf['section1']['name1'], 'value1')
     849          self.assertEqual(cf['section2']['name2'], 'value2')
     850          self.assertEqual(cf['section3']['name3'], 'value3')
     851          self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
     852          cf['section2'] = {'name22': 'value22'}
     853          self.assertEqual(set(cf['section2'].keys()), {'name22', 'named'})
     854          self.assertEqual(cf['section2']['name22'], 'value22')
     855          self.assertNotIn('name2', cf['section2'])
     856          self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
     857          cf['section3'] = {}
     858          self.assertEqual(set(cf['section3'].keys()), {'named'})
     859          self.assertNotIn('name3', cf['section3'])
     860          self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
     861          # For bpo-32108, assigning default_section to itself.
     862          cf[self.default_section] = cf[self.default_section]
     863          self.assertNotEqual(set(cf[self.default_section].keys()), set())
     864          cf[self.default_section] = {}
     865          self.assertEqual(set(cf[self.default_section].keys()), set())
     866          self.assertEqual(set(cf['section1'].keys()), {'name1'})
     867          self.assertEqual(set(cf['section2'].keys()), {'name22'})
     868          self.assertEqual(set(cf['section3'].keys()), set())
     869          self.assertEqual(cf.sections(), ['section1', 'section2', 'section3'])
     870          # For bpo-32108, assigning section to itself.
     871          cf['section2'] = cf['section2']
     872          self.assertEqual(set(cf['section2'].keys()), {'name22'})
     873  
     874      def test_invalid_multiline_value(self):
     875          if self.allow_no_value:
     876              self.skipTest('if no_value is allowed, ParsingError is not raised')
     877  
     878          invalid = textwrap.dedent("""\
     879              [DEFAULT]
     880              test {0} test
     881              invalid""".format(self.delimiters[0])
     882          )
     883          cf = self.newconfig()
     884          with self.assertRaises(configparser.ParsingError):
     885              cf.read_string(invalid)
     886          self.assertEqual(cf.get('DEFAULT', 'test'), 'test')
     887          self.assertEqual(cf['DEFAULT']['test'], 'test')
     888  
     889  
     890  class ESC[4;38;5;81mStrictTestCase(ESC[4;38;5;149mBasicTestCase, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     891      config_class = configparser.RawConfigParser
     892      strict = True
     893  
     894  
     895  class ESC[4;38;5;81mConfigParserTestCase(ESC[4;38;5;149mBasicTestCase, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     896      config_class = configparser.ConfigParser
     897  
     898      def test_interpolation(self):
     899          cf = self.get_interpolation_config()
     900          eq = self.assertEqual
     901          eq(cf.get("Foo", "bar"), "something with interpolation (1 step)")
     902          eq(cf.get("Foo", "bar9"),
     903             "something with lots of interpolation (9 steps)")
     904          eq(cf.get("Foo", "bar10"),
     905             "something with lots of interpolation (10 steps)")
     906          e = self.get_error(cf, configparser.InterpolationDepthError, "Foo", "bar11")
     907          if self.interpolation == configparser._UNSET:
     908              self.assertEqual(e.args, ("bar11", "Foo",
     909                  "something %(with11)s lots of interpolation (11 steps)"))
     910          elif isinstance(self.interpolation, configparser.LegacyInterpolation):
     911              self.assertEqual(e.args, ("bar11", "Foo",
     912                  "something %(with11)s lots of interpolation (11 steps)"))
     913  
     914      def test_interpolation_missing_value(self):
     915          cf = self.get_interpolation_config()
     916          e = self.get_error(cf, configparser.InterpolationMissingOptionError,
     917                             "Interpolation Error", "name")
     918          self.assertEqual(e.reference, "reference")
     919          self.assertEqual(e.section, "Interpolation Error")
     920          self.assertEqual(e.option, "name")
     921          if self.interpolation == configparser._UNSET:
     922              self.assertEqual(e.args, ('name', 'Interpolation Error',
     923                                      '%(reference)s', 'reference'))
     924          elif isinstance(self.interpolation, configparser.LegacyInterpolation):
     925              self.assertEqual(e.args, ('name', 'Interpolation Error',
     926                                      '%(reference)s', 'reference'))
     927  
     928      def test_items(self):
     929          self.check_items_config([('default', '<default>'),
     930                                   ('getdefault', '|<default>|'),
     931                                   ('key', '|value|'),
     932                                   ('name', 'value')])
     933  
     934      def test_safe_interpolation(self):
     935          # See http://www.python.org/sf/511737
     936          cf = self.fromstring("[section]\n"
     937                               "option1{eq}xxx\n"
     938                               "option2{eq}%(option1)s/xxx\n"
     939                               "ok{eq}%(option1)s/%%s\n"
     940                               "not_ok{eq}%(option2)s/%%s".format(
     941                                   eq=self.delimiters[0]))
     942          self.assertEqual(cf.get("section", "ok"), "xxx/%s")
     943          if self.interpolation == configparser._UNSET:
     944              self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s")
     945          elif isinstance(self.interpolation, configparser.LegacyInterpolation):
     946              with self.assertRaises(TypeError):
     947                  cf.get("section", "not_ok")
     948  
     949      def test_set_malformatted_interpolation(self):
     950          cf = self.fromstring("[sect]\n"
     951                               "option1{eq}foo\n".format(eq=self.delimiters[0]))
     952  
     953          self.assertEqual(cf.get('sect', "option1"), "foo")
     954  
     955          self.assertRaises(ValueError, cf.set, "sect", "option1", "%foo")
     956          self.assertRaises(ValueError, cf.set, "sect", "option1", "foo%")
     957          self.assertRaises(ValueError, cf.set, "sect", "option1", "f%oo")
     958  
     959          self.assertEqual(cf.get('sect', "option1"), "foo")
     960  
     961          # bug #5741: double percents are *not* malformed
     962          cf.set("sect", "option2", "foo%%bar")
     963          self.assertEqual(cf.get("sect", "option2"), "foo%bar")
     964  
     965      def test_set_nonstring_types(self):
     966          cf = self.fromstring("[sect]\n"
     967                               "option1{eq}foo\n".format(eq=self.delimiters[0]))
     968          # Check that we get a TypeError when setting non-string values
     969          # in an existing section:
     970          self.assertRaises(TypeError, cf.set, "sect", "option1", 1)
     971          self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0)
     972          self.assertRaises(TypeError, cf.set, "sect", "option1", object())
     973          self.assertRaises(TypeError, cf.set, "sect", "option2", 1)
     974          self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0)
     975          self.assertRaises(TypeError, cf.set, "sect", "option2", object())
     976          self.assertRaises(TypeError, cf.set, "sect", 123, "invalid opt name!")
     977          self.assertRaises(TypeError, cf.add_section, 123)
     978  
     979      def test_add_section_default(self):
     980          cf = self.newconfig()
     981          self.assertRaises(ValueError, cf.add_section, self.default_section)
     982  
     983      def test_defaults_keyword(self):
     984          """bpo-23835 fix for ConfigParser"""
     985          cf = self.newconfig(defaults={1: 2.4})
     986          self.assertEqual(cf[self.default_section]['1'], '2.4')
     987          self.assertAlmostEqual(cf[self.default_section].getfloat('1'), 2.4)
     988          cf = self.newconfig(defaults={"A": 5.2})
     989          self.assertEqual(cf[self.default_section]['a'], '5.2')
     990          self.assertAlmostEqual(cf[self.default_section].getfloat('a'), 5.2)
     991  
     992  
     993  class ESC[4;38;5;81mConfigParserTestCaseNoInterpolation(ESC[4;38;5;149mBasicTestCase, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     994      config_class = configparser.ConfigParser
     995      interpolation = None
     996      ini = textwrap.dedent("""
     997          [numbers]
     998          one = 1
     999          two = %(one)s * 2
    1000          three = ${common:one} * 3
    1001  
    1002          [hexen]
    1003          sixteen = ${numbers:two} * 8
    1004      """).strip()
    1005  
    1006      def assertMatchesIni(self, cf):
    1007          self.assertEqual(cf['numbers']['one'], '1')
    1008          self.assertEqual(cf['numbers']['two'], '%(one)s * 2')
    1009          self.assertEqual(cf['numbers']['three'], '${common:one} * 3')
    1010          self.assertEqual(cf['hexen']['sixteen'], '${numbers:two} * 8')
    1011  
    1012      def test_no_interpolation(self):
    1013          cf = self.fromstring(self.ini)
    1014          self.assertMatchesIni(cf)
    1015  
    1016      def test_empty_case(self):
    1017          cf = self.newconfig()
    1018          self.assertIsNone(cf.read_string(""))
    1019  
    1020      def test_none_as_default_interpolation(self):
    1021          class ESC[4;38;5;81mCustomConfigParser(ESC[4;38;5;149mconfigparserESC[4;38;5;149m.ESC[4;38;5;149mConfigParser):
    1022              _DEFAULT_INTERPOLATION = None
    1023  
    1024          cf = CustomConfigParser()
    1025          cf.read_string(self.ini)
    1026          self.assertMatchesIni(cf)
    1027  
    1028  
    1029  class ESC[4;38;5;81mConfigParserTestCaseLegacyInterpolation(ESC[4;38;5;149mConfigParserTestCase):
    1030      config_class = configparser.ConfigParser
    1031      with warnings.catch_warnings():
    1032          warnings.simplefilter("ignore", DeprecationWarning)
    1033          interpolation = configparser.LegacyInterpolation()
    1034  
    1035      def test_set_malformatted_interpolation(self):
    1036          cf = self.fromstring("[sect]\n"
    1037                               "option1{eq}foo\n".format(eq=self.delimiters[0]))
    1038  
    1039          self.assertEqual(cf.get('sect', "option1"), "foo")
    1040  
    1041          cf.set("sect", "option1", "%foo")
    1042          self.assertEqual(cf.get('sect', "option1"), "%foo")
    1043          cf.set("sect", "option1", "foo%")
    1044          self.assertEqual(cf.get('sect', "option1"), "foo%")
    1045          cf.set("sect", "option1", "f%oo")
    1046          self.assertEqual(cf.get('sect', "option1"), "f%oo")
    1047  
    1048          # bug #5741: double percents are *not* malformed
    1049          cf.set("sect", "option2", "foo%%bar")
    1050          self.assertEqual(cf.get("sect", "option2"), "foo%%bar")
    1051  
    1052  
    1053  class ESC[4;38;5;81mConfigParserTestCaseInvalidInterpolationType(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
    1054      def test_error_on_wrong_type_for_interpolation(self):
    1055          for value in [configparser.ExtendedInterpolation,  42,  "a string"]:
    1056              with self.subTest(value=value):
    1057                  with self.assertRaises(TypeError):
    1058                      configparser.ConfigParser(interpolation=value)
    1059  
    1060  
    1061  class ESC[4;38;5;81mConfigParserTestCaseNonStandardDelimiters(ESC[4;38;5;149mConfigParserTestCase):
    1062      delimiters = (':=', '$')
    1063      comment_prefixes = ('//', '"')
    1064      inline_comment_prefixes = ('//', '"')
    1065  
    1066  
    1067  class ESC[4;38;5;81mConfigParserTestCaseNonStandardDefaultSection(ESC[4;38;5;149mConfigParserTestCase):
    1068      default_section = 'general'
    1069  
    1070  
    1071  class ESC[4;38;5;81mMultilineValuesTestCase(ESC[4;38;5;149mBasicTestCase, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
    1072      config_class = configparser.ConfigParser
    1073      wonderful_spam = ("I'm having spam spam spam spam "
    1074                        "spam spam spam beaked beans spam "
    1075                        "spam spam and spam!").replace(' ', '\t\n')
    1076  
    1077      def setUp(self):
    1078          cf = self.newconfig()
    1079          for i in range(100):
    1080              s = 'section{}'.format(i)
    1081              cf.add_section(s)
    1082              for j in range(10):
    1083                  cf.set(s, 'lovely_spam{}'.format(j), self.wonderful_spam)
    1084          with open(os_helper.TESTFN, 'w', encoding="utf-8") as f:
    1085              cf.write(f)
    1086  
    1087      def tearDown(self):
    1088          os.unlink(os_helper.TESTFN)
    1089  
    1090      def test_dominating_multiline_values(self):
    1091          # We're reading from file because this is where the code changed
    1092          # during performance updates in Python 3.2
    1093          cf_from_file = self.newconfig()
    1094          with open(os_helper.TESTFN, encoding="utf-8") as f:
    1095              cf_from_file.read_file(f)
    1096          self.assertEqual(cf_from_file.get('section8', 'lovely_spam4'),
    1097                           self.wonderful_spam.replace('\t\n', '\n'))
    1098  
    1099  
    1100  class ESC[4;38;5;81mRawConfigParserTestCase(ESC[4;38;5;149mBasicTestCase, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
    1101      config_class = configparser.RawConfigParser
    1102  
    1103      def test_interpolation(self):
    1104          cf = self.get_interpolation_config()
    1105          eq = self.assertEqual
    1106          eq(cf.get("Foo", "bar"),
    1107             "something %(with1)s interpolation (1 step)")
    1108          eq(cf.get("Foo", "bar9"),
    1109             "something %(with9)s lots of interpolation (9 steps)")
    1110          eq(cf.get("Foo", "bar10"),
    1111             "something %(with10)s lots of interpolation (10 steps)")
    1112          eq(cf.get("Foo", "bar11"),
    1113             "something %(with11)s lots of interpolation (11 steps)")
    1114  
    1115      def test_items(self):
    1116          self.check_items_config([('default', '<default>'),
    1117                                   ('getdefault', '|%(default)s|'),
    1118                                   ('key', '|%(name)s|'),
    1119                                   ('name', '%(value)s')])
    1120  
    1121      def test_set_nonstring_types(self):
    1122          cf = self.newconfig()
    1123          cf.add_section('non-string')
    1124          cf.set('non-string', 'int', 1)
    1125          cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13])
    1126          cf.set('non-string', 'dict', {'pi': 3.14159})
    1127          self.assertEqual(cf.get('non-string', 'int'), 1)
    1128          self.assertEqual(cf.get('non-string', 'list'),
    1129                           [0, 1, 1, 2, 3, 5, 8, 13])
    1130          self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159})
    1131          cf.add_section(123)
    1132          cf.set(123, 'this is sick', True)
    1133          self.assertEqual(cf.get(123, 'this is sick'), True)
    1134          if cf._dict is configparser._default_dict:
    1135              # would not work for SortedDict; only checking for the most common
    1136              # default dictionary (dict)
    1137              cf.optionxform = lambda x: x
    1138              cf.set('non-string', 1, 1)
    1139              self.assertEqual(cf.get('non-string', 1), 1)
    1140  
    1141      def test_defaults_keyword(self):
    1142          """bpo-23835 legacy behavior for RawConfigParser"""
    1143          with self.assertRaises(AttributeError) as ctx:
    1144              self.newconfig(defaults={1: 2.4})
    1145          err = ctx.exception
    1146          self.assertEqual(str(err), "'int' object has no attribute 'lower'")
    1147          cf = self.newconfig(defaults={"A": 5.2})
    1148          self.assertAlmostEqual(cf[self.default_section]['a'], 5.2)
    1149  
    1150  
    1151  class ESC[4;38;5;81mRawConfigParserTestCaseNonStandardDelimiters(ESC[4;38;5;149mRawConfigParserTestCase):
    1152      delimiters = (':=', '$')
    1153      comment_prefixes = ('//', '"')
    1154      inline_comment_prefixes = ('//', '"')
    1155  
    1156  
    1157  class ESC[4;38;5;81mRawConfigParserTestSambaConf(ESC[4;38;5;149mCfgParserTestCaseClass, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
    1158      config_class = configparser.RawConfigParser
    1159      comment_prefixes = ('#', ';', '----')
    1160      inline_comment_prefixes = ('//',)
    1161      empty_lines_in_values = False
    1162  
    1163      def test_reading(self):
    1164          smbconf = support.findfile("cfgparser.2", subdir="configdata")
    1165          # check when we pass a mix of readable and non-readable files:
    1166          cf = self.newconfig()
    1167          parsed_files = cf.read([smbconf, "nonexistent-file"], encoding='utf-8')
    1168          self.assertEqual(parsed_files, [smbconf])
    1169          sections = ['global', 'homes', 'printers',
    1170                      'print$', 'pdf-generator', 'tmp', 'Agustin']
    1171          self.assertEqual(cf.sections(), sections)
    1172          self.assertEqual(cf.get("global", "workgroup"), "MDKGROUP")
    1173          self.assertEqual(cf.getint("global", "max log size"), 50)
    1174          self.assertEqual(cf.get("global", "hosts allow"), "127.")
    1175          self.assertEqual(cf.get("tmp", "echo command"), "cat %s; rm %s")
    1176  
    1177  class ESC[4;38;5;81mConfigParserTestCaseExtendedInterpolation(ESC[4;38;5;149mBasicTestCase, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
    1178      config_class = configparser.ConfigParser
    1179      interpolation = configparser.ExtendedInterpolation()
    1180      default_section = 'common'
    1181      strict = True
    1182  
    1183      def fromstring(self, string, defaults=None, optionxform=None):
    1184          cf = self.newconfig(defaults)
    1185          if optionxform:
    1186              cf.optionxform = optionxform
    1187          cf.read_string(string)
    1188          return cf
    1189  
    1190      def test_extended_interpolation(self):
    1191          cf = self.fromstring(textwrap.dedent("""
    1192              [common]
    1193              favourite Beatle = Paul
    1194              favourite color = green
    1195  
    1196              [tom]
    1197              favourite band = ${favourite color} day
    1198              favourite pope = John ${favourite Beatle} II
    1199              sequel = ${favourite pope}I
    1200  
    1201              [ambv]
    1202              favourite Beatle = George
    1203              son of Edward VII = ${favourite Beatle} V
    1204              son of George V = ${son of Edward VII}I
    1205  
    1206              [stanley]
    1207              favourite Beatle = ${ambv:favourite Beatle}
    1208              favourite pope = ${tom:favourite pope}
    1209              favourite color = black
    1210              favourite state of mind = paranoid
    1211              favourite movie = soylent ${common:favourite color}
    1212              favourite song = ${favourite color} sabbath - ${favourite state of mind}
    1213          """).strip())
    1214  
    1215          eq = self.assertEqual
    1216          eq(cf['common']['favourite Beatle'], 'Paul')
    1217          eq(cf['common']['favourite color'], 'green')
    1218          eq(cf['tom']['favourite Beatle'], 'Paul')
    1219          eq(cf['tom']['favourite color'], 'green')
    1220          eq(cf['tom']['favourite band'], 'green day')
    1221          eq(cf['tom']['favourite pope'], 'John Paul II')
    1222          eq(cf['tom']['sequel'], 'John Paul III')
    1223          eq(cf['ambv']['favourite Beatle'], 'George')
    1224          eq(cf['ambv']['favourite color'], 'green')
    1225          eq(cf['ambv']['son of Edward VII'], 'George V')
    1226          eq(cf['ambv']['son of George V'], 'George VI')
    1227          eq(cf['stanley']['favourite Beatle'], 'George')
    1228          eq(cf['stanley']['favourite color'], 'black')
    1229          eq(cf['stanley']['favourite state of mind'], 'paranoid')
    1230          eq(cf['stanley']['favourite movie'], 'soylent green')
    1231          eq(cf['stanley']['favourite pope'], 'John Paul II')
    1232          eq(cf['stanley']['favourite song'],
    1233             'black sabbath - paranoid')
    1234  
    1235      def test_endless_loop(self):
    1236          cf = self.fromstring(textwrap.dedent("""
    1237              [one for you]
    1238              ping = ${one for me:pong}
    1239  
    1240              [one for me]
    1241              pong = ${one for you:ping}
    1242  
    1243              [selfish]
    1244              me = ${me}
    1245          """).strip())
    1246  
    1247          with self.assertRaises(configparser.InterpolationDepthError):
    1248              cf['one for you']['ping']
    1249          with self.assertRaises(configparser.InterpolationDepthError):
    1250              cf['selfish']['me']
    1251  
    1252      def test_strange_options(self):
    1253          cf = self.fromstring("""
    1254              [dollars]
    1255              $var = $$value
    1256              $var2 = ${$var}
    1257              ${sick} = cannot interpolate me
    1258  
    1259              [interpolated]
    1260              $other = ${dollars:$var}
    1261              $trying = ${dollars:${sick}}
    1262          """)
    1263  
    1264          self.assertEqual(cf['dollars']['$var'], '$value')
    1265          self.assertEqual(cf['interpolated']['$other'], '$value')
    1266          self.assertEqual(cf['dollars']['${sick}'], 'cannot interpolate me')
    1267          exception_class = configparser.InterpolationMissingOptionError
    1268          with self.assertRaises(exception_class) as cm:
    1269              cf['interpolated']['$trying']
    1270          self.assertEqual(cm.exception.reference, 'dollars:${sick')
    1271          self.assertEqual(cm.exception.args[2], '${dollars:${sick}}') #rawval
    1272  
    1273      def test_case_sensitivity_basic(self):
    1274          ini = textwrap.dedent("""
    1275              [common]
    1276              optionlower = value
    1277              OptionUpper = Value
    1278  
    1279              [Common]
    1280              optionlower = a better ${common:optionlower}
    1281              OptionUpper = A Better ${common:OptionUpper}
    1282  
    1283              [random]
    1284              foolower = ${common:optionlower} redefined
    1285              FooUpper = ${Common:OptionUpper} Redefined
    1286          """).strip()
    1287  
    1288          cf = self.fromstring(ini)
    1289          eq = self.assertEqual
    1290          eq(cf['common']['optionlower'], 'value')
    1291          eq(cf['common']['OptionUpper'], 'Value')
    1292          eq(cf['Common']['optionlower'], 'a better value')
    1293          eq(cf['Common']['OptionUpper'], 'A Better Value')
    1294          eq(cf['random']['foolower'], 'value redefined')
    1295          eq(cf['random']['FooUpper'], 'A Better Value Redefined')
    1296  
    1297      def test_case_sensitivity_conflicts(self):
    1298          ini = textwrap.dedent("""
    1299              [common]
    1300              option = value
    1301              Option = Value
    1302  
    1303              [Common]
    1304              option = a better ${common:option}
    1305              Option = A Better ${common:Option}
    1306  
    1307              [random]
    1308              foo = ${common:option} redefined
    1309              Foo = ${Common:Option} Redefined
    1310          """).strip()
    1311          with self.assertRaises(configparser.DuplicateOptionError):
    1312              cf = self.fromstring(ini)
    1313  
    1314          # raw options
    1315          cf = self.fromstring(ini, optionxform=lambda opt: opt)
    1316          eq = self.assertEqual
    1317          eq(cf['common']['option'], 'value')
    1318          eq(cf['common']['Option'], 'Value')
    1319          eq(cf['Common']['option'], 'a better value')
    1320          eq(cf['Common']['Option'], 'A Better Value')
    1321          eq(cf['random']['foo'], 'value redefined')
    1322          eq(cf['random']['Foo'], 'A Better Value Redefined')
    1323  
    1324      def test_other_errors(self):
    1325          cf = self.fromstring("""
    1326              [interpolation fail]
    1327              case1 = ${where's the brace
    1328              case2 = ${does_not_exist}
    1329              case3 = ${wrong_section:wrong_value}
    1330              case4 = ${i:like:colon:characters}
    1331              case5 = $100 for Fail No 5!
    1332          """)
    1333  
    1334          with self.assertRaises(configparser.InterpolationSyntaxError):
    1335              cf['interpolation fail']['case1']
    1336          with self.assertRaises(configparser.InterpolationMissingOptionError):
    1337              cf['interpolation fail']['case2']
    1338          with self.assertRaises(configparser.InterpolationMissingOptionError):
    1339              cf['interpolation fail']['case3']
    1340          with self.assertRaises(configparser.InterpolationSyntaxError):
    1341              cf['interpolation fail']['case4']
    1342          with self.assertRaises(configparser.InterpolationSyntaxError):
    1343              cf['interpolation fail']['case5']
    1344          with self.assertRaises(ValueError):
    1345              cf['interpolation fail']['case6'] = "BLACK $ABBATH"
    1346  
    1347  
    1348  class ESC[4;38;5;81mConfigParserTestCaseNoValue(ESC[4;38;5;149mConfigParserTestCase):
    1349      allow_no_value = True
    1350  
    1351  
    1352  class ESC[4;38;5;81mConfigParserTestCaseTrickyFile(ESC[4;38;5;149mCfgParserTestCaseClass, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
    1353      config_class = configparser.ConfigParser
    1354      delimiters = {'='}
    1355      comment_prefixes = {'#'}
    1356      allow_no_value = True
    1357  
    1358      def test_cfgparser_dot_3(self):
    1359          tricky = support.findfile("cfgparser.3", subdir="configdata")
    1360          cf = self.newconfig()
    1361          self.assertEqual(len(cf.read(tricky, encoding='utf-8')), 1)
    1362          self.assertEqual(cf.sections(), ['strange',
    1363                                           'corruption',
    1364                                           'yeah, sections can be '
    1365                                           'indented as well',
    1366                                           'another one!',
    1367                                           'no values here',
    1368                                           'tricky interpolation',
    1369                                           'more interpolation'])
    1370          self.assertEqual(cf.getint(self.default_section, 'go',
    1371                                     vars={'interpolate': '-1'}), -1)
    1372          with self.assertRaises(ValueError):
    1373              # no interpolation will happen
    1374              cf.getint(self.default_section, 'go', raw=True,
    1375                        vars={'interpolate': '-1'})
    1376          self.assertEqual(len(cf.get('strange', 'other').split('\n')), 4)
    1377          self.assertEqual(len(cf.get('corruption', 'value').split('\n')), 10)
    1378          longname = 'yeah, sections can be indented as well'
    1379          self.assertFalse(cf.getboolean(longname, 'are they subsections'))
    1380          self.assertEqual(cf.get(longname, 'lets use some Unicode'), '片仮名')
    1381          self.assertEqual(len(cf.items('another one!')), 5) # 4 in section and
    1382                                                             # `go` from DEFAULT
    1383          with self.assertRaises(configparser.InterpolationMissingOptionError):
    1384              cf.items('no values here')
    1385          self.assertEqual(cf.get('tricky interpolation', 'lets'), 'do this')
    1386          self.assertEqual(cf.get('tricky interpolation', 'lets'),
    1387                           cf.get('tricky interpolation', 'go'))
    1388          self.assertEqual(cf.get('more interpolation', 'lets'), 'go shopping')
    1389  
    1390      def test_unicode_failure(self):
    1391          tricky = support.findfile("cfgparser.3", subdir="configdata")
    1392          cf = self.newconfig()
    1393          with self.assertRaises(UnicodeDecodeError):
    1394              cf.read(tricky, encoding='ascii')
    1395  
    1396  
    1397  class ESC[4;38;5;81mIssue7005TestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
    1398      """Test output when None is set() as a value and allow_no_value == False.
    1399  
    1400      http://bugs.python.org/issue7005
    1401  
    1402      """
    1403  
    1404      expected_output = "[section]\noption = None\n\n"
    1405  
    1406      def prepare(self, config_class):
    1407          # This is the default, but that's the point.
    1408          cp = config_class(allow_no_value=False)
    1409          cp.add_section("section")
    1410          cp.set("section", "option", None)
    1411          sio = io.StringIO()
    1412          cp.write(sio)
    1413          return sio.getvalue()
    1414  
    1415      def test_none_as_value_stringified(self):
    1416          cp = configparser.ConfigParser(allow_no_value=False)
    1417          cp.add_section("section")
    1418          with self.assertRaises(TypeError):
    1419              cp.set("section", "option", None)
    1420  
    1421      def test_none_as_value_stringified_raw(self):
    1422          output = self.prepare(configparser.RawConfigParser)
    1423          self.assertEqual(output, self.expected_output)
    1424  
    1425  
    1426  class ESC[4;38;5;81mSortedTestCase(ESC[4;38;5;149mRawConfigParserTestCase):
    1427      dict_type = SortedDict
    1428  
    1429      def test_sorted(self):
    1430          cf = self.fromstring("[b]\n"
    1431                               "o4=1\n"
    1432                               "o3=2\n"
    1433                               "o2=3\n"
    1434                               "o1=4\n"
    1435                               "[a]\n"
    1436                               "k=v\n")
    1437          output = io.StringIO()
    1438          cf.write(output)
    1439          self.assertEqual(output.getvalue(),
    1440                           "[a]\n"
    1441                           "k = v\n\n"
    1442                           "[b]\n"
    1443                           "o1 = 4\n"
    1444                           "o2 = 3\n"
    1445                           "o3 = 2\n"
    1446                           "o4 = 1\n\n")
    1447  
    1448  
    1449  class ESC[4;38;5;81mCompatibleTestCase(ESC[4;38;5;149mCfgParserTestCaseClass, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
    1450      config_class = configparser.RawConfigParser
    1451      comment_prefixes = '#;'
    1452      inline_comment_prefixes = ';'
    1453  
    1454      def test_comment_handling(self):
    1455          config_string = textwrap.dedent("""\
    1456          [Commented Bar]
    1457          baz=qwe ; a comment
    1458          foo: bar # not a comment!
    1459          # but this is a comment
    1460          ; another comment
    1461          quirk: this;is not a comment
    1462          ; a space must precede an inline comment
    1463          """)
    1464          cf = self.fromstring(config_string)
    1465          self.assertEqual(cf.get('Commented Bar', 'foo'),
    1466                           'bar # not a comment!')
    1467          self.assertEqual(cf.get('Commented Bar', 'baz'), 'qwe')
    1468          self.assertEqual(cf.get('Commented Bar', 'quirk'),
    1469                           'this;is not a comment')
    1470  
    1471  class ESC[4;38;5;81mCopyTestCase(ESC[4;38;5;149mBasicTestCase, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
    1472      config_class = configparser.ConfigParser
    1473  
    1474      def fromstring(self, string, defaults=None):
    1475          cf = self.newconfig(defaults)
    1476          cf.read_string(string)
    1477          cf_copy = self.newconfig()
    1478          cf_copy.read_dict(cf)
    1479          # we have to clean up option duplicates that appeared because of
    1480          # the magic DEFAULTSECT behaviour.
    1481          for section in cf_copy.values():
    1482              if section.name == self.default_section:
    1483                  continue
    1484              for default, value in cf[self.default_section].items():
    1485                  if section[default] == value:
    1486                      del section[default]
    1487          return cf_copy
    1488  
    1489  
    1490  class ESC[4;38;5;81mFakeFile:
    1491      def __init__(self):
    1492          file_path = support.findfile("cfgparser.1", subdir="configdata")
    1493          with open(file_path, encoding="utf-8") as f:
    1494              self.lines = f.readlines()
    1495              self.lines.reverse()
    1496  
    1497      def readline(self):
    1498          if len(self.lines):
    1499              return self.lines.pop()
    1500          return ''
    1501  
    1502  
    1503  def readline_generator(f):
    1504      """As advised in Doc/library/configparser.rst."""
    1505      line = f.readline()
    1506      while line:
    1507          yield line
    1508          line = f.readline()
    1509  
    1510  
    1511  class ESC[4;38;5;81mReadFileTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
    1512      def test_file(self):
    1513          file_paths = [support.findfile("cfgparser.1", subdir="configdata")]
    1514          try:
    1515              file_paths.append(file_paths[0].encode('utf8'))
    1516          except UnicodeEncodeError:
    1517              pass   # unfortunately we can't test bytes on this path
    1518          for file_path in file_paths:
    1519              parser = configparser.ConfigParser()
    1520              with open(file_path, encoding="utf-8") as f:
    1521                  parser.read_file(f)
    1522              self.assertIn("Foo Bar", parser)
    1523              self.assertIn("foo", parser["Foo Bar"])
    1524              self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
    1525  
    1526      def test_iterable(self):
    1527          lines = textwrap.dedent("""
    1528          [Foo Bar]
    1529          foo=newbar""").strip().split('\n')
    1530          parser = configparser.ConfigParser()
    1531          parser.read_file(lines)
    1532          self.assertIn("Foo Bar", parser)
    1533          self.assertIn("foo", parser["Foo Bar"])
    1534          self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
    1535  
    1536      def test_readline_generator(self):
    1537          """Issue #11670."""
    1538          parser = configparser.ConfigParser()
    1539          with self.assertRaises(TypeError):
    1540              parser.read_file(FakeFile())
    1541          parser.read_file(readline_generator(FakeFile()))
    1542          self.assertIn("Foo Bar", parser)
    1543          self.assertIn("foo", parser["Foo Bar"])
    1544          self.assertEqual(parser["Foo Bar"]["foo"], "newbar")
    1545  
    1546      def test_source_as_bytes(self):
    1547          """Issue #18260."""
    1548          lines = textwrap.dedent("""
    1549          [badbad]
    1550          [badbad]""").strip().split('\n')
    1551          parser = configparser.ConfigParser()
    1552          with self.assertRaises(configparser.DuplicateSectionError) as dse:
    1553              parser.read_file(lines, source=b"badbad")
    1554          self.assertEqual(
    1555              str(dse.exception),
    1556              "While reading from b'badbad' [line  2]: section 'badbad' "
    1557              "already exists"
    1558          )
    1559          lines = textwrap.dedent("""
    1560          [badbad]
    1561          bad = bad
    1562          bad = bad""").strip().split('\n')
    1563          parser = configparser.ConfigParser()
    1564          with self.assertRaises(configparser.DuplicateOptionError) as dse:
    1565              parser.read_file(lines, source=b"badbad")
    1566          self.assertEqual(
    1567              str(dse.exception),
    1568              "While reading from b'badbad' [line  3]: option 'bad' in section "
    1569              "'badbad' already exists"
    1570          )
    1571          lines = textwrap.dedent("""
    1572          [badbad]
    1573          = bad""").strip().split('\n')
    1574          parser = configparser.ConfigParser()
    1575          with self.assertRaises(configparser.ParsingError) as dse:
    1576              parser.read_file(lines, source=b"badbad")
    1577          self.assertEqual(
    1578              str(dse.exception),
    1579              "Source contains parsing errors: b'badbad'\n\t[line  2]: '= bad'"
    1580          )
    1581          lines = textwrap.dedent("""
    1582          [badbad
    1583          bad = bad""").strip().split('\n')
    1584          parser = configparser.ConfigParser()
    1585          with self.assertRaises(configparser.MissingSectionHeaderError) as dse:
    1586              parser.read_file(lines, source=b"badbad")
    1587          self.assertEqual(
    1588              str(dse.exception),
    1589              "File contains no section headers.\nfile: b'badbad', line: 1\n"
    1590              "'[badbad'"
    1591          )
    1592  
    1593  
    1594  class ESC[4;38;5;81mCoverageOneHundredTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
    1595      """Covers edge cases in the codebase."""
    1596  
    1597      def test_duplicate_option_error(self):
    1598          error = configparser.DuplicateOptionError('section', 'option')
    1599          self.assertEqual(error.section, 'section')
    1600          self.assertEqual(error.option, 'option')
    1601          self.assertEqual(error.source, None)
    1602          self.assertEqual(error.lineno, None)
    1603          self.assertEqual(error.args, ('section', 'option', None, None))
    1604          self.assertEqual(str(error), "Option 'option' in section 'section' "
    1605                                       "already exists")
    1606  
    1607      def test_interpolation_depth_error(self):
    1608          error = configparser.InterpolationDepthError('option', 'section',
    1609                                                       'rawval')
    1610          self.assertEqual(error.args, ('option', 'section', 'rawval'))
    1611          self.assertEqual(error.option, 'option')
    1612          self.assertEqual(error.section, 'section')
    1613  
    1614      def test_parsing_error(self):
    1615          with self.assertRaises(ValueError) as cm:
    1616              configparser.ParsingError()
    1617          self.assertEqual(str(cm.exception), "Required argument `source' not "
    1618                                              "given.")
    1619          with self.assertRaises(ValueError) as cm:
    1620              configparser.ParsingError(source='source', filename='filename')
    1621          self.assertEqual(str(cm.exception), "Cannot specify both `filename' "
    1622                                              "and `source'. Use `source'.")
    1623          error = configparser.ParsingError(filename='source')
    1624          self.assertEqual(error.source, 'source')
    1625          with warnings.catch_warnings(record=True) as w:
    1626              warnings.simplefilter("always", DeprecationWarning)
    1627              self.assertEqual(error.filename, 'source')
    1628              error.filename = 'filename'
    1629              self.assertEqual(error.source, 'filename')
    1630          for warning in w:
    1631              self.assertTrue(warning.category is DeprecationWarning)
    1632  
    1633      def test_interpolation_validation(self):
    1634          parser = configparser.ConfigParser()
    1635          parser.read_string("""
    1636              [section]
    1637              invalid_percent = %
    1638              invalid_reference = %(()
    1639              invalid_variable = %(does_not_exist)s
    1640          """)
    1641          with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
    1642              parser['section']['invalid_percent']
    1643          self.assertEqual(str(cm.exception), "'%' must be followed by '%' or "
    1644                                              "'(', found: '%'")
    1645          with self.assertRaises(configparser.InterpolationSyntaxError) as cm:
    1646              parser['section']['invalid_reference']
    1647          self.assertEqual(str(cm.exception), "bad interpolation variable "
    1648                                              "reference '%(()'")
    1649  
    1650      def test_readfp_deprecation(self):
    1651          sio = io.StringIO("""
    1652          [section]
    1653          option = value
    1654          """)
    1655          parser = configparser.ConfigParser()
    1656          with warnings.catch_warnings(record=True) as w:
    1657              warnings.simplefilter("always", DeprecationWarning)
    1658              parser.readfp(sio, filename='StringIO')
    1659          for warning in w:
    1660              self.assertTrue(warning.category is DeprecationWarning)
    1661          self.assertEqual(len(parser), 2)
    1662          self.assertEqual(parser['section']['option'], 'value')
    1663  
    1664      def test_safeconfigparser_deprecation(self):
    1665          with warnings.catch_warnings(record=True) as w:
    1666              warnings.simplefilter("always", DeprecationWarning)
    1667              parser = configparser.SafeConfigParser()
    1668          for warning in w:
    1669              self.assertTrue(warning.category is DeprecationWarning)
    1670  
    1671      def test_legacyinterpolation_deprecation(self):
    1672          with warnings.catch_warnings(record=True) as w:
    1673              warnings.simplefilter("always", DeprecationWarning)
    1674              configparser.LegacyInterpolation()
    1675          self.assertGreaterEqual(len(w), 1)
    1676          for warning in w:
    1677              self.assertIs(warning.category, DeprecationWarning)
    1678  
    1679      def test_sectionproxy_repr(self):
    1680          parser = configparser.ConfigParser()
    1681          parser.read_string("""
    1682              [section]
    1683              key = value
    1684          """)
    1685          self.assertEqual(repr(parser['section']), '<Section: section>')
    1686  
    1687      def test_inconsistent_converters_state(self):
    1688          parser = configparser.ConfigParser()
    1689          import decimal
    1690          parser.converters['decimal'] = decimal.Decimal
    1691          parser.read_string("""
    1692              [s1]
    1693              one = 1
    1694              [s2]
    1695              two = 2
    1696          """)
    1697          self.assertIn('decimal', parser.converters)
    1698          self.assertEqual(parser.getdecimal('s1', 'one'), 1)
    1699          self.assertEqual(parser.getdecimal('s2', 'two'), 2)
    1700          self.assertEqual(parser['s1'].getdecimal('one'), 1)
    1701          self.assertEqual(parser['s2'].getdecimal('two'), 2)
    1702          del parser.getdecimal
    1703          with self.assertRaises(AttributeError):
    1704              parser.getdecimal('s1', 'one')
    1705          self.assertIn('decimal', parser.converters)
    1706          del parser.converters['decimal']
    1707          self.assertNotIn('decimal', parser.converters)
    1708          with self.assertRaises(AttributeError):
    1709              parser.getdecimal('s1', 'one')
    1710          with self.assertRaises(AttributeError):
    1711              parser['s1'].getdecimal('one')
    1712          with self.assertRaises(AttributeError):
    1713              parser['s2'].getdecimal('two')
    1714  
    1715  
    1716  class ESC[4;38;5;81mExceptionPicklingTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
    1717      """Tests for issue #13760: ConfigParser exceptions are not picklable."""
    1718  
    1719      def test_error(self):
    1720          import pickle
    1721          e1 = configparser.Error('value')
    1722          for proto in range(pickle.HIGHEST_PROTOCOL + 1):
    1723              pickled = pickle.dumps(e1, proto)
    1724              e2 = pickle.loads(pickled)
    1725              self.assertEqual(e1.message, e2.message)
    1726              self.assertEqual(repr(e1), repr(e2))
    1727  
    1728      def test_nosectionerror(self):
    1729          import pickle
    1730          e1 = configparser.NoSectionError('section')
    1731          for proto in range(pickle.HIGHEST_PROTOCOL + 1):
    1732              pickled = pickle.dumps(e1, proto)
    1733              e2 = pickle.loads(pickled)
    1734              self.assertEqual(e1.message, e2.message)
    1735              self.assertEqual(e1.args, e2.args)
    1736              self.assertEqual(e1.section, e2.section)
    1737              self.assertEqual(repr(e1), repr(e2))
    1738  
    1739      def test_nooptionerror(self):
    1740          import pickle
    1741          e1 = configparser.NoOptionError('option', 'section')
    1742          for proto in range(pickle.HIGHEST_PROTOCOL + 1):
    1743              pickled = pickle.dumps(e1, proto)
    1744              e2 = pickle.loads(pickled)
    1745              self.assertEqual(e1.message, e2.message)
    1746              self.assertEqual(e1.args, e2.args)
    1747              self.assertEqual(e1.section, e2.section)
    1748              self.assertEqual(e1.option, e2.option)
    1749              self.assertEqual(repr(e1), repr(e2))
    1750  
    1751      def test_duplicatesectionerror(self):
    1752          import pickle
    1753          e1 = configparser.DuplicateSectionError('section', 'source', 123)
    1754          for proto in range(pickle.HIGHEST_PROTOCOL + 1):
    1755              pickled = pickle.dumps(e1, proto)
    1756              e2 = pickle.loads(pickled)
    1757              self.assertEqual(e1.message, e2.message)
    1758              self.assertEqual(e1.args, e2.args)
    1759              self.assertEqual(e1.section, e2.section)
    1760              self.assertEqual(e1.source, e2.source)
    1761              self.assertEqual(e1.lineno, e2.lineno)
    1762              self.assertEqual(repr(e1), repr(e2))
    1763  
    1764      def test_duplicateoptionerror(self):
    1765          import pickle
    1766          e1 = configparser.DuplicateOptionError('section', 'option', 'source',
    1767              123)
    1768          for proto in range(pickle.HIGHEST_PROTOCOL + 1):
    1769              pickled = pickle.dumps(e1, proto)
    1770              e2 = pickle.loads(pickled)
    1771              self.assertEqual(e1.message, e2.message)
    1772              self.assertEqual(e1.args, e2.args)
    1773              self.assertEqual(e1.section, e2.section)
    1774              self.assertEqual(e1.option, e2.option)
    1775              self.assertEqual(e1.source, e2.source)
    1776              self.assertEqual(e1.lineno, e2.lineno)
    1777              self.assertEqual(repr(e1), repr(e2))
    1778  
    1779      def test_interpolationerror(self):
    1780          import pickle
    1781          e1 = configparser.InterpolationError('option', 'section', 'msg')
    1782          for proto in range(pickle.HIGHEST_PROTOCOL + 1):
    1783              pickled = pickle.dumps(e1, proto)
    1784              e2 = pickle.loads(pickled)
    1785              self.assertEqual(e1.message, e2.message)
    1786              self.assertEqual(e1.args, e2.args)
    1787              self.assertEqual(e1.section, e2.section)
    1788              self.assertEqual(e1.option, e2.option)
    1789              self.assertEqual(repr(e1), repr(e2))
    1790  
    1791      def test_interpolationmissingoptionerror(self):
    1792          import pickle
    1793          e1 = configparser.InterpolationMissingOptionError('option', 'section',
    1794              'rawval', 'reference')
    1795          for proto in range(pickle.HIGHEST_PROTOCOL + 1):
    1796              pickled = pickle.dumps(e1, proto)
    1797              e2 = pickle.loads(pickled)
    1798              self.assertEqual(e1.message, e2.message)
    1799              self.assertEqual(e1.args, e2.args)
    1800              self.assertEqual(e1.section, e2.section)
    1801              self.assertEqual(e1.option, e2.option)
    1802              self.assertEqual(e1.reference, e2.reference)
    1803              self.assertEqual(repr(e1), repr(e2))
    1804  
    1805      def test_interpolationsyntaxerror(self):
    1806          import pickle
    1807          e1 = configparser.InterpolationSyntaxError('option', 'section', 'msg')
    1808          for proto in range(pickle.HIGHEST_PROTOCOL + 1):
    1809              pickled = pickle.dumps(e1, proto)
    1810              e2 = pickle.loads(pickled)
    1811              self.assertEqual(e1.message, e2.message)
    1812              self.assertEqual(e1.args, e2.args)
    1813              self.assertEqual(e1.section, e2.section)
    1814              self.assertEqual(e1.option, e2.option)
    1815              self.assertEqual(repr(e1), repr(e2))
    1816  
    1817      def test_interpolationdeptherror(self):
    1818          import pickle
    1819          e1 = configparser.InterpolationDepthError('option', 'section',
    1820              'rawval')
    1821          for proto in range(pickle.HIGHEST_PROTOCOL + 1):
    1822              pickled = pickle.dumps(e1, proto)
    1823              e2 = pickle.loads(pickled)
    1824              self.assertEqual(e1.message, e2.message)
    1825              self.assertEqual(e1.args, e2.args)
    1826              self.assertEqual(e1.section, e2.section)
    1827              self.assertEqual(e1.option, e2.option)
    1828              self.assertEqual(repr(e1), repr(e2))
    1829  
    1830      def test_parsingerror(self):
    1831          import pickle
    1832          e1 = configparser.ParsingError('source')
    1833          e1.append(1, 'line1')
    1834          e1.append(2, 'line2')
    1835          e1.append(3, 'line3')
    1836          for proto in range(pickle.HIGHEST_PROTOCOL + 1):
    1837              pickled = pickle.dumps(e1, proto)
    1838              e2 = pickle.loads(pickled)
    1839              self.assertEqual(e1.message, e2.message)
    1840              self.assertEqual(e1.args, e2.args)
    1841              self.assertEqual(e1.source, e2.source)
    1842              self.assertEqual(e1.errors, e2.errors)
    1843              self.assertEqual(repr(e1), repr(e2))
    1844          e1 = configparser.ParsingError(filename='filename')
    1845          e1.append(1, 'line1')
    1846          e1.append(2, 'line2')
    1847          e1.append(3, 'line3')
    1848          for proto in range(pickle.HIGHEST_PROTOCOL + 1):
    1849              pickled = pickle.dumps(e1, proto)
    1850              e2 = pickle.loads(pickled)
    1851              self.assertEqual(e1.message, e2.message)
    1852              self.assertEqual(e1.args, e2.args)
    1853              self.assertEqual(e1.source, e2.source)
    1854              self.assertEqual(e1.errors, e2.errors)
    1855              self.assertEqual(repr(e1), repr(e2))
    1856  
    1857      def test_missingsectionheadererror(self):
    1858          import pickle
    1859          e1 = configparser.MissingSectionHeaderError('filename', 123, 'line')
    1860          for proto in range(pickle.HIGHEST_PROTOCOL + 1):
    1861              pickled = pickle.dumps(e1, proto)
    1862              e2 = pickle.loads(pickled)
    1863              self.assertEqual(e1.message, e2.message)
    1864              self.assertEqual(e1.args, e2.args)
    1865              self.assertEqual(e1.line, e2.line)
    1866              self.assertEqual(e1.source, e2.source)
    1867              self.assertEqual(e1.lineno, e2.lineno)
    1868              self.assertEqual(repr(e1), repr(e2))
    1869  
    1870  
    1871  class ESC[4;38;5;81mInlineCommentStrippingTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
    1872      """Tests for issue #14590: ConfigParser doesn't strip inline comment when
    1873      delimiter occurs earlier without preceding space.."""
    1874  
    1875      def test_stripping(self):
    1876          cfg = configparser.ConfigParser(inline_comment_prefixes=(';', '#',
    1877                  '//'))
    1878          cfg.read_string("""
    1879          [section]
    1880          k1 = v1;still v1
    1881          k2 = v2 ;a comment
    1882          k3 = v3 ; also a comment
    1883          k4 = v4;still v4 ;a comment
    1884          k5 = v5;still v5 ; also a comment
    1885          k6 = v6;still v6; and still v6 ;a comment
    1886          k7 = v7;still v7; and still v7 ; also a comment
    1887  
    1888          [multiprefix]
    1889          k1 = v1;still v1 #a comment ; yeah, pretty much
    1890          k2 = v2 // this already is a comment ; continued
    1891          k3 = v3;#//still v3# and still v3 ; a comment
    1892          """)
    1893          s = cfg['section']
    1894          self.assertEqual(s['k1'], 'v1;still v1')
    1895          self.assertEqual(s['k2'], 'v2')
    1896          self.assertEqual(s['k3'], 'v3')
    1897          self.assertEqual(s['k4'], 'v4;still v4')
    1898          self.assertEqual(s['k5'], 'v5;still v5')
    1899          self.assertEqual(s['k6'], 'v6;still v6; and still v6')
    1900          self.assertEqual(s['k7'], 'v7;still v7; and still v7')
    1901          s = cfg['multiprefix']
    1902          self.assertEqual(s['k1'], 'v1;still v1')
    1903          self.assertEqual(s['k2'], 'v2')
    1904          self.assertEqual(s['k3'], 'v3;#//still v3# and still v3')
    1905  
    1906  
    1907  class ESC[4;38;5;81mExceptionContextTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
    1908      """ Test that implementation details doesn't leak
    1909      through raising exceptions. """
    1910  
    1911      def test_get_basic_interpolation(self):
    1912          parser = configparser.ConfigParser()
    1913          parser.read_string("""
    1914          [Paths]
    1915          home_dir: /Users
    1916          my_dir: %(home_dir1)s/lumberjack
    1917          my_pictures: %(my_dir)s/Pictures
    1918          """)
    1919          cm = self.assertRaises(configparser.InterpolationMissingOptionError)
    1920          with cm:
    1921              parser.get('Paths', 'my_dir')
    1922          self.assertIs(cm.exception.__suppress_context__, True)
    1923  
    1924      def test_get_extended_interpolation(self):
    1925          parser = configparser.ConfigParser(
    1926            interpolation=configparser.ExtendedInterpolation())
    1927          parser.read_string("""
    1928          [Paths]
    1929          home_dir: /Users
    1930          my_dir: ${home_dir1}/lumberjack
    1931          my_pictures: ${my_dir}/Pictures
    1932          """)
    1933          cm = self.assertRaises(configparser.InterpolationMissingOptionError)
    1934          with cm:
    1935              parser.get('Paths', 'my_dir')
    1936          self.assertIs(cm.exception.__suppress_context__, True)
    1937  
    1938      def test_missing_options(self):
    1939          parser = configparser.ConfigParser()
    1940          parser.read_string("""
    1941          [Paths]
    1942          home_dir: /Users
    1943          """)
    1944          with self.assertRaises(configparser.NoSectionError) as cm:
    1945              parser.options('test')
    1946          self.assertIs(cm.exception.__suppress_context__, True)
    1947  
    1948      def test_missing_section(self):
    1949          config = configparser.ConfigParser()
    1950          with self.assertRaises(configparser.NoSectionError) as cm:
    1951              config.set('Section1', 'an_int', '15')
    1952          self.assertIs(cm.exception.__suppress_context__, True)
    1953  
    1954      def test_remove_option(self):
    1955          config = configparser.ConfigParser()
    1956          with self.assertRaises(configparser.NoSectionError) as cm:
    1957              config.remove_option('Section1', 'an_int')
    1958          self.assertIs(cm.exception.__suppress_context__, True)
    1959  
    1960  
    1961  class ESC[4;38;5;81mConvertersTestCase(ESC[4;38;5;149mBasicTestCase, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
    1962      """Introduced in 3.5, issue #18159."""
    1963  
    1964      config_class = configparser.ConfigParser
    1965  
    1966      def newconfig(self, defaults=None):
    1967          instance = super().newconfig(defaults=defaults)
    1968          instance.converters['list'] = lambda v: [e.strip() for e in v.split()
    1969                                                   if e.strip()]
    1970          return instance
    1971  
    1972      def test_converters(self):
    1973          cfg = self.newconfig()
    1974          self.assertIn('boolean', cfg.converters)
    1975          self.assertIn('list', cfg.converters)
    1976          self.assertIsNone(cfg.converters['int'])
    1977          self.assertIsNone(cfg.converters['float'])
    1978          self.assertIsNone(cfg.converters['boolean'])
    1979          self.assertIsNotNone(cfg.converters['list'])
    1980          self.assertEqual(len(cfg.converters), 4)
    1981          with self.assertRaises(ValueError):
    1982              cfg.converters[''] = lambda v: v
    1983          with self.assertRaises(ValueError):
    1984              cfg.converters[None] = lambda v: v
    1985          cfg.read_string("""
    1986          [s]
    1987          str = string
    1988          int = 1
    1989          float = 0.5
    1990          list = a b c d e f g
    1991          bool = yes
    1992          """)
    1993          s = cfg['s']
    1994          self.assertEqual(s['str'], 'string')
    1995          self.assertEqual(s['int'], '1')
    1996          self.assertEqual(s['float'], '0.5')
    1997          self.assertEqual(s['list'], 'a b c d e f g')
    1998          self.assertEqual(s['bool'], 'yes')
    1999          self.assertEqual(cfg.get('s', 'str'), 'string')
    2000          self.assertEqual(cfg.get('s', 'int'), '1')
    2001          self.assertEqual(cfg.get('s', 'float'), '0.5')
    2002          self.assertEqual(cfg.get('s', 'list'), 'a b c d e f g')
    2003          self.assertEqual(cfg.get('s', 'bool'), 'yes')
    2004          self.assertEqual(cfg.get('s', 'str'), 'string')
    2005          self.assertEqual(cfg.getint('s', 'int'), 1)
    2006          self.assertEqual(cfg.getfloat('s', 'float'), 0.5)
    2007          self.assertEqual(cfg.getlist('s', 'list'), ['a', 'b', 'c', 'd',
    2008                                                      'e', 'f', 'g'])
    2009          self.assertEqual(cfg.getboolean('s', 'bool'), True)
    2010          self.assertEqual(s.get('str'), 'string')
    2011          self.assertEqual(s.getint('int'), 1)
    2012          self.assertEqual(s.getfloat('float'), 0.5)
    2013          self.assertEqual(s.getlist('list'), ['a', 'b', 'c', 'd',
    2014                                               'e', 'f', 'g'])
    2015          self.assertEqual(s.getboolean('bool'), True)
    2016          with self.assertRaises(AttributeError):
    2017              cfg.getdecimal('s', 'float')
    2018          with self.assertRaises(AttributeError):
    2019              s.getdecimal('float')
    2020          import decimal
    2021          cfg.converters['decimal'] = decimal.Decimal
    2022          self.assertIn('decimal', cfg.converters)
    2023          self.assertIsNotNone(cfg.converters['decimal'])
    2024          self.assertEqual(len(cfg.converters), 5)
    2025          dec0_5 = decimal.Decimal('0.5')
    2026          self.assertEqual(cfg.getdecimal('s', 'float'), dec0_5)
    2027          self.assertEqual(s.getdecimal('float'), dec0_5)
    2028          del cfg.converters['decimal']
    2029          self.assertNotIn('decimal', cfg.converters)
    2030          self.assertEqual(len(cfg.converters), 4)
    2031          with self.assertRaises(AttributeError):
    2032              cfg.getdecimal('s', 'float')
    2033          with self.assertRaises(AttributeError):
    2034              s.getdecimal('float')
    2035          with self.assertRaises(KeyError):
    2036              del cfg.converters['decimal']
    2037          with self.assertRaises(KeyError):
    2038              del cfg.converters['']
    2039          with self.assertRaises(KeyError):
    2040              del cfg.converters[None]
    2041  
    2042  
    2043  class ESC[4;38;5;81mBlatantOverrideConvertersTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
    2044      """What if somebody overrode a getboolean()? We want to make sure that in
    2045      this case the automatic converters do not kick in."""
    2046  
    2047      config = """
    2048          [one]
    2049          one = false
    2050          two = false
    2051          three = long story short
    2052  
    2053          [two]
    2054          one = false
    2055          two = false
    2056          three = four
    2057      """
    2058  
    2059      def test_converters_at_init(self):
    2060          cfg = configparser.ConfigParser(converters={'len': len})
    2061          cfg.read_string(self.config)
    2062          self._test_len(cfg)
    2063          self.assertIsNotNone(cfg.converters['len'])
    2064  
    2065      def test_inheritance(self):
    2066          class ESC[4;38;5;81mStrangeConfigParser(ESC[4;38;5;149mconfigparserESC[4;38;5;149m.ESC[4;38;5;149mConfigParser):
    2067              gettysburg = 'a historic borough in south central Pennsylvania'
    2068  
    2069              def getboolean(self, section, option, *, raw=False, vars=None,
    2070                          fallback=configparser._UNSET):
    2071                  if section == option:
    2072                      return True
    2073                  return super().getboolean(section, option, raw=raw, vars=vars,
    2074                                            fallback=fallback)
    2075              def getlen(self, section, option, *, raw=False, vars=None,
    2076                         fallback=configparser._UNSET):
    2077                  return self._get_conv(section, option, len, raw=raw, vars=vars,
    2078                                        fallback=fallback)
    2079  
    2080          cfg = StrangeConfigParser()
    2081          cfg.read_string(self.config)
    2082          self._test_len(cfg)
    2083          self.assertIsNone(cfg.converters['len'])
    2084          self.assertTrue(cfg.getboolean('one', 'one'))
    2085          self.assertTrue(cfg.getboolean('two', 'two'))
    2086          self.assertFalse(cfg.getboolean('one', 'two'))
    2087          self.assertFalse(cfg.getboolean('two', 'one'))
    2088          cfg.converters['boolean'] = cfg._convert_to_boolean
    2089          self.assertFalse(cfg.getboolean('one', 'one'))
    2090          self.assertFalse(cfg.getboolean('two', 'two'))
    2091          self.assertFalse(cfg.getboolean('one', 'two'))
    2092          self.assertFalse(cfg.getboolean('two', 'one'))
    2093  
    2094      def _test_len(self, cfg):
    2095          self.assertEqual(len(cfg.converters), 4)
    2096          self.assertIn('boolean', cfg.converters)
    2097          self.assertIn('len', cfg.converters)
    2098          self.assertNotIn('tysburg', cfg.converters)
    2099          self.assertIsNone(cfg.converters['int'])
    2100          self.assertIsNone(cfg.converters['float'])
    2101          self.assertIsNone(cfg.converters['boolean'])
    2102          self.assertEqual(cfg.getlen('one', 'one'), 5)
    2103          self.assertEqual(cfg.getlen('one', 'two'), 5)
    2104          self.assertEqual(cfg.getlen('one', 'three'), 16)
    2105          self.assertEqual(cfg.getlen('two', 'one'), 5)
    2106          self.assertEqual(cfg.getlen('two', 'two'), 5)
    2107          self.assertEqual(cfg.getlen('two', 'three'), 4)
    2108          self.assertEqual(cfg.getlen('two', 'four', fallback=0), 0)
    2109          with self.assertRaises(configparser.NoOptionError):
    2110              cfg.getlen('two', 'four')
    2111          self.assertEqual(cfg['one'].getlen('one'), 5)
    2112          self.assertEqual(cfg['one'].getlen('two'), 5)
    2113          self.assertEqual(cfg['one'].getlen('three'), 16)
    2114          self.assertEqual(cfg['two'].getlen('one'), 5)
    2115          self.assertEqual(cfg['two'].getlen('two'), 5)
    2116          self.assertEqual(cfg['two'].getlen('three'), 4)
    2117          self.assertEqual(cfg['two'].getlen('four', 0), 0)
    2118          self.assertEqual(cfg['two'].getlen('four'), None)
    2119  
    2120      def test_instance_assignment(self):
    2121          cfg = configparser.ConfigParser()
    2122          cfg.getboolean = lambda section, option: True
    2123          cfg.getlen = lambda section, option: len(cfg[section][option])
    2124          cfg.read_string(self.config)
    2125          self.assertEqual(len(cfg.converters), 3)
    2126          self.assertIn('boolean', cfg.converters)
    2127          self.assertNotIn('len', cfg.converters)
    2128          self.assertIsNone(cfg.converters['int'])
    2129          self.assertIsNone(cfg.converters['float'])
    2130          self.assertIsNone(cfg.converters['boolean'])
    2131          self.assertTrue(cfg.getboolean('one', 'one'))
    2132          self.assertTrue(cfg.getboolean('two', 'two'))
    2133          self.assertTrue(cfg.getboolean('one', 'two'))
    2134          self.assertTrue(cfg.getboolean('two', 'one'))
    2135          cfg.converters['boolean'] = cfg._convert_to_boolean
    2136          self.assertFalse(cfg.getboolean('one', 'one'))
    2137          self.assertFalse(cfg.getboolean('two', 'two'))
    2138          self.assertFalse(cfg.getboolean('one', 'two'))
    2139          self.assertFalse(cfg.getboolean('two', 'one'))
    2140          self.assertEqual(cfg.getlen('one', 'one'), 5)
    2141          self.assertEqual(cfg.getlen('one', 'two'), 5)
    2142          self.assertEqual(cfg.getlen('one', 'three'), 16)
    2143          self.assertEqual(cfg.getlen('two', 'one'), 5)
    2144          self.assertEqual(cfg.getlen('two', 'two'), 5)
    2145          self.assertEqual(cfg.getlen('two', 'three'), 4)
    2146          # If a getter impl is assigned straight to the instance, it won't
    2147          # be available on the section proxies.
    2148          with self.assertRaises(AttributeError):
    2149              self.assertEqual(cfg['one'].getlen('one'), 5)
    2150          with self.assertRaises(AttributeError):
    2151              self.assertEqual(cfg['two'].getlen('one'), 5)
    2152  
    2153  
    2154  class ESC[4;38;5;81mMiscTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
    2155      def test__all__(self):
    2156          support.check__all__(self, configparser, not_exported={"Error"})
    2157  
    2158  
    2159  if __name__ == '__main__':
    2160      unittest.main()