~ubuntu-branches/ubuntu/natty/configobj/natty

« back to all changes in this revision

Viewing changes to .pc/triplequotes.diff/tests/test_configobj.py

  • Committer: Bazaar Package Importer
  • Author(s): Jelmer Vernooij, Stefano Rivera, Jelmer Vernooij
  • Date: 2011-03-16 11:17:14 UTC
  • mto: This revision was merged to the branch mainline in revision 13.
  • Revision ID: james.westby@ubuntu.com-20110316111714-p0199jx0lfyd5az9
[ Stefano Rivera ]
* Don't leak uid and umask into source tarball and set -e.
* Bumped Standards-Version to 3.9.1 (no changes needed).
* Enable test suites.
  - Build Depend on python-all, python-unittest2.
  - New patch: report-doctest-failure.diff: Fail on failures.
  - New patch: py27-test.diff: Convert float-comparing doctests to unit
    tests.
* Wrap long lines in debian/control.
* Merge Build-Depends-Indep into Build-Depends (no arch-dependant packages).
* Switch to dh_python2
  - Use X-Python-Version (requires python-all >= 2.6.5-13~).
  - Use ${python:Breaks}.
* Update my e-mail address.
* Use DEP5 format in debian/copyright.

[ Jelmer Vernooij ]
* Properly handle triple quotes. Closes: #618349, LP: #710410
* Add myself to uploaders.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# configobj_test.py
 
2
# doctests for ConfigObj
 
3
# A config file reader/writer that supports nested sections in config files.
 
4
# Copyright (C) 2005-2010 Michael Foord, Nicola Larosa
 
5
# E-mail: fuzzyman AT voidspace DOT org DOT uk
 
6
#         nico AT tekNico DOT net
 
7
 
 
8
# ConfigObj 4
 
9
# http://www.voidspace.org.uk/python/configobj.html
 
10
 
 
11
# Released subject to the BSD License
 
12
# Please see http://www.voidspace.org.uk/python/license.shtml
 
13
 
 
14
# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
 
15
# For information about bugfixes, updates and support, please join the
 
16
# ConfigObj mailing list:
 
17
# http://lists.sourceforge.net/lists/listinfo/configobj-develop
 
18
# Comments, suggestions and bug reports welcome.
 
19
 
 
20
 
 
21
from __future__ import generators
 
22
from StringIO import StringIO
 
23
 
 
24
import os
 
25
import sys
 
26
INTP_VER = sys.version_info[:2]
 
27
if INTP_VER < (2, 2):
 
28
    raise RuntimeError("Python v.2.2 or later needed")
 
29
 
 
30
try:
 
31
    from codecs import BOM_UTF8
 
32
except ImportError:
 
33
    # Python 2.2 does not have this
 
34
    # UTF-8
 
35
    BOM_UTF8 = '\xef\xbb\xbf'
 
36
 
 
37
from configobj import *
 
38
from validate import Validator, VdtValueTooSmallError
 
39
 
 
40
 
 
41
"""
 
42
    >>> z = ConfigObj()
 
43
    >>> z['a'] = 'a'
 
44
    >>> z['sect'] = {
 
45
    ...    'subsect': {
 
46
    ...         'a': 'fish',
 
47
    ...         'b': 'wobble',
 
48
    ...     },
 
49
    ...     'member': 'value',
 
50
    ... }
 
51
    >>> x = ConfigObj(z.write())
 
52
    >>> z == x
 
53
    1
 
54
"""
 
55
 
 
56
 
 
57
def _error_test():
 
58
    """
 
59
    Testing the error classes.
 
60
    
 
61
    >>> raise ConfigObjError
 
62
    Traceback (most recent call last):
 
63
    ConfigObjError
 
64
 
 
65
    >>> raise NestingError
 
66
    Traceback (most recent call last):
 
67
    NestingError
 
68
    
 
69
    >>> raise ParseError
 
70
    Traceback (most recent call last):
 
71
    ParseError
 
72
    
 
73
    >>> raise DuplicateError
 
74
    Traceback (most recent call last):
 
75
    DuplicateError
 
76
    
 
77
    >>> raise ConfigspecError
 
78
    Traceback (most recent call last):
 
79
    ConfigspecError
 
80
    
 
81
    >>> raise InterpolationLoopError('yoda')
 
82
    Traceback (most recent call last):
 
83
    InterpolationLoopError: interpolation loop detected in value "yoda".
 
84
    
 
85
    >>> raise RepeatSectionError
 
86
    Traceback (most recent call last):
 
87
    RepeatSectionError
 
88
    
 
89
    >>> raise MissingInterpolationOption('yoda')
 
90
    Traceback (most recent call last):
 
91
    MissingInterpolationOption: missing option "yoda" in interpolation.
 
92
    
 
93
    
 
94
    >>> raise ReloadError()
 
95
    Traceback (most recent call last):
 
96
    ReloadError: reload failed, filename is not set.
 
97
    >>> try:
 
98
    ...     raise ReloadError()
 
99
    ... except IOError:
 
100
    ...     pass
 
101
    ... else:
 
102
    ...     raise Exception('We should catch a ReloadError as an IOError')
 
103
    >>>
 
104
    
 
105
    """
 
106
 
 
107
 
 
108
def _section_test():
 
109
    """
 
110
    Tests from Section methods.
 
111
    
 
112
    >>> n = a.dict()
 
113
    >>> n == a
 
114
    1
 
115
    >>> n is a
 
116
    0
 
117
        
 
118
    >>> a = '''[section1]
 
119
    ...     option1 = True
 
120
    ...     [[subsection]]
 
121
    ...     more_options = False
 
122
    ...     # end of file'''.splitlines()
 
123
    >>> b = '''# File is user.ini
 
124
    ...     [section1]
 
125
    ...     option1 = False
 
126
    ...     # end of file'''.splitlines()
 
127
    >>> c1 = ConfigObj(b)
 
128
    >>> c2 = ConfigObj(a)
 
129
    >>> c2.merge(c1)
 
130
    >>> c2
 
131
    ConfigObj({'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}})
 
132
    
 
133
    >>> config = '''[XXXXsection]
 
134
    ... XXXXkey = XXXXvalue'''.splitlines()
 
135
    >>> cfg = ConfigObj(config)
 
136
    >>> cfg
 
137
    ConfigObj({'XXXXsection': {'XXXXkey': 'XXXXvalue'}})
 
138
    >>> def transform(section, key):
 
139
    ...     val = section[key]
 
140
    ...     newkey = key.replace('XXXX', 'CLIENT1')
 
141
    ...     section.rename(key, newkey)
 
142
    ...     if isinstance(val, (tuple, list, dict)):
 
143
    ...         pass
 
144
    ...     else:
 
145
    ...         val = val.replace('XXXX', 'CLIENT1')
 
146
    ...         section[newkey] = val
 
147
    >>> cfg.walk(transform, call_on_sections=True)
 
148
    {'CLIENT1section': {'CLIENT1key': None}}
 
149
    >>> cfg
 
150
    ConfigObj({'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}})
 
151
    """
 
152
 
 
153
 
 
154
def _test_reset():
 
155
    """
 
156
    >>> something = object()
 
157
    >>> c = ConfigObj()
 
158
    >>> c['something'] = something
 
159
    >>> c['section'] = {'something': something}
 
160
    >>> c.filename = 'fish'
 
161
    >>> c.raise_errors = something
 
162
    >>> c.list_values = something
 
163
    >>> c.create_empty = something
 
164
    >>> c.file_error = something
 
165
    >>> c.stringify = something
 
166
    >>> c.indent_type = something
 
167
    >>> c.encoding = something
 
168
    >>> c.default_encoding = something
 
169
    >>> c.BOM = something
 
170
    >>> c.newlines = something
 
171
    >>> c.write_empty_values = something
 
172
    >>> c.unrepr = something
 
173
    >>> c.initial_comment = something
 
174
    >>> c.final_comment = something
 
175
    >>> c.configspec = something
 
176
    >>> c.inline_comments = something
 
177
    >>> c.comments = something
 
178
    >>> c.defaults = something
 
179
    >>> c.default_values = something
 
180
    >>> c.reset()
 
181
    >>> 
 
182
    >>> c.filename
 
183
    >>> c.raise_errors
 
184
    False
 
185
    >>> c.list_values
 
186
    True
 
187
    >>> c.create_empty
 
188
    False
 
189
    >>> c.file_error
 
190
    False
 
191
    >>> c.interpolation
 
192
    True
 
193
    >>> c.configspec
 
194
    >>> c.stringify
 
195
    True
 
196
    >>> c.indent_type
 
197
    >>> c.encoding
 
198
    >>> c.default_encoding
 
199
    >>> c.unrepr
 
200
    False
 
201
    >>> c.write_empty_values
 
202
    False
 
203
    >>> c.inline_comments
 
204
    {}
 
205
    >>> c.comments
 
206
    {}
 
207
    >>> c.defaults
 
208
    []
 
209
    >>> c.default_values
 
210
    {}
 
211
    >>> c == ConfigObj()
 
212
    True
 
213
    >>> c
 
214
    ConfigObj({})
 
215
    """
 
216
 
 
217
 
 
218
def _test_reload():
 
219
    """
 
220
    >>> c = ConfigObj(StringIO())
 
221
    >>> c.reload()
 
222
    Traceback (most recent call last):
 
223
    ReloadError: reload failed, filename is not set.
 
224
    >>> c = ConfigObj()
 
225
    >>> c.reload()
 
226
    Traceback (most recent call last):
 
227
    ReloadError: reload failed, filename is not set.
 
228
    >>> c = ConfigObj([])
 
229
    >>> c.reload()
 
230
    Traceback (most recent call last):
 
231
    ReloadError: reload failed, filename is not set.
 
232
    
 
233
    We need to use a real file as reload is only for files loaded from
 
234
    the filesystem.
 
235
    >>> h = open('temp', 'w')
 
236
    >>> h.write('''
 
237
    ...     test1=40
 
238
    ...     test2=hello
 
239
    ...     test3=3
 
240
    ...     test4=5.0
 
241
    ...     [section]
 
242
    ...         test1=40
 
243
    ...         test2=hello
 
244
    ...         test3=3
 
245
    ...         test4=5.0
 
246
    ...         [[sub section]]
 
247
    ...             test1=40
 
248
    ...             test2=hello
 
249
    ...             test3=3
 
250
    ...             test4=5.0
 
251
    ...     [section2]
 
252
    ...         test1=40
 
253
    ...         test2=hello
 
254
    ...         test3=3
 
255
    ...         test4=5.0
 
256
    ... ''')
 
257
    >>> h.close()
 
258
    >>> configspec = '''
 
259
    ...     test1= integer(30,50)
 
260
    ...     test2= string
 
261
    ...     test3=integer
 
262
    ...     test4=float(4.5)
 
263
    ...     [section]
 
264
    ...         test1=integer(30,50)
 
265
    ...         test2=string
 
266
    ...         test3=integer
 
267
    ...         test4=float(4.5)
 
268
    ...         [[sub section]]
 
269
    ...             test1=integer(30,50)
 
270
    ...             test2=string
 
271
    ...             test3=integer
 
272
    ...             test4=float(4.5)
 
273
    ...     [section2]
 
274
    ...         test1=integer(30,50)
 
275
    ...         test2=string
 
276
    ...         test3=integer
 
277
    ...         test4=float(4.5)
 
278
    ...     '''.split('\\n')
 
279
    >>> c = ConfigObj('temp', configspec=configspec)
 
280
    >>> c.configspec['test1'] = 'integer(50,60)'
 
281
    >>> backup = ConfigObj('temp')
 
282
    >>> del c['section']
 
283
    >>> del c['test1']
 
284
    >>> c['extra'] = '3'
 
285
    >>> c['section2']['extra'] = '3'
 
286
    >>> c.reload()
 
287
    >>> c == backup
 
288
    True
 
289
    >>> c.validate(Validator())
 
290
    True
 
291
    >>> os.remove('temp')
 
292
    """
 
