1 import ConfigParser 2 import StringIO 3 import os 4 import unittest 5 import UserDict 6 7 from test import test_support 8 9 10 class SortedDict(UserDict.UserDict): 11 def items(self): 12 result = self.data.items() 13 result.sort() 14 return result 15 16 def keys(self): 17 result = self.data.keys() 18 result.sort() 19 return result 20 21 def values(self): 22 # XXX never used? 23 result = self.items() 24 return [i[1] for i in result] 25 26 def iteritems(self): return iter(self.items()) 27 def iterkeys(self): return iter(self.keys()) 28 __iter__ = iterkeys 29 def itervalues(self): return iter(self.values()) 30 31 32 class TestCaseBase(unittest.TestCase): 33 allow_no_value = False 34 35 def newconfig(self, defaults=None): 36 if defaults is None: 37 self.cf = self.config_class(allow_no_value=self.allow_no_value) 38 else: 39 self.cf = self.config_class(defaults, 40 allow_no_value=self.allow_no_value) 41 return self.cf 42 43 def fromstring(self, string, defaults=None): 44 cf = self.newconfig(defaults) 45 sio = StringIO.StringIO(string) 46 cf.readfp(sio) 47 return cf 48 49 def test_basic(self): 50 config_string = ( 51 "[Foo Bar]\n" 52 "foo=bar\n" 53 "[Spacey Bar]\n" 54 "foo = bar\n" 55 "[Commented Bar]\n" 56 "foo: bar ; comment\n" 57 "[Long Line]\n" 58 "foo: this line is much, much longer than my editor\n" 59 " likes it.\n" 60 "[Section\\with$weird%characters[\t]\n" 61 "[Internationalized Stuff]\n" 62 "foo[bg]: Bulgarian\n" 63 "foo=Default\n" 64 "foo[en]=English\n" 65 "foo[de]=Deutsch\n" 66 "[Spaces]\n" 67 "key with spaces : value\n" 68 "another with spaces = splat!\n" 69 ) 70 if self.allow_no_value: 71 config_string += ( 72 "[NoValue]\n" 73 "option-without-value\n" 74 ) 75 76 cf = self.fromstring(config_string) 77 L = cf.sections() 78 L.sort() 79 E = [r'Commented Bar', 80 r'Foo Bar', 81 r'Internationalized Stuff', 82 r'Long Line', 83 r'Section\with$weird%characters[' '\t', 84 r'Spaces', 85 r'Spacey Bar', 86 ] 87 if self.allow_no_value: 88 E.append(r'NoValue') 89 E.sort() 90 eq = self.assertEqual 91 eq(L, E) 92 93 # The use of spaces in the section names serves as a 94 # regression test for SourceForge bug #583248: 95 # http://www.python.org/sf/583248 96 eq(cf.get('Foo Bar', 'foo'), 'bar') 97 eq(cf.get('Spacey Bar', 'foo'), 'bar') 98 eq(cf.get('Commented Bar', 'foo'), 'bar') 99 eq(cf.get('Spaces', 'key with spaces'), 'value') 100 eq(cf.get('Spaces', 'another with spaces'), 'splat!') 101 if self.allow_no_value: 102 eq(cf.get('NoValue', 'option-without-value'), None) 103 104 self.assertNotIn('__name__', cf.options("Foo Bar"), 105 '__name__ "option" should not be exposed by the API!') 106 107 # Make sure the right things happen for remove_option(); 108 # added to include check for SourceForge bug #123324: 109 self.assertTrue(cf.remove_option('Foo Bar', 'foo'), 110 "remove_option() failed to report existence of option") 111 self.assertFalse(cf.has_option('Foo Bar', 'foo'), 112 "remove_option() failed to remove option") 113 self.assertFalse(cf.remove_option('Foo Bar', 'foo'), 114 "remove_option() failed to report non-existence of option" 115 " that was removed") 116 117 self.assertRaises(ConfigParser.NoSectionError, 118 cf.remove_option, 'No Such Section', 'foo') 119 120 eq(cf.get('Long Line', 'foo'), 121 'this line is much, much longer than my editor\nlikes it.') 122 123 def test_case_sensitivity(self): 124 cf = self.newconfig() 125 cf.add_section("A") 126 cf.add_section("a") 127 L = cf.sections() 128 L.sort() 129 eq = self.assertEqual 130 eq(L, ["A", "a"]) 131 cf.set("a", "B", "value") 132 eq(cf.options("a"), ["b"]) 133 eq(cf.get("a", "b"), "value", 134 "could not locate option, expecting case-insensitive option names") 135 self.assertTrue(cf.has_option("a", "b")) 136 cf.set("A", "A-B", "A-B value") 137 for opt in ("a-b", "A-b", "a-B", "A-B"): 138 self.assertTrue( 139 cf.has_option("A", opt), 140 "has_option() returned false for option which should exist") 141 eq(cf.options("A"), ["a-b"]) 142 eq(cf.options("a"), ["b"]) 143 cf.remove_option("a", "B") 144 eq(cf.options("a"), []) 145 146 # SF bug #432369: 147 cf = self.fromstring( 148 "[MySection]\nOption: first line\n\tsecond line\n") 149 eq(cf.options("MySection"), ["option"]) 150 eq(cf.get("MySection", "Option"), "first line\nsecond line") 151 152 # SF bug #561822: 153 cf = self.fromstring("[section]\nnekey=nevalue\n", 154 defaults={"key":"value"}) 155 self.assertTrue(cf.has_option("section", "Key")) 156 157 158 def test_default_case_sensitivity(self): 159 cf = self.newconfig({"foo": "Bar"}) 160 self.assertEqual( 161 cf.get("DEFAULT", "Foo"), "Bar", 162 "could not locate option, expecting case-insensitive option names") 163 cf = self.newconfig({"Foo": "Bar"}) 164 self.assertEqual( 165 cf.get("DEFAULT", "Foo"), "Bar", 166 "could not locate option, expecting case-insensitive defaults") 167 168 def test_parse_errors(self): 169 self.newconfig() 170 self.parse_error(ConfigParser.ParsingError, 171 "[Foo]\n extra-spaces: splat\n") 172 self.parse_error(ConfigParser.ParsingError, 173 "[Foo]\n extra-spaces= splat\n") 174 self.parse_error(ConfigParser.ParsingError, 175 "[Foo]\n:value-without-option-name\n") 176 self.parse_error(ConfigParser.ParsingError, 177 "[Foo]\n=value-without-option-name\n") 178 self.parse_error(ConfigParser.MissingSectionHeaderError, 179 "No Section!\n") 180 181 def parse_error(self, exc, src): 182 sio = StringIO.StringIO(src) 183 self.assertRaises(exc, self.cf.readfp, sio) 184 185 def test_query_errors(self): 186 cf = self.newconfig() 187 self.assertEqual(cf.sections(), [], 188 "new ConfigParser should have no defined sections") 189 self.assertFalse(cf.has_section("Foo"), 190 "new ConfigParser should have no acknowledged " 191 "sections") 192 self.assertRaises(ConfigParser.NoSectionError, 193 cf.options, "Foo") 194 self.assertRaises(ConfigParser.NoSectionError, 195 cf.set, "foo", "bar", "value") 196 self.get_error(ConfigParser.NoSectionError, "foo", "bar") 197 cf.add_section("foo") 198 self.get_error(ConfigParser.NoOptionError, "foo", "bar") 199 200 def get_error(self, exc, section, option): 201 try: 202 self.cf.get(section, option) 203 except exc, e: 204 return e 205 else: 206 self.fail("expected exception type %s.%s" 207 % (exc.__module__, exc.__name__)) 208 209 def test_boolean(self): 210 cf = self.fromstring( 211 "[BOOLTEST]\n" 212 "T1=1\n" 213 "T2=TRUE\n" 214 "T3=True\n" 215 "T4=oN\n" 216 "T5=yes\n" 217 "F1=0\n" 218 "F2=FALSE\n" 219 "F3=False\n" 220 "F4=oFF\n" 221 "F5=nO\n" 222 "E1=2\n" 223 "E2=foo\n" 224 "E3=-1\n" 225 "E4=0.1\n" 226 "E5=FALSE AND MORE" 227 ) 228 for x in range(1, 5): 229 self.assertTrue(cf.getboolean('BOOLTEST', 't%d' % x)) 230 self.assertFalse(cf.getboolean('BOOLTEST', 'f%d' % x)) 231 self.assertRaises(ValueError, 232 cf.getboolean, 'BOOLTEST', 'e%d' % x) 233 234 def test_weird_errors(self): 235 cf = self.newconfig() 236 cf.add_section("Foo") 237 self.assertRaises(ConfigParser.DuplicateSectionError, 238 cf.add_section, "Foo") 239 240 def test_write(self): 241 config_string = ( 242 "[Long Line]\n" 243 "foo: this line is much, much longer than my editor\n" 244 " likes it.\n" 245 "[DEFAULT]\n" 246 "foo: another very\n" 247 " long line\n" 248 ) 249 if self.allow_no_value: 250 config_string += ( 251 "[Valueless]\n" 252 "option-without-value\n" 253 ) 254 255 cf = self.fromstring(config_string) 256 output = StringIO.StringIO() 257 cf.write(output) 258 expect_string = ( 259 "[DEFAULT]\n" 260 "foo = another very\n" 261 "\tlong line\n" 262 "\n" 263 "[Long Line]\n" 264 "foo = this line is much, much longer than my editor\n" 265 "\tlikes it.\n" 266 "\n" 267 ) 268 if self.allow_no_value: 269 expect_string += ( 270 "[Valueless]\n" 271 "option-without-value\n" 272 "\n" 273 ) 274 self.assertEqual(output.getvalue(), expect_string) 275 276 def test_set_string_types(self): 277 cf = self.fromstring("[sect]\n" 278 "option1=foo\n") 279 # Check that we don't get an exception when setting values in 280 # an existing section using strings: 281 class mystr(str): 282 pass 283 cf.set("sect", "option1", "splat") 284 cf.set("sect", "option1", mystr("splat")) 285 cf.set("sect", "option2", "splat") 286 cf.set("sect", "option2", mystr("splat")) 287 try: 288 unicode 289 except NameError: 290 pass 291 else: 292 cf.set("sect", "option1", unicode("splat")) 293 cf.set("sect", "option2", unicode("splat")) 294 295 def test_read_returns_file_list(self): 296 file1 = test_support.findfile("cfgparser.1") 297 # check when we pass a mix of readable and non-readable files: 298 cf = self.newconfig() 299 parsed_files = cf.read([file1, "nonexistent-file"]) 300 self.assertEqual(parsed_files, [file1]) 301 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar") 302 # check when we pass only a filename: 303 cf = self.newconfig() 304 parsed_files = cf.read(file1) 305 self.assertEqual(parsed_files, [file1]) 306 self.assertEqual(cf.get("Foo Bar", "foo"), "newbar") 307 # check when we pass only missing files: 308 cf = self.newconfig() 309 parsed_files = cf.read(["nonexistent-file"]) 310 self.assertEqual(parsed_files, []) 311 # check when we pass no files: 312 cf = self.newconfig() 313 parsed_files = cf.read([]) 314 self.assertEqual(parsed_files, []) 315 316 # shared by subclasses 317 def get_interpolation_config(self): 318 return self.fromstring( 319 "[Foo]\n" 320 "bar=something %(with1)s interpolation (1 step)\n" 321 "bar9=something %(with9)s lots of interpolation (9 steps)\n" 322 "bar10=something %(with10)s lots of interpolation (10 steps)\n" 323 "bar11=something %(with11)s lots of interpolation (11 steps)\n" 324 "with11=%(with10)s\n" 325 "with10=%(with9)s\n" 326 "with9=%(with8)s\n" 327 "with8=%(With7)s\n" 328 "with7=%(WITH6)s\n" 329 "with6=%(with5)s\n" 330 "With5=%(with4)s\n" 331 "WITH4=%(with3)s\n" 332 "with3=%(with2)s\n" 333 "with2=%(with1)s\n" 334 "with1=with\n" 335 "\n" 336 "[Mutual Recursion]\n" 337 "foo=%(bar)s\n" 338 "bar=%(foo)s\n" 339 "\n" 340 "[Interpolation Error]\n" 341 "name=%(reference)s\n", 342 # no definition for 'reference' 343 defaults={"getname": "%(__name__)s"}) 344 345 def check_items_config(self, expected): 346 cf = self.fromstring( 347 "[section]\n" 348 "name = value\n" 349 "key: |%(name)s| \n" 350 "getdefault: |%(default)s|\n" 351 "getname: |%(__name__)s|", 352 defaults={"default": "<default>"}) 353 L = list(cf.items("section")) 354 L.sort() 355 self.assertEqual(L, expected) 356 357 358 class ConfigParserTestCase(TestCaseBase): 359 config_class = ConfigParser.ConfigParser 360 allow_no_value = True 361 362 def test_interpolation(self): 363 rawval = { 364 ConfigParser.ConfigParser: ("something %(with11)s " 365 "lots of interpolation (11 steps)"), 366 ConfigParser.SafeConfigParser: "%(with1)s", 367 } 368 cf = self.get_interpolation_config() 369 eq = self.assertEqual 370 eq(cf.get("Foo", "getname"), "Foo") 371 eq(cf.get("Foo", "bar"), "something with interpolation (1 step)") 372 eq(cf.get("Foo", "bar9"), 373 "something with lots of interpolation (9 steps)") 374 eq(cf.get("Foo", "bar10"), 375 "something with lots of interpolation (10 steps)") 376 self.get_error(ConfigParser.InterpolationDepthError, "Foo", "bar11") 377 378 def test_interpolation_missing_value(self): 379 self.get_interpolation_config() 380 e = self.get_error(ConfigParser.InterpolationError, 381 "Interpolation Error", "name") 382 self.assertEqual(e.reference, "reference") 383 self.assertEqual(e.section, "Interpolation Error") 384 self.assertEqual(e.option, "name") 385 386 def test_items(self): 387 self.check_items_config([('default', '<default>'), 388 ('getdefault', '|<default>|'), 389 ('getname', '|section|'), 390 ('key', '|value|'), 391 ('name', 'value')]) 392 393 def test_set_nonstring_types(self): 394 cf = self.newconfig() 395 cf.add_section('non-string') 396 cf.set('non-string', 'int', 1) 397 cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13, '%(']) 398 cf.set('non-string', 'dict', {'pi': 3.14159, '%(': 1, 399 '%(list)': '%(list)'}) 400 cf.set('non-string', 'string_with_interpolation', '%(list)s') 401 cf.set('non-string', 'no-value') 402 self.assertEqual(cf.get('non-string', 'int', raw=True), 1) 403 self.assertRaises(TypeError, cf.get, 'non-string', 'int') 404 self.assertEqual(cf.get('non-string', 'list', raw=True), 405 [0, 1, 1, 2, 3, 5, 8, 13, '%(']) 406 self.assertRaises(TypeError, cf.get, 'non-string', 'list') 407 self.assertEqual(cf.get('non-string', 'dict', raw=True), 408 {'pi': 3.14159, '%(': 1, '%(list)': '%(list)'}) 409 self.assertRaises(TypeError, cf.get, 'non-string', 'dict') 410 self.assertEqual(cf.get('non-string', 'string_with_interpolation', 411 raw=True), '%(list)s') 412 self.assertRaises(ValueError, cf.get, 'non-string', 413 'string_with_interpolation', raw=False) 414 self.assertEqual(cf.get('non-string', 'no-value'), None) 415 416 class MultilineValuesTestCase(TestCaseBase): 417 config_class = ConfigParser.ConfigParser 418 wonderful_spam = ("I'm having spam spam spam spam " 419 "spam spam spam beaked beans spam " 420 "spam spam and spam!").replace(' ', '\t\n') 421 422 def setUp(self): 423 cf = self.newconfig() 424 for i in range(100): 425 s = 'section{}'.format(i) 426 cf.add_section(s) 427 for j in range(10): 428 cf.set(s, 'lovely_spam{}'.format(j), self.wonderful_spam) 429 with open(test_support.TESTFN, 'w') as f: 430 cf.write(f) 431 432 def tearDown(self): 433 os.unlink(test_support.TESTFN) 434 435 def test_dominating_multiline_values(self): 436 # we're reading from file because this is where the code changed 437 # during performance updates in Python 3.2 438 cf_from_file = self.newconfig() 439 with open(test_support.TESTFN) as f: 440 cf_from_file.readfp(f) 441 self.assertEqual(cf_from_file.get('section8', 'lovely_spam4'), 442 self.wonderful_spam.replace('\t\n', '\n')) 443 444 class RawConfigParserTestCase(TestCaseBase): 445 config_class = ConfigParser.RawConfigParser 446 447 def test_interpolation(self): 448 cf = self.get_interpolation_config() 449 eq = self.assertEqual 450 eq(cf.get("Foo", "getname"), "%(__name__)s") 451 eq(cf.get("Foo", "bar"), 452 "something %(with1)s interpolation (1 step)") 453 eq(cf.get("Foo", "bar9"), 454 "something %(with9)s lots of interpolation (9 steps)") 455 eq(cf.get("Foo", "bar10"), 456 "something %(with10)s lots of interpolation (10 steps)") 457 eq(cf.get("Foo", "bar11"), 458 "something %(with11)s lots of interpolation (11 steps)") 459 460 def test_items(self): 461 self.check_items_config([('default', '<default>'), 462 ('getdefault', '|%(default)s|'), 463 ('getname', '|%(__name__)s|'), 464 ('key', '|%(name)s|'), 465 ('name', 'value')]) 466 467 def test_set_nonstring_types(self): 468 cf = self.newconfig() 469 cf.add_section('non-string') 470 cf.set('non-string', 'int', 1) 471 cf.set('non-string', 'list', [0, 1, 1, 2, 3, 5, 8, 13]) 472 cf.set('non-string', 'dict', {'pi': 3.14159}) 473 self.assertEqual(cf.get('non-string', 'int'), 1) 474 self.assertEqual(cf.get('non-string', 'list'), 475 [0, 1, 1, 2, 3, 5, 8, 13]) 476 self.assertEqual(cf.get('non-string', 'dict'), {'pi': 3.14159}) 477 478 479 class SafeConfigParserTestCase(ConfigParserTestCase): 480 config_class = ConfigParser.SafeConfigParser 481 482 def test_safe_interpolation(self): 483 # See http://www.python.org/sf/511737 484 cf = self.fromstring("[section]\n" 485 "option1=xxx\n" 486 "option2=%(option1)s/xxx\n" 487 "ok=%(option1)s/%%s\n" 488 "not_ok=%(option2)s/%%s") 489 self.assertEqual(cf.get("section", "ok"), "xxx/%s") 490 self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s") 491 492 def test_set_malformatted_interpolation(self): 493 cf = self.fromstring("[sect]\n" 494 "option1=foo\n") 495 496 self.assertEqual(cf.get('sect', "option1"), "foo") 497 498 self.assertRaises(ValueError, cf.set, "sect", "option1", "%foo") 499 self.assertRaises(ValueError, cf.set, "sect", "option1", "foo%") 500 self.assertRaises(ValueError, cf.set, "sect", "option1", "f%oo") 501 502 self.assertEqual(cf.get('sect', "option1"), "foo") 503 504 # bug #5741: double percents are *not* malformed 505 cf.set("sect", "option2", "foo%%bar") 506 self.assertEqual(cf.get("sect", "option2"), "foo%bar") 507 508 def test_set_nonstring_types(self): 509 cf = self.fromstring("[sect]\n" 510 "option1=foo\n") 511 # Check that we get a TypeError when setting non-string values 512 # in an existing section: 513 self.assertRaises(TypeError, cf.set, "sect", "option1", 1) 514 self.assertRaises(TypeError, cf.set, "sect", "option1", 1.0) 515 self.assertRaises(TypeError, cf.set, "sect", "option1", object()) 516 self.assertRaises(TypeError, cf.set, "sect", "option2", 1) 517 self.assertRaises(TypeError, cf.set, "sect", "option2", 1.0) 518 self.assertRaises(TypeError, cf.set, "sect", "option2", object()) 519 520 def test_add_section_default_1(self): 521 cf = self.newconfig() 522 self.assertRaises(ValueError, cf.add_section, "default") 523 524 def test_add_section_default_2(self): 525 cf = self.newconfig() 526 self.assertRaises(ValueError, cf.add_section, "DEFAULT") 527 528 529 class SafeConfigParserTestCaseNoValue(SafeConfigParserTestCase): 530 allow_no_value = True 531 532 533 class Issue7005TestCase(unittest.TestCase): 534 """Test output when None is set() as a value and allow_no_value == False. 535 536 http://bugs.python.org/issue7005 537 538 """ 539 540 expected_output = "[section]\noption = None\n\n" 541 542 def prepare(self, config_class): 543 # This is the default, but that's the point. 544 cp = config_class(allow_no_value=False) 545 cp.add_section("section") 546 cp.set("section", "option", None) 547 sio = StringIO.StringIO() 548 cp.write(sio) 549 return sio.getvalue() 550 551 def test_none_as_value_stringified(self): 552 output = self.prepare(ConfigParser.ConfigParser) 553 self.assertEqual(output, self.expected_output) 554 555 def test_none_as_value_stringified_raw(self): 556 output = self.prepare(ConfigParser.RawConfigParser) 557 self.assertEqual(output, self.expected_output) 558 559 560 class SortedTestCase(RawConfigParserTestCase): 561 def newconfig(self, defaults=None): 562 self.cf = self.config_class(defaults=defaults, dict_type=SortedDict) 563 return self.cf 564 565 def test_sorted(self): 566 self.fromstring("[b]\n" 567 "o4=1\n" 568 "o3=2\n" 569 "o2=3\n" 570 "o1=4\n" 571 "[a]\n" 572 "k=v\n") 573 output = StringIO.StringIO() 574 self.cf.write(output) 575 self.assertEqual(output.getvalue(), 576 "[a]\n" 577 "k = v\n\n" 578 "[b]\n" 579 "o1 = 4\n" 580 "o2 = 3\n" 581 "o3 = 2\n" 582 "o4 = 1\n\n") 583 584 585 def test_main(): 586 test_support.run_unittest( 587 ConfigParserTestCase, 588 MultilineValuesTestCase, 589 RawConfigParserTestCase, 590 SafeConfigParserTestCase, 591 SafeConfigParserTestCaseNoValue, 592 SortedTestCase, 593 Issue7005TestCase, 594 ) 595 596 597 if __name__ == "__main__": 598 test_main() 599