293
 
 
294
 
 
295
def _doctest():
 
296
    """
 
297
    Dummy function to hold some of the doctests.
 
298
    
 
299
    >>> a.depth
 
300
    0
 
301
    >>> a == {
 
302
    ...     'key2': 'val',
 
303
    ...     'key1': 'val',
 
304
    ...     'lev1c': {
 
305
    ...         'lev2c': {
 
306
    ...             'lev3c': {
 
307
    ...                 'key1': 'val',
 
308
    ...             },
 
309
    ...         },
 
310
    ...     },
 
311
    ...     'lev1b': {
 
312
    ...         'key2': 'val',
 
313
    ...         'key1': 'val',
 
314
    ...         'lev2ba': {
 
315
    ...             'key1': 'val',
 
316
    ...         },
 
317
    ...         'lev2bb': {
 
318
    ...             'key1': 'val',
 
319
    ...         },
 
320
    ...     },
 
321
    ...     'lev1a': {
 
322
    ...         'key2': 'val',
 
323
    ...         'key1': 'val',
 
324
    ...     },
 
325
    ... }
 
326
    1
 
327
    >>> b.depth
 
328
    0
 
329
    >>> b == {
 
330
    ...     'key3': 'val3',
 
331
    ...     'key2': 'val2',
 
332
    ...     'key1': 'val1',
 
333
    ...     'section 1': {
 
334
    ...         'keys11': 'val1',
 
335
    ...         'keys13': 'val3',
 
336
    ...         'keys12': 'val2',
 
337
    ...     },
 
338
    ...     'section 2': {
 
339
    ...         'section 2 sub 1': {
 
340
    ...             'fish': '3',
 
341
    ...     },
 
342
    ...     'keys21': 'val1',
 
343
    ...     'keys22': 'val2',
 
344
    ...     'keys23': 'val3',
 
345
    ...     },
 
346
    ... }
 
347
    1
 
348
    >>> t = '''
 
349
    ... 'a' = b # !"$%^&*(),::;'@~#= 33
 
350
    ... "b" = b #= 6, 33
 
351
    ... ''' .split('\\n')
 
352
    >>> t2 = ConfigObj(t)
 
353
    >>> assert t2 == {'a': 'b', 'b': 'b'}
 
354
    >>> t2.inline_comments['b'] = ''
 
355
    >>> del t2['a']
 
356
    >>> assert t2.write() == ['','b = b', '']
 
357
    
 
358
    # Test ``list_values=False`` stuff
 
359
    >>> c = '''
 
360
    ...     key1 = no quotes
 
361
    ...     key2 = 'single quotes'
 
362
    ...     key3 = "double quotes"
 
363
    ...     key4 = "list", 'with', several, "quotes"
 
364
    ...     '''
 
365
    >>> cfg = ConfigObj(c.splitlines(), list_values=False)
 
366
    >>> cfg == {'key1': 'no quotes', 'key2': "'single quotes'", 
 
367
    ... 'key3': '"double quotes"', 
 
368
    ... 'key4': '"list", \\'with\\', several, "quotes"'
 
369
    ... }
 
370
    1
 
371
    >>> cfg = ConfigObj(list_values=False)
 
372
    >>> cfg['key1'] = 'Multiline\\nValue'
 
373
    >>> cfg['key2'] = '''"Value" with 'quotes' !'''
 
374
    >>> cfg.write()
 
375
    ["key1 = '''Multiline\\nValue'''", 'key2 = "Value" with \\'quotes\\' !']
 
376
    >>> cfg.list_values = True
 
377
    >>> cfg.write() == ["key1 = '''Multiline\\nValue'''",
 
378
    ... 'key2 = \\'\\'\\'"Value" with \\'quotes\\' !\\'\\'\\'']
 
379
    1
 
380
    
 
381
    Test flatten_errors:
 
382
    
 
383
    >>> config = '''
 
384
    ...     test1=40
 
385
    ...     test2=hello
 
386
    ...     test3=3
 
387
    ...     test4=5.0
 
388
    ...     [section]
 
389
    ...         test1=40
 
390
    ...         test2=hello
 
391
    ...         test3=3
 
392
    ...         test4=5.0
 
393
    ...         [[sub section]]
 
394
    ...             test1=40
 
395
    ...             test2=hello
 
396
    ...             test3=3
 
397
    ...             test4=5.0
 
398
    ... '''.split('\\n')
 
399
    >>> configspec = '''
 
400
    ...     test1= integer(30,50)
 
401
    ...     test2= string
 
402
    ...     test3=integer
 
403
    ...     test4=float(6.0)
 
404
    ...     [section]
 
405
    ...         test1=integer(30,50)
 
406
    ...         test2=string
 
407
    ...         test3=integer
 
408
    ...         test4=float(6.0)
 
409
    ...         [[sub section]]
 
410
    ...             test1=integer(30,50)
 
411
    ...             test2=string
 
412
    ...             test3=integer
 
413
    ...             test4=float(6.0)
 
414
    ...     '''.split('\\n')
 
415
    >>> val = Validator()
 
416
    >>> c1 = ConfigObj(config, configspec=configspec)
 
417
    >>> res = c1.validate(val)
 
418
    >>> flatten_errors(c1, res) == [([], 'test4', False), (['section', 
 
419
    ...     'sub section'], 'test4', False), (['section'], 'test4', False)]
 
420
    True
 
421
    >>> res = c1.validate(val, preserve_errors=True)
 
422
    >>> check = flatten_errors(c1, res)
 
423
    >>> check[0][:2]
 
424
    ([], 'test4')
 
425
    >>> check[1][:2]
 
426
    (['section', 'sub section'], 'test4')
 
427
    >>> check[2][:2]
 
428
    (['section'], 'test4')
 
429
    >>> for entry in check:
 
430
    ...     isinstance(entry[2], VdtValueTooSmallError)
 
431
    ...     print str(entry[2])
 
432
    True
 
433
    the value "5.0" is too small.
 
434
    True
 
435
    the value "5.0" is too small.
 
436
    True
 
437
    the value "5.0" is too small.
 
438
    
 
439
    Test unicode handling, BOM, write witha file like object and line endings :
 
440
    >>> u_base = '''
 
441
    ... # initial comment
 
442
    ...     # inital comment 2
 
443
    ... 
 
444
    ... test1 = some value
 
445
    ... # comment
 
446
    ... test2 = another value    # inline comment
 
447
    ... # section comment
 
448
    ... [section]    # inline comment
 
449
    ...     test = test    # another inline comment
 
450
    ...     test2 = test2
 
451
    ... 
 
452
    ... # final comment
 
453
    ... # final comment2
 
454
    ... '''
 
455
    >>> u = u_base.encode('utf_8').splitlines(True)
 
456
    >>> u[0] = BOM_UTF8 + u[0]
 
457
    >>> uc = ConfigObj(u)
 
458
    >>> uc.encoding = None
 
459
    >>> uc.BOM == True
 
460
    1
 
461
    >>> uc == {'test1': 'some value', 'test2': 'another value',
 
462
    ... 'section': {'test': 'test', 'test2': 'test2'}}
 
463
    1
 
464
    >>> uc = ConfigObj(u, encoding='utf_8', default_encoding='latin-1')
 
465
    >>> uc.BOM
 
466
    1
 
467
    >>> isinstance(uc['test1'], unicode)
 
468
    1
 
469
    >>> uc.encoding
 
470
    'utf_8'
 
471
    >>> uc.newlines
 
472
    '\\n'
 
473
    >>> uc['latin1'] = "This costs lot's of "
 
474
    >>> a_list = uc.write()
 
475
    >>> len(a_list)
 
476
    15
 
477
    >>> isinstance(a_list[0], str)
 
478
    1
 
479
    >>> a_list[0].startswith(BOM_UTF8)
 
480
    1
 
481
    >>> u = u_base.replace('\\n', '\\r\\n').encode('utf_8').splitlines(True)
 
482
    >>> uc = ConfigObj(u)
 
483
    >>> uc.newlines
 
484
    '\\r\\n'
 
485
    >>> uc.newlines = '\\r'
 
486
    >>> file_like = StringIO()
 
487
    >>> uc.write(file_like)
 
488
    >>> file_like.seek(0)
 
489
    >>> uc2 = ConfigObj(file_like)
 
490
    >>> uc2 == uc
 
491
    1
 
492
    >>> uc2.filename == None
 
493
    1
 
494
    >>> uc2.newlines == '\\r'
 
495
    1
 
496
    
 
497
    Test validate in copy mode
 
498
    >>> a = '''
 
499
    ... # Initial Comment
 
500
    ... 
 
501
    ... key1 = string(default=Hello)
 
502
    ... 
 
503
    ... # section comment
 
504
    ... [section] # inline comment
 
505
    ... # key1 comment
 
506
    ... key1 = integer(default=6)
 
507
    ... # key2 comment
 
508
    ... key2 = boolean(default=True)
 
509
    ... 
 
510
    ...     # subsection comment
 
511
    ...     [[sub-section]] # inline comment
 
512
    ...     # another key1 comment
 
513
    ...     key1 = float(default=3.0)'''.splitlines()
 
514
    >>> b = ConfigObj(configspec=a)
 
515
    >>> b.validate(val, copy=True)
 
516
    1
 
517
    >>> b.write() == ['',
 
518
    ... '# Initial Comment',
 
519
    ... '',
 
520
    ... 'key1 = Hello',
 
521
    ... '',
 
522
    ... '# section comment',
 
523
    ... '[section]    # inline comment',
 
524
    ... '    # key1 comment',
 
525
    ... '    key1 = 6',
 
526
    ... '    # key2 comment',
 
527
    ... '    key2 = True',
 
528
    ... '    ',
 
529
    ... '    # subsection comment',
 
530
    ... '    [[sub-section]]    # inline comment',
 
531
    ... '        # another key1 comment',
 
532
    ... '        key1 = 3.0']
 
533
    1
 
534
    
 
535
    Test Writing Empty Values
 
536
    >>> a = '''
 
537
    ...     key1 =
 
538
    ...     key2 =# a comment''' 
 
539
    >>> b = ConfigObj(a.splitlines())
 
540
    >>> b.write()
 
541
    ['', 'key1 = ""', 'key2 = ""    # a comment']
 
542
    >>> b.write_empty_values = True
 
543
    >>> b.write()
 
544
    ['', 'key1 = ', 'key2 =     # a comment']
 
545
    
 
546
    Test unrepr when reading
 
547
    >>> a = '''
 
548
    ...     key1 = (1, 2, 3)    # comment
 
549
    ...     key2 = True
 
550
    ...     key3 = 'a string'
 
551
    ...     key4 = [1, 2, 3, 'a mixed list']
 
552
    ... '''.splitlines()
 
553
    >>> b = ConfigObj(a, unrepr=True)
 
554
    >>> b == {'key1': (1, 2, 3),
 
555
    ... 'key2': True,
 
556
    ... 'key3': 'a string',
 
557
    ... 'key4': [1, 2, 3, 'a mixed list']}
 
558
    1
 
559
    
 
560
    Test unrepr when writing
 
561
    >>> c = ConfigObj(b.write(), unrepr=True)
 
562
    >>> c == b
 
563
    1
 
564
    
 
565
    Test unrepr with multiline values
 
566
    >>> a = '''k = \"""{
 
567
    ... 'k1': 3,
 
568
    ... 'k2': 6.0}\"""
 
569
    ... '''.splitlines()
 
570
    >>> c = ConfigObj(a, unrepr=True)
 
571
    >>> c == {'k': {'k1': 3, 'k2': 6.0}}
 
572
    1
 
573
    
 
574
    Test unrepr with a dictionary
 
575
    >>> a = 'k = {"a": 1}'.splitlines()
 
576
    >>> c = ConfigObj(a, unrepr=True)
 
577
    >>> type(c['k']) == dict
 
578
    1
 
579
 
 
580
     Test # with unrepr
 
581
     >>> a = '''
 
582
     ...     key1 = (1, 2, 3)    # comment
 
583
     ...     key2 = True
 
584
     ...     key3 = 'a string'
 
585
     ...     key4 = [1, 2, 3, 'a mixed list#']
 
586
     ... '''.splitlines()
 
587
     >>> b = ConfigObj(a, unrepr=True)
 
588
     >>> b == {'key1': (1, 2, 3),
 
589
     ... 'key2': True,
 
590
     ... 'key3': 'a string',
 
591
     ... 'key4': [1, 2, 3, 'a mixed list#']}
 
592
     1
 
593
    """
 
594
    
 
595
    # Comments are no longer parsed from values in configspecs 
 
596
    # so the following test fails and is disabled
 
597
    untested = """
 
598
    Test validate in copy mode
 
599
    >>> a = '''
 
600
    ... # Initial Comment
 
601
    ... 
 
602
    ... key1 = string(default=Hello)    # comment 1
 
603
    ... 
 
604
    ... # section comment
 
605
    ... [section] # inline comment
 
606
    ... # key1 comment
 
607
    ... key1 = integer(default=6) # an integer value
 
608
    ... # key2 comment
 
609
    ... key2 = boolean(default=True) # a boolean
 
610
    ... 
 
611
    ...     # subsection comment
 
612
    ...     [[sub-section]] # inline comment
 
613
    ...     # another key1 comment
 
614
    ...     key1 = float(default=3.0) # a float'''.splitlines()
 
615
    >>> b = ConfigObj(configspec=a)
 
616
    >>> b.validate(val, copy=True)
 
617
    1
 
618
    >>> b.write()
 
619
    >>> b.write() == ['',
 
620
    ... '# Initial Comment',
 
621
    ... '',
 
622
    ... 'key1 = Hello    # comment 1',
 
623
    ... '',
 
624
    ... '# section comment',
 
625
    ... '[section]    # inline comment',
 
626
    ... '    # key1 comment',
 
627
    ... '    key1 = 6    # an integer value',
 
628
    ... '    # key2 comment',
 
629
    ... '    key2 = True    # a boolean',
 
630
    ... '    ',
 
631
    ... '    # subsection comment',
 
632
    ... '    [[sub-section]]    # inline comment',
 
633
    ... '        # another key1 comment',
 
634
    ... '        key1 = 3.0    # a float']
 
635
    1
 
636
    """
 
637
 
 
638
 
 
639
def _test_configobj():
 
640
    """
 
641
    Testing ConfigObj
 
642
    Testing with duplicate keys and sections.
 
643
    
 
644
    >>> c = '''
 
645
    ... [hello]
 
646
    ... member = value
 
647
    ... [hello again]
 
648
    ... member = value
 
649
    ... [ "hello" ]
 
650
    ... member = value
 
651
    ... '''
 
652
    >>> ConfigObj(c.split('\\n'), raise_errors = True)
 
653
    Traceback (most recent call last):
 
654
    DuplicateError: Duplicate section name at line 6.
 
655
    
 
656
    >>> d = '''
 
657
    ... [hello]
 
658
    ... member = value
 
659
    ... [hello again]
 
660
    ... member1 = value
 
661
    ... member2 = value
 
662
    ... 'member1' = value
 
663
    ... [ "and again" ]
 
664
    ... member = value
 
665
    ... '''
 
666
    >>> ConfigObj(d.split('\\n'), raise_errors = True)
 
667
    Traceback (most recent call last):
 
668
    DuplicateError: Duplicate keyword name at line 7.
 
669
 
 
670
    Testing ConfigParser-style interpolation
 
671
    
 
672
    >>> c = ConfigObj()
 
673
    >>> c['DEFAULT'] = {
 
674
    ...     'b': 'goodbye',
 
675
    ...     'userdir': 'c:\\\\home',
 
676
    ...     'c': '%(d)s',
 
677
    ...     'd': '%(c)s'
 
678
    ... }
 
679
    >>> c['section'] = {
 
680
    ...     'a': '%(datadir)s\\\\some path\\\\file.py',
 
681
    ...     'b': '%(userdir)s\\\\some path\\\\file.py',
 
682
    ...     'c': 'Yo %(a)s',
 
683
    ...     'd': '%(not_here)s',
 
684
    ...     'e': '%(e)s',
 
685
    ... }
 
686
    >>> c['section']['DEFAULT'] = {
 
687
    ...     'datadir': 'c:\\\\silly_test',
 
688
    ...     'a': 'hello - %(b)s',
 
689
    ... }
 
690
    >>> c['section']['a'] == 'c:\\\\silly_test\\\\some path\\\\file.py'
 
691
    1
 
692
    >>> c['section']['b'] == 'c:\\\\home\\\\some path\\\\file.py'
 
693
    1
 
694
    >>> c['section']['c'] == 'Yo c:\\\\silly_test\\\\some path\\\\file.py'
 
695
    1
 
696
    
 
697
    Switching Interpolation Off
 
698
    
 
699
    >>> c.interpolation = False
 
700
    >>> c['section']['a'] == '%(datadir)s\\\\some path\\\\file.py'
 
701
    1
 
702
    >>> c['section']['b'] == '%(userdir)s\\\\some path\\\\file.py'
 
703
    1
 
704
    >>> c['section']['c'] == 'Yo %(a)s'
 
705
    1
 
706
    
 
707
    Testing the interpolation errors.
 
708
    
 
709
    >>> c.interpolation = True
 
710
    >>> c['section']['d']
 
711
    Traceback (most recent call last):
 
712
    MissingInterpolationOption: missing option "not_here" in interpolation.
 
713
    >>> c['section']['e']
 
714
    Traceback (most recent call last):
 
715
    InterpolationLoopError: interpolation loop detected in value "e".
 
716
    
 
717
    Testing Template-style interpolation
 
718
    
 
719
    >>> interp_cfg = '''
 
720
    ... [DEFAULT]
 
721
    ... keyword1 = value1
 
722
    ... 'keyword 2' = 'value 2'
 
723
    ... reference = ${keyword1}
 
724
    ... foo = 123
 
725
    ... 
 
726
    ... [ section ]
 
727
    ... templatebare = $keyword1/foo
 
728
    ... bar = $$foo
 
729
    ... dollar = $$300.00
 
730
    ... stophere = $$notinterpolated
 
731
    ... with_braces = ${keyword1}s (plural)
 
732
    ... with_spaces = ${keyword 2}!!!
 
733
    ... with_several = $keyword1/$reference/$keyword1
 
734
    ... configparsersample = %(keyword 2)sconfig
 
735
    ... deep = ${reference}
 
736
    ... 
 
737
    ...     [[DEFAULT]]
 
738
    ...     baz = $foo
 
739
    ... 
 
740
    ...     [[ sub-section ]]
 
741
    ...     quux = '$baz + $bar + $foo'
 
742
    ... 
 
743
    ...         [[[ sub-sub-section ]]]
 
744
    ...         convoluted = "$bar + $baz + $quux + $bar"
 
745
    ... '''
 
746
    >>> c = ConfigObj(interp_cfg.split('\\n'), interpolation='Template')
 
747
    >>> c['section']['templatebare']
 
748
    'value1/foo'
 
749
    >>> c['section']['dollar']
 
750
    '$300.00'
 
751
    >>> c['section']['stophere']
 
752
    '$notinterpolated'
 
753
    >>> c['section']['with_braces']
 
754
    'value1s (plural)'
 
755
    >>> c['section']['with_spaces']
 
756
    'value 2!!!'
 
757
    >>> c['section']['with_several']
 
758
    'value1/value1/value1'
 
759
    >>> c['section']['configparsersample']
 
760
    '%(keyword 2)sconfig'
 
761
    >>> c['section']['deep']
 
762
    'value1'
 
763
    >>> c['section']['sub-section']['quux']
 
764
    '123 + $foo + 123'
 
765
    >>> c['section']['sub-section']['sub-sub-section']['convoluted']
 
766
    '$foo + 123 + 123 + $foo + 123 + $foo'
 
767
    
 
768
    Testing our quoting.
 
769
    
 
770
    >>> i._quote('\"""\\'\\'\\'')
 
771
    Traceback (most recent call last):
 
772
    ConfigObjError: Value \"\"""'''" cannot be safely quoted.
 
773
    >>> try:
 
774
    ...     i._quote('\\n', multiline=False)
 
775
    ... except ConfigObjError, e:
 
776
    ...    e.msg
 
777
    'Value "\\n" cannot be safely quoted.'
 
778
    >>> i._quote(' "\\' ', multiline=False)
 
779
    Traceback (most recent call last):
 
780
    ConfigObjError: Value " "' " cannot be safely quoted.
 
781
    
 
782
    Testing with "stringify" off.
 
783
    >>> c.stringify = False
 
784
    >>> c['test'] = 1
 
785
    Traceback (most recent call last):
 
786
    TypeError: Value is not a string "1".
 
787
    
 
788
    Testing Empty values.
 
789
    >>> cfg_with_empty = '''
 
790
    ... k =
 
791
    ... k2 =# comment test
 
792
    ... val = test
 
793
    ... val2 = ,
 
794
    ... val3 = 1,
 
795
    ... val4 = 1, 2
 
796
    ... val5 = 1, 2, \'''.splitlines()
 
797
    >>> cwe = ConfigObj(cfg_with_empty)
 
798
    >>> cwe == {'k': '', 'k2': '', 'val': 'test', 'val2': [],
 
799
    ...  'val3': ['1'], 'val4': ['1', '2'], 'val5': ['1', '2']}
 
800
    1
 
801
    >>> cwe = ConfigObj(cfg_with_empty, list_values=False)
 
802
    >>> cwe == {'k': '', 'k2': '', 'val': 'test', 'val2': ',',
 
803
    ...  'val3': '1,', 'val4': '1, 2', 'val5': '1, 2,'}
 
804
    1
 
805
    
 
806
    Testing list values.
 
807
    >>> testconfig3 = \'''
 
808
    ... a = ,
 
809
    ... b = test,
 
810
    ... c = test1, test2   , test3
 
811
    ... d = test1, test2, test3,
 
812
    ... \'''
 
813
    >>> d = ConfigObj(testconfig3.split('\\n'), raise_errors=True)
 
814
    >>> d['a'] == []
 
815
    1
 
816
    >>> d['b'] == ['test']
 
817
    1
 
818
    >>> d['c'] == ['test1', 'test2', 'test3']
 
819
    1
 
820
    >>> d['d'] == ['test1', 'test2', 'test3']
 
821
    1
 
822
    
 
823
    Testing with list values off.
 
824
    
 
825
    >>> e = ConfigObj(
 
826
    ...     testconfig3.split('\\n'),
 
827
    ...     raise_errors=True,
 
828
    ...     list_values=False)
 
829
    >>> e['a'] == ','
 
830
    1
 
831
    >>> e['b'] == 'test,'
 
832
    1
 
833
    >>> e['c'] == 'test1, test2   , test3'
 
834
    1
 
835
    >>> e['d'] == 'test1, test2, test3,'
 
836
    1
 
837
    
 
838
    Testing creating from a dictionary.
 
839
    
 
840
    >>> f = {
 
841
    ...     'key1': 'val1',
 
842
    ...     'key2': 'val2',
 
843
    ...     'section 1': {
 
844
    ...         'key1': 'val1',
 
845
    ...         'key2': 'val2',
 
846
    ...         'section 1b': {
 
847
    ...             'key1': 'val1',
 
848
    ...             'key2': 'val2',
 
849
    ...         },
 
850
    ...     },
 
851
    ...     'section 2': {
 
852
    ...         'key1': 'val1',
 
853
    ...         'key2': 'val2',
 
854
    ...         'section 2b': {
 
855
    ...             'key1': 'val1',
 
856
    ...             'key2': 'val2',
 
857
    ...         },
 
858
    ...     },
 
859
    ...      'key3': 'val3',
 
860
    ... }
 
861
    >>> g = ConfigObj(f)
 
862
    >>> f == g
 
863
    1
 
864
    
 
865
    Testing we correctly detect badly built list values (4 of them).
 
866
    
 
867
    >>> testconfig4 = '''
 
868
    ... config = 3,4,,
 
869
    ... test = 3,,4
 
870
    ... fish = ,,
 
871
    ... dummy = ,,hello, goodbye
 
872
    ... '''
 
873
    >>> try:
 
874
    ...     ConfigObj(testconfig4.split('\\n'))
 
875
    ... except ConfigObjError, e:
 
876
    ...     len(e.errors)
 
877
    4
 
878
    
 
879
    Testing we correctly detect badly quoted values (4 of them).
 
880
    
 
881
    >>> testconfig5 = '''
 
882
    ... config = "hello   # comment
 
883
    ... test = 'goodbye
 
884
    ... fish = 'goodbye   # comment
 
885
    ... dummy = "hello again
 
886
    ... '''
 
887
    >>> try:
 
888
    ...     ConfigObj(testconfig5.split('\\n'))
 
889
    ... except ConfigObjError, e:
 
890
    ...     len(e.errors)
 
891
    4
 
892
    
 
893
    Test Multiline Comments
 
894
    >>> i == {
 
895
    ...     'name4': ' another single line value ',
 
896
    ...     'multi section': {
 
897
    ...         'name4': '\\n        Well, this is a\\n        multiline '
 
898
    ...             'value\\n        ',
 
899
    ...         'name2': '\\n        Well, this is a\\n        multiline '
 
900
    ...             'value\\n        ',
 
901
    ...         'name3': '\\n        Well, this is a\\n        multiline '
 
902
    ...             'value\\n        ',
 
903
    ...         'name1': '\\n        Well, this is a\\n        multiline '
 
904
    ...             'value\\n        ',
 
905
    ...     },
 
906
    ...     'name2': ' another single line value ',
 
907
    ...     'name3': ' a single line value ',
 
908
    ...     'name1': ' a single line value ',
 
909
    ... }
 
910
    1
 
911
     
 
912
    >>> filename = a.filename
 
913
    >>> a.filename = None
 
914
    >>> values = a.write()
 
915
    >>> index = 0
 
916
    >>> while index < 23:
 
917
    ...     index += 1
 
918
    ...     line = values[index-1]
 
919
    ...     assert line.endswith('# comment ' + str(index))
 
920
    >>> a.filename = filename
 
921
    
 
922
    >>> start_comment = ['# Initial Comment', '', '#']
 
923
    >>> end_comment = ['', '#', '# Final Comment']
 
924
    >>> newconfig = start_comment + testconfig1.split('\\n') + end_comment
 
925
    >>> nc = ConfigObj(newconfig)
 
926
    >>> nc.initial_comment
 
927
    ['# Initial Comment', '', '#']
 
928
    >>> nc.final_comment
 
929
    ['', '#', '# Final Comment']
 
930
    >>> nc.initial_comment == start_comment
 
931
    1
 
932
    >>> nc.final_comment == end_comment
 
933
    1
 
934
    
 
935
    Test the _handle_comment method
 
936
    
 
937
    >>> c = ConfigObj()
 
938
    >>> c['foo'] = 'bar'
 
939
    >>> c.inline_comments['foo'] = 'Nice bar'
 
940
    >>> c.write()
 
941
    ['foo = bar # Nice bar']
 
942
    
 
943
    tekNico: FIXME: use StringIO instead of real files
 
944
    
 
945
    >>> filename = a.filename
 
946
    >>> a.filename = 'test.ini'
 
947
    >>> a.write()
 
948
    >>> a.filename = filename
 
949
    >>> a == ConfigObj('test.ini', raise_errors=True)
 
950
    1
 
951
    >>> os.remove('test.ini')
 
952
    >>> b.filename = 'test.ini'
 
953
    >>> b.write()
 
954
    >>> b == ConfigObj('test.ini', raise_errors=True)
 
955
    1
 
956
    >>> os.remove('test.ini')
 
957
    >>> i.filename = 'test.ini'
 
958
    >>> i.write()
 
959
    >>> i == ConfigObj('test.ini', raise_errors=True)
 
960
    1
 
961
    >>> os.remove('test.ini')
 
962
    >>> a = ConfigObj()
 
963
    >>> a['DEFAULT'] = {'a' : 'fish'}
 
964
    >>> a['a'] = '%(a)s'
 
965
    >>> a.write()
 
966
    ['a = %(a)s', '[DEFAULT]', 'a = fish']
 
967
    
 
968
    Test indentation handling
 
969
    
 
970
    >>> ConfigObj({'sect': {'sect': {'foo': 'bar'}}}).write()
 
971
    ['[sect]', '    [[sect]]', '        foo = bar']
 
972
    >>> cfg = ['[sect]', '[[sect]]', 'foo = bar']
 
973
    >>> ConfigObj(cfg).write() == cfg
 
974
    1
 
975
    >>> cfg = ['[sect]', '  [[sect]]', '    foo = bar']
 
976
    >>> ConfigObj(cfg).write() == cfg
 
977
    1
 
978
    >>> cfg = ['[sect]', '    [[sect]]', '        foo = bar']
 
979
    >>> ConfigObj(cfg).write() == cfg
 
980
    1
 
981
    >>> ConfigObj(oneTabCfg).write() == oneTabCfg
 
982
    1
 
983
    >>> ConfigObj(twoTabsCfg).write() == twoTabsCfg
 
984
    1
 
985
    >>> ConfigObj(tabsAndSpacesCfg).write() == tabsAndSpacesCfg
 
986
    1
 
987
    >>> ConfigObj(cfg, indent_type=chr(9)).write() == oneTabCfg
 
988
    1
 
989
    >>> ConfigObj(oneTabCfg, indent_type='    ').write() == cfg
 
990
    1
 
991
    """
 
992
 
 
993
 
 
994
def _test_validate():
 
995
    """
 
996
    >>> config = '''
 
997
    ... test1=40
 
998
    ... test2=hello
 
999
    ... test3=3
 
1000
    ... test4=5.0
 
1001
    ... [section]
 
1002
    ...     test1=40
 
1003
    ...     test2=hello
 
1004
    ...     test3=3
 
1005
    ...     test4=5.0
 
1006
    ...     [[sub section]]
 
1007
    ...         test1=40
 
1008
    ...         test2=hello
 
1009
    ...         test3=3
 
1010
    ...         test4=5.0
 
1011
    ... '''.split('\\n')
 
1012
    >>> configspec = '''
 
1013
    ... test1= integer(30,50)
 
1014
    ... test2= string
 
1015
    ... test3=integer
 
1016
    ... test4=float(6.0)
 
1017
    ... [section ]
 
1018
    ...     test1=integer(30,50)
 
1019
    ...     test2=string
 
1020
    ...     test3=integer
 
1021
    ...     test4=float(6.0)
 
1022
    ...     [[sub section]]
 
1023
    ...         test1=integer(30,50)
 
1024
    ...         test2=string
 
1025
    ...         test3=integer
 
1026
    ...         test4=float(6.0)
 
1027
    ... '''.split('\\n')
 
1028
    >>> val = Validator()
 
1029
    >>> c1 = ConfigObj(config, configspec=configspec)
 
1030
    >>> test = c1.validate(val)
 
1031
    >>> test == {
 
1032
    ...         'test1': True,
 
1033
    ...         'test2': True,
 
1034
    ...         'test3': True,
 
1035
    ...         'test4': False,
 
1036
    ...         'section': {
 
1037
    ...             'test1': True,
 
1038
    ...             'test2': True,
 
1039
    ...             'test3': True,
 
1040
    ...             'test4': False,
 
1041
    ...             'sub section': {
 
1042
    ...                 'test1': True,
 
1043
    ...                 'test2': True,
 
1044
    ...                 'test3': True,
 
1045
    ...                 'test4': False,
 
1046
    ...             },
 
1047
    ...         },
 
1048
    ...     }
 
1049
    1
 
1050
    >>> val.check(c1.configspec['test4'], c1['test4'])
 
1051
    Traceback (most recent call last):
 
1052
    VdtValueTooSmallError: the value "5.0" is too small.
 
1053
    
 
1054
    >>> val_test_config = '''
 
1055
    ...     key = 0
 
1056
    ...     key2 = 1.1
 
1057
    ...     [section]
 
1058
    ...     key = some text
 
1059
    ...     key2 = 1.1, 3.0, 17, 6.8
 
1060
    ...         [[sub-section]]
 
1061
    ...         key = option1
 
1062
    ...         key2 = True'''.split('\\n')
 
1063
    >>> val_test_configspec = '''
 
1064
    ...     key = integer
 
1065
    ...     key2 = float
 
1066
    ...     [section]
 
1067
    ...     key = string
 
1068
    ...     key2 = float_list(4)
 
1069
    ...        [[sub-section]]
 
1070
    ...        key = option(option1, option2)
 
1071
    ...        key2 = boolean'''.split('\\n')
 
1072
    >>> val_test = ConfigObj(val_test_config, configspec=val_test_configspec)
 
1073
    >>> val_test.validate(val)
 
1074
    1
 
1075
    >>> val_test['key'] = 'text not a digit'
 
1076
    >>> val_res = val_test.validate(val)
 
1077
    >>> val_res == {'key2': True, 'section': True, 'key': False}
 
1078
    1
 
1079
    >>> configspec = '''
 
1080
    ...     test1=integer(30,50, default=40)
 
1081
    ...     test2=string(default="hello")
 
1082
    ...     test3=integer(default=3)
 
1083
    ...     test4=float(6.0, default=6.0)
 
1084
    ...     [section ]
 
1085
    ...         test1=integer(30,50, default=40)
 
1086
    ...         test2=string(default="hello")
 
1087
    ...         test3=integer(default=3)
 
1088
    ...         test4=float(6.0, default=6.0)
 
1089
    ...         [[sub section]]
 
1090
    ...             test1=integer(30,50, default=40)
 
1091
    ...             test2=string(default="hello")
 
1092
    ...             test3=integer(default=3)
 
1093
    ...             test4=float(6.0, default=6.0)
 
1094
    ...     '''.split('\\n')
 
1095
    >>> default_test = ConfigObj(['test1=30'], configspec=configspec)
 
1096
    >>> default_test
 
1097
    ConfigObj({'test1': '30'})
 
1098
    >>> default_test.defaults
 
1099
    []
 
1100
    >>> default_test.default_values
 
1101
    {}
 
1102
    >>> default_test.validate(val)
 
1103
    1
 
1104
    >>> default_test == {
 
1105
    ...     'test1': 30,
 
1106
    ...     'test2': 'hello',
 
1107
    ...     'test3': 3,
 
1108
    ...     'test4': 6.0,
 
1109
    ...     'section': {
 
1110
    ...         'test1': 40,
 
1111
    ...         'test2': 'hello',
 
1112
    ...         'test3': 3,
 
1113
    ...         'test4': 6.0,
 
1114
    ...         'sub section': {
 
1115
    ...             'test1': 40,
 
1116
    ...             'test3': 3,
 
1117
    ...             'test2': 'hello',
 
1118
    ...             'test4': 6.0,
 
1119
    ...         },
 
1120
    ...     },
 
1121
    ... }
 
1122
    1
 
1123
    >>> default_test.defaults
 
1124
    ['test2', 'test3', 'test4']
 
1125
    >>> default_test.default_values == {'test1': 40, 'test2': 'hello',
 
1126
    ... 'test3': 3, 'test4': 6.0}
 
1127
    1
 
1128
    >>> default_test.restore_default('test1')
 
1129
    40
 
1130
    >>> default_test['test1']
 
1131
    40
 
1132
    >>> 'test1' in default_test.defaults
 
1133
    1
 
1134
    >>> def change(section, key): 
 
1135
    ...     section[key] = 3
 
1136
    >>> _ = default_test.walk(change)
 
1137
    >>> default_test['section']['sub section']['test4']
 
1138
    3
 
1139
    >>> default_test.restore_defaults()
 
1140
    >>> default_test == {
 
1141
    ...     'test1': 40,
 
1142
    ...     'test2': "hello",
 
1143
    ...     'test3': 3,
 
1144
    ...     'test4': 6.0,
 
1145
    ...     'section': {
 
1146
    ...         'test1': 40,
 
1147
    ...         'test2': "hello",
 
1148
    ...         'test3': 3,
 
1149
    ...         'test4': 6.0,
 
1150
    ...         'sub section': {
 
1151
    ...             'test1': 40,
 
1152
    ...             'test2': "hello",
 
1153
    ...             'test3': 3,
 
1154
    ...             'test4': 6.0
 
1155
    ... }}}
 
1156
    1
 
1157
    >>> a = ['foo = fish']
 
1158
    >>> b = ['foo = integer(default=3)']
 
1159
    >>> c = ConfigObj(a, configspec=b)
 
1160
    >>> c
 
1161
    ConfigObj({'foo': 'fish'})
 
1162
    >>> from validate import Validator
 
1163
    >>> v = Validator()
 
1164
    >>> c.validate(v)
 
1165
    0
 
1166
    >>> c.default_values
 
1167
    {'foo': 3}
 
1168
    >>> c.restore_default('foo')
 
1169
    3
 
1170
 
 
1171
    Now testing with repeated sections : BIG TEST
 
1172
    
 
1173
    >>> repeated_1 = '''
 
1174
    ... [dogs]
 
1175
    ...     [[__many__]] # spec for a dog
 
1176
    ...         fleas = boolean(default=True)
 
1177
    ...         tail = option(long, short, default=long)
 
1178
    ...         name = string(default=rover)
 
1179
    ...         [[[__many__]]]  # spec for a puppy
 
1180
    ...             name = string(default="son of rover")
 
1181
    ...             age = float(default=0.0)
 
1182
    ... [cats]
 
1183
    ...     [[__many__]] # spec for a cat
 
1184
    ...         fleas = boolean(default=True)
 
1185
    ...         tail = option(long, short, default=short)
 
1186
    ...         name = string(default=pussy)
 
1187
    ...         [[[__many__]]] # spec for a kitten
 
1188
    ...             name = string(default="son of pussy")
 
1189
    ...             age = float(default=0.0)
 
1190
    ...         '''.split('\\n')
 
1191
    >>> repeated_2 = '''
 
1192
    ... [dogs]
 
1193
    ... 
 
1194
    ...     # blank dogs with puppies
 
1195
    ...     # should be filled in by the configspec
 
1196
    ...     [[dog1]]
 
1197
    ...         [[[puppy1]]]
 
1198
    ...         [[[puppy2]]]
 
1199
    ...         [[[puppy3]]]
 
1200
    ...     [[dog2]]
 
1201
    ...         [[[puppy1]]]
 
1202
    ...         [[[puppy2]]]
 
1203
    ...         [[[puppy3]]]
 
1204
    ...     [[dog3]]
 
1205
    ...         [[[puppy1]]]
 
1206
    ...         [[[puppy2]]]
 
1207
    ...         [[[puppy3]]]
 
1208
    ... [cats]
 
1209
    ... 
 
1210
    ...     # blank cats with kittens
 
1211
    ...     # should be filled in by the configspec
 
1212
    ...     [[cat1]]
 
1213
    ...         [[[kitten1]]]
 
1214
    ...         [[[kitten2]]]
 
1215
    ...         [[[kitten3]]]
 
1216
    ...     [[cat2]]
 
1217
    ...         [[[kitten1]]]
 
1218
    ...         [[[kitten2]]]
 
1219
    ...         [[[kitten3]]]
 
1220
    ...     [[cat3]]
 
1221
    ...         [[[kitten1]]]
 
1222
    ...         [[[kitten2]]]
 
1223
    ...         [[[kitten3]]]
 
1224
    ... '''.split('\\n')
 
1225
    >>> repeated_3 = '''
 
1226
    ... [dogs]
 
1227
    ... 
 
1228
    ...     [[dog1]]
 
1229
    ...     [[dog2]]
 
1230
    ...     [[dog3]]
 
1231
    ... [cats]
 
1232
    ... 
 
1233
    ...     [[cat1]]
 
1234
    ...     [[cat2]]
 
1235
    ...     [[cat3]]
 
1236
    ... '''.split('\\n')
 
1237
    >>> repeated_4 = '''
 
1238
    ... [__many__]
 
1239
    ... 
 
1240
    ...     name = string(default=Michael)
 
1241
    ...     age = float(default=0.0)
 
1242
    ...     sex = option(m, f, default=m)
 
1243
    ... '''.split('\\n')
 
1244
    >>> repeated_5 = '''
 
1245
    ... [cats]
 
1246
    ... [[__many__]]
 
1247
    ...     fleas = boolean(default=True)
 
1248
    ...     tail = option(long, short, default=short)
 
1249
    ...     name = string(default=pussy)
 
1250
    ...     [[[description]]]
 
1251
    ...         height = float(default=3.3)
 
1252
    ...         weight = float(default=6)
 
1253
    ...         [[[[coat]]]]
 
1254
    ...             fur = option(black, grey, brown, "tortoise shell", default=black)
 
1255
    ...             condition = integer(0,10, default=5)
 
1256
    ... '''.split('\\n')
 
1257
    >>> val= Validator()
 
1258
    >>> repeater = ConfigObj(repeated_2, configspec=repeated_1)
 
1259
    >>> repeater.validate(val)
 
1260
    1
 
1261
    >>> repeater == {
 
1262
    ...     'dogs': {
 
1263
    ...         'dog1': {
 
1264
    ...             'fleas': True,
 
1265
    ...             'tail': 'long',
 
1266
    ...             'name': 'rover',
 
1267
    ...             'puppy1': {'name': 'son of rover', 'age': 0.0},
 
1268
    ...             'puppy2': {'name': 'son of rover', 'age': 0.0},
 
1269
    ...             'puppy3': {'name': 'son of rover', 'age': 0.0},
 
1270
    ...         },
 
1271
    ...         'dog2': {
 
1272
    ...             'fleas': True,
 
1273
    ...             'tail': 'long',
 
1274
    ...             'name': 'rover',
 
1275
    ...             'puppy1': {'name': 'son of rover', 'age': 0.0},
 
1276
    ...             'puppy2': {'name': 'son of rover', 'age': 0.0},
 
1277
    ...             'puppy3': {'name': 'son of rover', 'age': 0.0},
 
1278
    ...         },
 
1279
    ...         'dog3': {
 
1280
    ...             'fleas': True,
 
1281
    ...             'tail': 'long',
 
1282
    ...             'name': 'rover',
 
1283
    ...             'puppy1': {'name': 'son of rover', 'age': 0.0},
 
1284
    ...             'puppy2': {'name': 'son of rover', 'age': 0.0},
 
1285
    ...             'puppy3': {'name': 'son of rover', 'age': 0.0},
 
1286
    ...         },
 
1287
    ...     },
 
1288
    ...     'cats': {
 
1289
    ...         'cat1': {
 
1290
    ...             'fleas': True,
 
1291
    ...             'tail': 'short',
 
1292
    ...             'name': 'pussy',
 
1293
    ...             'kitten1': {'name': 'son of pussy', 'age': 0.0},
 
1294
    ...             'kitten2': {'name': 'son of pussy', 'age': 0.0},
 
1295
    ...             'kitten3': {'name': 'son of pussy', 'age': 0.0},
 
1296
    ...         },
 
1297
    ...         'cat2': {
 
1298
    ...             'fleas': True,
 
1299
    ...             'tail': 'short',
 
1300
    ...             'name': 'pussy',
 
1301
    ...             'kitten1': {'name': 'son of pussy', 'age': 0.0},
 
1302
    ...             'kitten2': {'name': 'son of pussy', 'age': 0.0},
 
1303
    ...             'kitten3': {'name': 'son of pussy', 'age': 0.0},
 
1304
    ...         },
 
1305
    ...         'cat3': {
 
1306
    ...             'fleas': True,
 
1307
    ...             'tail': 'short',
 
1308
    ...             'name': 'pussy',
 
1309
    ...             'kitten1': {'name': 'son of pussy', 'age': 0.0},
 
1310
    ...             'kitten2': {'name': 'son of pussy', 'age': 0.0},
 
1311
    ...             'kitten3': {'name': 'son of pussy', 'age': 0.0},
 
1312
    ...         },
 
1313
    ...     },
 
1314
    ... }
 
1315
    1
 
1316
    >>> repeater = ConfigObj(repeated_3, configspec=repeated_1)
 
1317
    >>> repeater.validate(val)
 
1318
    1
 
1319
    >>> repeater == {
 
1320
    ...     'cats': {
 
1321
    ...         'cat1': {'fleas': True, 'tail': 'short', 'name': 'pussy'},
 
1322
    ...         'cat2': {'fleas': True, 'tail': 'short', 'name': 'pussy'},
 
1323
    ...         'cat3': {'fleas': True, 'tail': 'short', 'name': 'pussy'},
 
1324
    ...     },
 
1325
    ...     'dogs': {
 
1326
    ...         'dog1': {'fleas': True, 'tail': 'long', 'name': 'rover'},
 
1327
    ...         'dog2': {'fleas': True, 'tail': 'long', 'name': 'rover'},
 
1328
    ...         'dog3': {'fleas': True, 'tail': 'long', 'name': 'rover'},
 
1329
    ...     },
 
1330
    ... }
 
1331
    1
 
1332
    >>> repeater = ConfigObj(configspec=repeated_4)
 
1333
    >>> repeater['Michael'] = {}
 
1334
    >>> repeater.validate(val)
 
1335
    1
 
1336
    >>> repeater == {
 
1337
    ...     'Michael': {'age': 0.0, 'name': 'Michael', 'sex': 'm'},
 
1338
    ... }
 
1339
    1
 
1340
    >>> repeater = ConfigObj(repeated_3, configspec=repeated_5)
 
1341
    >>> repeater == {
 
1342
    ...     'dogs': {'dog1': {}, 'dog2': {}, 'dog3': {}},
 
1343
    ...     'cats': {'cat1': {}, 'cat2': {}, 'cat3': {}},
 
1344
    ... }
 
1345
    1
 
1346
    >>> repeater.validate(val)
 
1347
    1
 
1348
    >>> repeater == {
 
1349
    ...     'dogs': {'dog1': {}, 'dog2': {}, 'dog3': {}},
 
1350
    ...     'cats': {
 
1351
    ...         'cat1': {
 
1352
    ...             'fleas': True,
 
1353
    ...             'tail': 'short',
 
1354
    ...             'name': 'pussy',
 
1355
    ...             'description': {
 
1356
    ...                 'weight': 6.0,
 
1357
    ...                 'height': 3.2999999999999998,
 
1358
    ...                 'coat': {'fur': 'black', 'condition': 5},
 
1359
    ...             },
 
1360
    ...         },
 
1361
    ...         'cat2': {
 
1362
    ...             'fleas': True,
 
1363
    ...             'tail': 'short',
 
1364
    ...             'name': 'pussy',
 
1365
    ...             'description': {
 
1366
    ...                 'weight': 6.0,
 
1367
    ...                 'height': 3.2999999999999998,
 
1368
    ...                 'coat': {'fur': 'black', 'condition': 5},
 
1369
    ...             },
 
1370
    ...         },
 
1371
    ...         'cat3': {
 
1372
    ...             'fleas': True,
 
1373
    ...             'tail': 'short',
 
1374
    ...             'name': 'pussy',
 
1375
    ...             'description': {
 
1376
    ...                 'weight': 6.0,
 
1377
    ...                 'height': 3.2999999999999998,
 
1378
    ...                 'coat': {'fur': 'black', 'condition': 5},
 
1379
    ...             },
 
1380
    ...         },
 
1381
    ...     },
 
1382
    ... }
 
1383
    1
 
1384
    
 
1385
    Test that interpolation is preserved for validated string values.
 
1386
    Also check that interpolation works in configspecs.
 
1387
    >>> t = ConfigObj(configspec=['test = string'])
 
1388
    >>> t['DEFAULT'] = {}
 
1389
    >>> t['DEFAULT']['def_test'] = 'a'
 
1390
    >>> t['test'] = '%(def_test)s'
 
1391
    >>> t['test']
 
1392
    'a'
 
1393
    >>> v = Validator()
 
1394
    >>> t.validate(v)
 
1395
    1
 
1396
    >>> t.interpolation = False
 
1397
    >>> t
 
1398
    ConfigObj({'test': '%(def_test)s', 'DEFAULT': {'def_test': 'a'}})
 
1399
    >>> specs = [
 
1400
    ...    'interpolated string  = string(default="fuzzy-%(man)s")',
 
1401
    ...    '[DEFAULT]',
 
1402
    ...    'man = wuzzy',
 
1403
    ...    ]
 
1404
    >>> c = ConfigObj(configspec=specs)
 
1405
    >>> c.validate(v)
 
1406
    1
 
1407
    >>> c['interpolated string']
 
1408
    'fuzzy-wuzzy'
 
1409
 
 
1410
    Test SimpleVal
 
1411
    >>> val = SimpleVal()
 
1412
    >>> config = '''
 
1413
    ... test1=40
 
1414
    ... test2=hello
 
1415
    ... test3=3
 
1416
    ... test4=5.0
 
1417
    ... [section]
 
1418
    ... test1=40
 
1419
    ... test2=hello
 
1420
    ... test3=3
 
1421
    ... test4=5.0
 
1422
    ...     [[sub section]]
 
1423
    ...     test1=40
 
1424
    ...     test2=hello
 
1425
    ...     test3=3
 
1426
    ...     test4=5.0
 
1427
    ... '''.split('\\n')
 
1428
    >>> configspec = '''
 
1429
    ... test1=''
 
1430
    ... test2=''
 
1431
    ... test3=''
 
1432
    ... test4=''
 
1433
    ... [section]
 
1434
    ... test1=''
 
1435
    ... test2=''
 
1436
    ... test3=''
 
1437
    ... test4=''
 
1438
    ...     [[sub section]]
 
1439
    ...     test1=''
 
1440
    ...     test2=''
 
1441
    ...     test3=''
 
1442
    ...     test4=''
 
1443
    ... '''.split('\\n')
 
1444
    >>> o = ConfigObj(config, configspec=configspec)
 
1445
    >>> o.validate(val)
 
1446
    1
 
1447
    >>> o = ConfigObj(configspec=configspec)
 
1448
    >>> o.validate(val)
 
1449
    0
 
1450
    
 
1451
    Test Flatten Errors
 
1452
    >>> vtor = Validator()
 
1453
    >>> my_ini = '''
 
1454
    ...     option1 = True
 
1455
    ...     [section1]
 
1456
    ...     option1 = True
 
1457
    ...     [section2]
 
1458
    ...     another_option = Probably
 
1459
    ...     [section3]
 
1460
    ...     another_option = True
 
1461
    ...     [[section3b]]
 
1462
    ...     value = 3
 
1463
    ...     value2 = a
 
1464
    ...     value3 = 11
 
1465
    ...     '''
 
1466
    >>> my_cfg = '''
 
1467
    ...     option1 = boolean()
 
1468
    ...     option2 = boolean()
 
1469
    ...     option3 = boolean(default=Bad_value)
 
1470
    ...     [section1]
 
1471
    ...     option1 = boolean()
 
1472
    ...     option2 = boolean()
 
1473
    ...     option3 = boolean(default=Bad_value)
 
1474
    ...     [section2]
 
1475
    ...     another_option = boolean()
 
1476
    ...     [section3]
 
1477
    ...     another_option = boolean()
 
1478
    ...     [[section3b]]
 
1479
    ...     value = integer
 
1480
    ...     value2 = integer
 
1481
    ...     value3 = integer(0, 10)
 
1482
    ...         [[[section3b-sub]]]
 
1483
    ...         value = string
 
1484
    ...     [section4]
 
1485
    ...     another_option = boolean()
 
1486
    ...     '''
 
1487
    >>> cs = my_cfg.split('\\n')
 
1488
    >>> ini = my_ini.split('\\n')
 
1489
    >>> cfg = ConfigObj(ini, configspec=cs)
 
1490
    >>> res = cfg.validate(vtor, preserve_errors=True)
 
1491
    >>> errors = []
 
1492
    >>> for entry in flatten_errors(cfg, res):
 
1493
    ...     section_list, key, error = entry
 
1494
    ...     section_list.insert(0, '[root]')
 
1495
    ...     if key is not None:
 
1496
    ...         section_list.append(key)
 
1497
    ...     section_string = ', '.join(section_list)
 
1498
    ...     errors.append('%s%s%s' % (section_string, ' = ', error or 'missing'))
 
1499
    >>> errors.sort()
 
1500
    >>> for entry in errors:
 
1501
    ...     print entry
 
1502
    [root], option2 = missing
 
1503
    [root], option3 = the value "Bad_value" is of the wrong type.
 
1504
    [root], section1, option2 = missing
 
1505
    [root], section1, option3 = the value "Bad_value" is of the wrong type.
 
1506
    [root], section2, another_option = the value "Probably" is of the wrong type.
 
1507
    [root], section3, section3b, section3b-sub = missing
 
1508
    [root], section3, section3b, value2 = the value "a" is of the wrong type.
 
1509
    [root], section3, section3b, value3 = the value "11" is too big.
 
1510
    [root], section4 = missing
 
1511
    """
 
1512
 
 
1513
 
 
1514
def _test_errors():
 
1515
    """
 
1516
    Test the error messages and objects, in normal mode and unrepr mode.
 
1517
    >>> bad_syntax = '''
 
1518
    ... key = "value"
 
1519
    ... key2 = "value
 
1520
    ... '''.splitlines()
 
1521
    >>> c = ConfigObj(bad_syntax)
 
1522
    Traceback (most recent call last):
 
1523
    ParseError: Parse error in value at line 3.
 
1524
    >>> c = ConfigObj(bad_syntax, raise_errors=True)
 
1525
    Traceback (most recent call last):
 
1526
    ParseError: Parse error in value at line 3.
 
1527
    >>> c = ConfigObj(bad_syntax, raise_errors=True, unrepr=True)
 
1528
    Traceback (most recent call last):
 
1529
    UnreprError: Parse error in value at line 3.
 
1530
    >>> try:
 
1531
    ...     c = ConfigObj(bad_syntax)
 
1532
    ... except Exception, e:
 
1533
    ...     pass
 
1534
    >>> assert(isinstance(e, ConfigObjError))
 
1535
    >>> print e
 
1536
    Parse error in value at line 3.
 
1537
    >>> len(e.errors) == 1
 
1538
    1
 
1539
    >>> try:
 
1540
    ...     c = ConfigObj(bad_syntax, unrepr=True)
 
1541
    ... except Exception, e:
 
1542
    ...     pass
 
1543
    >>> assert(isinstance(e, ConfigObjError))
 
1544
    >>> print e
 
1545
    Parse error in value at line 3.
 
1546
    >>> len(e.errors) == 1
 
1547
    1
 
1548
    >>> the_error = e.errors[0]
 
1549
    >>> assert(isinstance(the_error, UnreprError))
 
1550
    
 
1551
    >>> multiple_bad_syntax = '''
 
1552
    ... key = "value"
 
1553
    ... key2 = "value
 
1554
    ... key3 = "value2
 
1555
    ... '''.splitlines()
 
1556
    >>> try:
 
1557
    ...     c = ConfigObj(multiple_bad_syntax)
 
1558
    ... except ConfigObjError, e:
 
1559
    ...     str(e)
 
1560
    'Parsing failed with several errors.\\nFirst error at line 3.'
 
1561
    >>> c = ConfigObj(multiple_bad_syntax, raise_errors=True)
 
1562
    Traceback (most recent call last):
 
1563
    ParseError: Parse error in value at line 3.
 
1564
    >>> c = ConfigObj(multiple_bad_syntax, raise_errors=True, unrepr=True)
 
1565
    Traceback (most recent call last):
 
1566
    UnreprError: Parse error in value at line 3.
 
1567
    >>> try:
 
1568
    ...     c = ConfigObj(multiple_bad_syntax)
 
1569
    ... except Exception, e:
 
1570
    ...     pass
 
1571
    >>> assert(isinstance(e, ConfigObjError))
 
1572
    >>> print e
 
1573
    Parsing failed with several errors.
 
1574
    First error at line 3.
 
1575
    >>> len(e.errors) == 2
 
1576
    1
 
1577
    >>> try:
 
1578
    ...     c = ConfigObj(multiple_bad_syntax, unrepr=True)
 
1579
    ... except Exception, e:
 
1580
    ...     pass
 
1581
    >>> assert(isinstance(e, ConfigObjError))
 
1582
    >>> print e
 
1583
    Parsing failed with several errors.
 
1584
    First error at line 3.
 
1585
    >>> len(e.errors) == 2
 
1586
    1
 
1587
    >>> the_error = e.errors[1]
 
1588
    >>> assert(isinstance(the_error, UnreprError))
 
1589
    
 
1590
    >>> unknown_name = '''
 
1591
    ... key = "value"
 
1592
    ... key2 = value
 
1593
    ... '''.splitlines()
 
1594
    >>> c = ConfigObj(unknown_name)
 
1595
    >>> c = ConfigObj(unknown_name, unrepr=True)
 
1596
    Traceback (most recent call last):
 
1597
    UnreprError: Unknown name or type in value at line 3.
 
1598
    >>> c = ConfigObj(unknown_name, raise_errors=True, unrepr=True)
 
1599
    Traceback (most recent call last):
 
1600
    UnreprError: Unknown name or type in value at line 3.
 
1601
    """
 
1602
 
 
1603
 
 
1604
def _test_unrepr_comments():
 
1605
    """
 
1606
    >>> config = '''
 
1607
    ... # initial comments
 
1608
    ... # with two lines
 
1609
    ... key = "value"
 
1610
    ... # section comment
 
1611
    ... [section] # inline section comment
 
1612
    ... # key comment
 
1613
    ... key = "value"
 
1614
    ... # final comment
 
1615
    ... # with two lines
 
1616
    ... '''.splitlines()
 
1617
    >>> c = ConfigObj(config, unrepr=True)
 
1618
    >>> c == { 'key': 'value',
 
1619
    ... 'section': { 'key': 'value'}}
 
1620
    1
 
1621
    >>> c.initial_comment == ['', '# initial comments', '# with two lines']
 
1622
    1
 
1623
    >>> c.comments == {'section': ['# section comment'], 'key': []}
 
1624
    1
 
1625
    >>> c.inline_comments == {'section': '# inline section comment', 'key': ''}
 
1626
    1
 
1627
    >>> c['section'].comments == { 'key': ['# key comment']}
 
1628
    1
 
1629
    >>> c.final_comment == ['# final comment', '# with two lines']
 
1630
    1
 
1631
    """
 
1632
 
 
1633
 
 
1634
def _test_newline_terminated():
 
1635
    """
 
1636
    >>> c = ConfigObj()
 
1637
    >>> c.newlines = '\\n'
 
1638
    >>> c['a'] = 'b'
 
1639
    >>> collector = StringIO()
 
1640
    >>> c.write(collector)
 
1641
    >>> collector.getvalue()
 
1642
    'a = b\\n'
 
1643
    """
 
1644
    
 
1645
    
 
1646
def _test_hash_escaping():
 
1647
    """
 
1648
    >>> c = ConfigObj()
 
1649
    >>> c.newlines = '\\n'
 
1650
    >>> c['#a'] = 'b # something'
 
1651
    >>> collector = StringIO()
 
1652
    >>> c.write(collector)
 
1653
    >>> collector.getvalue()
 
1654
    '"#a" = "b # something"\\n'
 
1655
    
 
1656
    >>> c = ConfigObj()
 
1657
    >>> c.newlines = '\\n'
 
1658
    >>> c['a'] = 'b # something', 'c # something'
 
1659
    >>> collector = StringIO()
 
1660
    >>> c.write(collector)
 
1661
    >>> collector.getvalue()
 
1662
    'a = "b # something", "c # something"\\n'
 
1663
    """
 
1664
 
 
1665
 
 
1666
def _test_lineendings():
 
1667
    """
 
1668
    NOTE: Need to use a real file because this code is only
 
1669
          exercised when reading from the filesystem.
 
1670
          
 
1671
    >>> h = open('temp', 'wb')
 
1672
    >>> h.write('\\r\\n')
 
1673
    >>> h.close()
 
1674
    >>> c = ConfigObj('temp')
 
1675
    >>> c.newlines
 
1676
    '\\r\\n'
 
1677
    >>> h = open('temp', 'wb')
 
1678
    >>> h.write('\\n')
 
1679
    >>> h.close()
 
1680
    >>> c = ConfigObj('temp')
 
1681
    >>> c.newlines
 
1682
    '\\n'
 
1683
    >>> os.remove('temp')
 
1684
    """
 
1685
 
 
1686
 
 
1687
def _test_validate_with_copy_and_many():
 
1688
    """
 
1689
    >>> spec = '''
 
1690
    ... [section]
 
1691
    ... [[__many__]]
 
1692
    ... value = string(default='nothing')
 
1693
    ... '''
 
1694
    >>> config = '''
 
1695
    ... [section]
 
1696
    ... [[something]]
 
1697
    ... '''
 
1698
    >>> c = ConfigObj(StringIO(config), configspec=StringIO(spec))
 
1699
    >>> v = Validator()
 
1700
    >>> r = c.validate(v, copy=True)
 
1701
    >>> c['section']['something']['value']
 
1702
    'nothing'
 
1703
    """
 
1704
    
 
1705
def _test_configspec_with_hash():
 
1706
    """
 
1707
    >>> spec = ['stuff = string(default="#ff00dd")']
 
1708
    >>> c = ConfigObj(spec, _inspec=True)
 
1709
    >>> c['stuff']
 
1710
    'string(default="#ff00dd")'
 
1711
    >>> c = ConfigObj(configspec=spec)
 
1712
    >>> v = Validator()
 
1713
    >>> c.validate(v)
 
1714
    1
 
1715
    >>> c['stuff']
 
1716
    '#ff00dd'
 
1717
    
 
1718
    
 
1719
    >>> spec = ['stuff = string(default="fish") # wooble']
 
1720
    >>> c = ConfigObj(spec, _inspec=True)
 
1721
    >>> c['stuff']
 
1722
    'string(default="fish") # wooble'
 
1723
    """
 
1724
 
 
1725
def _test_many_check():
 
1726
    """
 
1727
    >>> spec = ['__many__ = integer()']
 
1728
    >>> config = ['a = 6', 'b = 7']
 
1729
    >>> c = ConfigObj(config, configspec=spec)
 
1730
    >>> v = Validator()
 
1731
    >>> c.validate(v)
 
1732
    1
 
1733
    >>> type(c['a'])
 
1734
    <type 'int'>
 
1735
    >>> type(c['b'])
 
1736
    <type 'int'>
 
1737
    
 
1738
    
 
1739
    >>> spec = ['[name]', '__many__ = integer()']
 
1740
    >>> config = ['[name]', 'a = 6', 'b = 7']
 
1741
    >>> c = ConfigObj(config, configspec=spec)
 
1742
    >>> v = Validator()
 
1743
    >>> c.validate(v)
 
1744
    1
 
1745
    >>> type(c['name']['a'])
 
1746
    <type 'int'>
 
1747
    >>> type(c['name']['b'])
 
1748
    <type 'int'>
 
1749
    
 
1750
    
 
1751
    >>> spec = ['[__many__]', '__many__ = integer()']
 
1752
    >>> config = ['[name]', 'hello = 7', '[thing]', 'fish = 0']
 
1753
    >>> c = ConfigObj(config, configspec=spec)
 
1754
    >>> v = Validator()
 
1755
    >>> c.validate(v)
 
1756
    1
 
1757
    >>> type(c['name']['hello'])
 
1758
    <type 'int'>
 
1759
    >>> type(c['thing']['fish'])
 
1760
    <type 'int'>
 
1761
    
 
1762
    
 
1763
    >>> spec = '''
 
1764
    ... ___many___ = integer
 
1765
    ... [__many__]
 
1766
    ... ___many___ = boolean
 
1767
    ... [[__many__]]
 
1768
    ... __many__ = float
 
1769
    ... '''.splitlines()
 
1770
    >>> config = '''
 
1771
    ... fish = 8
 
1772
    ... buggle = 4
 
1773
    ... [hi]
 
1774
    ... one = true
 
1775
    ... two = false
 
1776
    ... [[bye]]
 
1777
    ... odd = 3
 
1778
    ... whoops = 9.0
 
1779
    ... [bye]
 
1780
    ... one = true
 
1781
    ... two = true
 
1782
    ... [[lots]]
 
1783
    ... odd = 3
 
1784
    ... whoops = 9.0
 
1785
    ... '''.splitlines()
 
1786
    >>> c = ConfigObj(config, configspec=spec)
 
1787
    >>> v = Validator()
 
1788
    >>> c.validate(v)
 
1789
    1
 
1790
    >>> type(c['fish'])
 
1791
    <type 'int'>
 
1792
    >>> type(c['buggle'])
 
1793
    <type 'int'>
 
1794
    >>> c['hi']['one']
 
1795
    1
 
1796
    >>> c['hi']['two']
 
1797
    0
 
1798
    >>> type(c['hi']['bye']['odd'])
 
1799
    <type 'float'>
 
1800
    >>> type(c['hi']['bye']['whoops'])
 
1801
    <type 'float'>
 
1802
    >>> c['bye']['one']
 
1803
    1
 
1804
    >>> c['bye']['two']
 
1805
    1
 
1806
    >>> type(c['bye']['lots']['odd'])
 
1807
    <type 'float'>
 
1808
    >>> type(c['bye']['lots']['whoops'])
 
1809
    <type 'float'>
 
1810
    
 
1811
    
 
1812
    >>> spec = ['___many___ = integer()']
 
1813
    >>> config = ['a = 6', 'b = 7']
 
1814
    >>> c = ConfigObj(config, configspec=spec)
 
1815
    >>> v = Validator()
 
1816
    >>> c.validate(v)
 
1817
    1
 
1818
    >>> type(c['a'])
 
1819
    <type 'int'>
 
1820
    >>> type(c['b'])
 
1821
    <type 'int'>
 
1822
 
 
1823
    
 
1824
    >>> spec = '''
 
1825
    ... [__many__]
 
1826
    ... [[__many__]]
 
1827
    ... __many__ = float
 
1828
    ... '''.splitlines()
 
1829
    >>> config = '''
 
1830
    ... [hi]
 
1831
    ... [[bye]]
 
1832
    ... odd = 3
 
1833
    ... whoops = 9.0
 
1834
    ... [bye]
 
1835
    ... [[lots]]
 
1836
    ... odd = 3
 
1837
    ... whoops = 9.0
 
1838
    ... '''.splitlines()
 
1839
    >>> c = ConfigObj(config, configspec=spec)
 
1840
    >>> v = Validator()
 
1841
    >>> c.validate(v)
 
1842
    1
 
1843
    >>> type(c['hi']['bye']['odd'])
 
1844
    <type 'float'>
 
1845
    >>> type(c['hi']['bye']['whoops'])
 
1846
    <type 'float'>
 
1847
    >>> type(c['bye']['lots']['odd'])
 
1848
    <type 'float'>
 
1849
    >>> type(c['bye']['lots']['whoops'])
 
1850
    <type 'float'>
 
1851
    
 
1852
    >>> s = ['[dog]', '[[cow]]', 'something = boolean', '[[__many__]]', 
 
1853
    ...      'fish = integer']
 
1854
    >>> c = ['[dog]', '[[cow]]', 'something = true', '[[ob]]', 
 
1855
    ...      'fish = 3', '[[bo]]', 'fish = 6']
 
1856
    >>> ini = ConfigObj(c, configspec=s)
 
1857
    >>> v = Validator()
 
1858
    >>> ini.validate(v)
 
1859
    1
 
1860
    >>> ini['dog']['cow']['something']
 
1861
    1
 
1862
    >>> ini['dog']['ob']['fish']
 
1863
    3
 
1864
    >>> ini['dog']['bo']['fish']
 
1865
    6
 
1866
    
 
1867
    
 
1868
    >>> s = ['[cow]', 'something = boolean', '[__many__]', 
 
1869
    ...      'fish = integer']
 
1870
    >>> c = ['[cow]', 'something = true', '[ob]', 
 
1871
    ...      'fish = 3', '[bo]', 'fish = 6']
 
1872
    >>> ini = ConfigObj(c, configspec=s)
 
1873
    >>> v = Validator()
 
1874
    >>> ini.validate(v)
 
1875
    1
 
1876
    >>> ini['cow']['something']
 
1877
    1
 
1878
    >>> ini['ob']['fish']
 
1879
    3
 
1880
    >>> ini['bo']['fish']
 
1881
    6
 
1882
    """
 
1883
 
 
1884
    
 
1885
def _unexpected_validation_errors():
 
1886
    """
 
1887
    Although the input is nonsensical we should not crash but correctly 
 
1888
    report the failure to validate
 
1889
    
 
1890
    # section specified, got scalar
 
1891
    >>> from validate import ValidateError 
 
1892
    >>> s = ['[cow]', 'something = boolean']
 
1893
    >>> c = ['cow = true']
 
1894
    >>> ini = ConfigObj(c, configspec=s)
 
1895
    >>> v = Validator()
 
1896
    >>> ini.validate(v)
 
1897
    0
 
1898
 
 
1899
    >>> ini = ConfigObj(c, configspec=s)
 
1900
    >>> res = ini.validate(v, preserve_errors=True)
 
1901
    >>> check = flatten_errors(ini, res)
 
1902
    >>> for entry in check:
 
1903
    ...     isinstance(entry[2], ValidateError)
 
1904
    ...     print str(entry[2])
 
1905
    True
 
1906
    Section 'cow' was provided as a single value
 
1907
    
 
1908
 
 
1909
    # scalar specified, got section
 
1910
    >>> s = ['something = boolean']
 
1911
    >>> c = ['[something]', 'cow = true']
 
1912
    >>> ini = ConfigObj(c, configspec=s)
 
1913
    >>> v = Validator()
 
1914
    >>> ini.validate(v)
 
1915
    0
 
1916
    
 
1917
    >>> ini = ConfigObj(c, configspec=s)
 
1918
    >>> res = ini.validate(v, preserve_errors=True)
 
1919
    >>> check = flatten_errors(ini, res)
 
1920
    >>> for entry in check:
 
1921
    ...     isinstance(entry[2], ValidateError)
 
1922
    ...     print str(entry[2])
 
1923
    True
 
1924
    Value 'something' was provided as a section
 
1925
    
 
1926
    # unexpected section
 
1927
    >>> s = []
 
1928
    >>> c = ['[cow]', 'dog = true']
 
1929
    >>> ini = ConfigObj(c, configspec=s)
 
1930
    >>> v = Validator()
 
1931
    >>> ini.validate(v)
 
1932
    1
 
1933
    
 
1934
    
 
1935
    >>> s = ['[cow]', 'dog = boolean']
 
1936
    >>> c = ['[cow]', 'dog = true']
 
1937
    >>> ini = ConfigObj(c, configspec=s)
 
1938
    >>> v = Validator()
 
1939
    >>> ini.validate(v, preserve_errors=True)
 
1940
    1
 
1941
    """
 
1942
    
 
1943
def _test_pickle():
 
1944
    """
 
1945
    >>> import pickle
 
1946
    >>> s = ['[cow]', 'dog = boolean']
 
1947
    >>> c = ['[cow]', 'dog = true']
 
1948
    >>> ini = ConfigObj(c, configspec=s)
 
1949
    >>> v = Validator()
 
1950
    >>> string = pickle.dumps(ini)
 
1951
    >>> new = pickle.loads(string)
 
1952
    >>> new.validate(v)
 
1953
    1
 
1954
    """
 
1955
 
 
1956
def _test_as_list():
 
1957
    """
 
1958
    >>> a = ConfigObj()
 
1959
    >>> a['a'] = 1
 
1960
    >>> a.as_list('a')
 
1961
    [1]
 
1962
    >>> a['a'] = (1,)
 
1963
    >>> a.as_list('a')
 
1964
    [1]
 
1965
    >>> a['a'] = [1]
 
1966
    >>> a.as_list('a')
 
1967
    [1]
 
1968
    """
 
1969
 
 
1970
def _test_list_interpolation():
 
1971
    """
 
1972
    >>> c = ConfigObj()
 
1973
    >>> c['x'] = 'foo'
 
1974
    >>> c['list'] = ['%(x)s', 3]
 
1975
    >>> c['list']
 
1976
    ['foo', 3]
 
1977
    """
 
1978
 
 
1979
def _test_extra_values():
 
1980
    """
 
1981
    >>> spec = ['[section]']
 
1982
    >>> infile = ['bar = 3', '[something]', 'foo = fish', '[section]', 'foo=boo']
 
1983
    >>> c = ConfigObj(infile, configspec=spec)
 
1984
    >>> c.extra_values
 
1985
    []
 
1986
    >>> c.extra_values = ['bar', 'gosh', 'what']
 
1987
    >>> c.validate(Validator())
 
1988
    1
 
1989
    >>> c.extra_values
 
1990
    ['bar', 'something']
 
1991
    >>> c['section'].extra_values
 
1992
    ['foo']
 
1993
    >>> c['something'].extra_values
 
1994
    []
 
1995
    """
 
1996
 
 
1997
def _test_reset_and_clear_more():
 
1998
    """
 
1999
    >>> c = ConfigObj()
 
2000
    >>> c.extra_values = ['foo']
 
2001
    >>> c.defaults = ['bar']
 
2002
    >>> c.default_values = {'bar': 'baz'}
 
2003
    >>> c.clear()
 
2004
    >>> c.defaults
 
2005
    []
 
2006
    >>> c.extra_values
 
2007
    []
 
2008
    >>> c.default_values
 
2009
    {'bar': 'baz'}
 
2010
    >>> c.extra_values = ['foo']
 
2011
    >>> c.defaults = ['bar']
 
2012
    >>> c.reset()
 
2013
    >>> c.defaults
 
2014
    []
 
2015
    >>> c.extra_values
 
2016
    []
 
2017
    >>> c.default_values
 
2018
    {}
 
2019
    """
 
2020
 
 
2021
def _test_invalid_lists():
 
2022
    """
 
2023
    >>> v = ['string = val, val2, , val3']
 
2024
    >>> c = ConfigObj(v)
 
2025
    Traceback (most recent call last):
 
2026
    ParseError: Parse error in value at line 1.
 
2027
    >>> v = ['string = val, val2,, val3']
 
2028
    >>> c = ConfigObj(v)
 
2029
    Traceback (most recent call last):
 
2030
    ParseError: Parse error in value at line 1.
 
2031
    >>> v = ['string = val, val2,,']
 
2032
    >>> c = ConfigObj(v)
 
2033
    Traceback (most recent call last):
 
2034
    ParseError: Parse error in value at line 1.
 
2035
    >>> v = ['string = val, ,']
 
2036
    >>> c = ConfigObj(v)
 
2037
    Traceback (most recent call last):
 
2038
    ParseError: Parse error in value at line 1.
 
2039
    >>> v = ['string = val, ,  ']
 
2040
    >>> c = ConfigObj(v)
 
2041
    Traceback (most recent call last):
 
2042
    ParseError: Parse error in value at line 1.
 
2043
    >>> v = ['string = ,,']
 
2044
    >>> c = ConfigObj(v)
 
2045
    Traceback (most recent call last):
 
2046
    ParseError: Parse error in value at line 1.
 
2047
    >>> v = ['string = ,, ']
 
2048
    >>> c = ConfigObj(v)
 
2049
    Traceback (most recent call last):
 
2050
    ParseError: Parse error in value at line 1.
 
2051
    >>> v = ['string = ,foo']
 
2052
    >>> c = ConfigObj(v)
 
2053
    Traceback (most recent call last):
 
2054
    ParseError: Parse error in value at line 1.
 
2055
    >>> v = ['string = foo, ']
 
2056
    >>> c = ConfigObj(v)
 
2057
    >>> c['string']
 
2058
    ['foo']
 
2059
    >>> v = ['string = foo, "']
 
2060
    >>> c = ConfigObj(v)
 
2061
    Traceback (most recent call last):
 
2062
    ParseError: Parse error in value at line 1.
 
2063
    """
 
2064
 
 
2065
def _test_validation_with_preserve_errors():
 
2066
    """
 
2067
    >>> v = Validator()
 
2068
    >>> spec = ['[section]', 'foo = integer']
 
2069
    >>> c = ConfigObj(configspec=spec)
 
2070
    >>> c.validate(v, preserve_errors=True)
 
2071
    {'section': False}
 
2072
    >>> c = ConfigObj(['[section]'], configspec=spec)
 
2073
    >>> c.validate(v)
 
2074
    False
 
2075
    >>> c.validate(v, preserve_errors=True)
 
2076
    {'section': {'foo': False}}
 
2077
    """
 
2078
 
 
2079
 
 
2080
# test _created on Section
 
2081
 
 
2082
# TODO: Test BOM handling
 
2083
# TODO: Test error code for badly built multiline values
 
2084
# TODO: Test handling of StringIO
 
2085
# TODO: Test interpolation with writing
 
2086
 
 
2087
 
 
2088
if __name__ == '__main__':
 
2089
    # run the code tests in doctest format
 
2090
    #
 
2091
    testconfig1 = """\
 
2092
    key1= val    # comment 1
 
2093
    key2= val    # comment 2
 
2094
    # comment 3
 
2095
    [lev1a]     # comment 4
 
2096
    key1= val    # comment 5
 
2097
    key2= val    # comment 6
 
2098
    # comment 7
 
2099
    [lev1b]    # comment 8
 
2100
    key1= val    # comment 9
 
2101
    key2= val    # comment 10
 
2102
    # comment 11
 
2103
        [[lev2ba]]    # comment 12
 
2104
        key1= val    # comment 13
 
2105
        # comment 14
 
2106
        [[lev2bb]]    # comment 15
 
2107
        key1= val    # comment 16
 
2108
    # comment 17
 
2109
    [lev1c]    # comment 18
 
2110
    # comment 19
 
2111
        [[lev2c]]    # comment 20
 
2112
        # comment 21
 
2113
            [[[lev3c]]]    # comment 22
 
2114
            key1 = val    # comment 23"""
 
2115
    #
 
2116
    testconfig2 = """\
 
2117
                        key1 = 'val1'
 
2118
                        key2 =   "val2"
 
2119
                        key3 = val3
 
2120
                        ["section 1"] # comment
 
2121
                        keys11 = val1
 
2122
                        keys12 = val2
 
2123
                        keys13 = val3
 
2124
                        [section 2]
 
2125
                        keys21 = val1
 
2126
                        keys22 = val2
 
2127
                        keys23 = val3
 
2128
                        
 
2129
                            [['section 2 sub 1']]
 
2130
                            fish = 3
 
2131
    """
 
2132
    #
 
2133
    testconfig6 = '''
 
2134
    name1 = """ a single line value """ # comment
 
2135
    name2 = \''' another single line value \''' # comment
 
2136
    name3 = """ a single line value """
 
2137
    name4 = \''' another single line value \'''
 
2138
        [ "multi section" ]
 
2139
        name1 = """
 
2140
        Well, this is a
 
2141
        multiline value
 
2142
        """
 
2143
        name2 = \'''
 
2144
        Well, this is a
 
2145
        multiline value
 
2146
        \'''
 
2147
        name3 = """
 
2148
        Well, this is a
 
2149
        multiline value
 
2150
        """     # a comment
 
2151
        name4 = \'''
 
2152
        Well, this is a
 
2153
        multiline value
 
2154
        \'''  # I guess this is a comment too
 
2155
    '''
 
2156
    #
 
2157
    # these cannot be put among the doctests, because the doctest module
 
2158
    # does a string.expandtabs() on all of them, sigh
 
2159
    oneTabCfg = ['[sect]', '\t[[sect]]', '\t\tfoo = bar']
 
2160
    twoTabsCfg = ['[sect]', '\t\t[[sect]]', '\t\t\t\tfoo = bar']
 
2161
    tabsAndSpacesCfg = ['[sect]', '\t \t [[sect]]', '\t \t \t \t foo = bar']
 
2162
    #
 
2163
    import doctest
 
2164
    m = sys.modules.get('__main__')
 
2165
    globs = m.__dict__.copy()
 
2166
    a = ConfigObj(testconfig1.split('\n'), raise_errors=True)
 
2167
    b = ConfigObj(testconfig2.split('\n'), raise_errors=True)
 
2168
    i = ConfigObj(testconfig6.split('\n'), raise_errors=True)
 
2169
    globs.update({'INTP_VER': INTP_VER, 'a': a, 'b': b, 'i': i,
 
2170
        'oneTabCfg': oneTabCfg, 'twoTabsCfg': twoTabsCfg,
 
2171
        'tabsAndSpacesCfg': tabsAndSpacesCfg})
 
2172
    doctest.testmod(m, globs=globs)
 
2173
    
 
2174
    import configobj
 
2175
    failures, tests = doctest.testmod(configobj, globs=globs)
 
2176
    print "Ran %i tests. Failures: %i" % (tests, failures)
 
2177
    if failures:
 
2178
        sys.exit(1)
 
2179
 
 
2180
 
 
2181
# Man alive I prefer unittest ;-)