~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/util/configobj/configobj.py

  • Committer: Martin Pool
  • Date: 2005-05-10 06:07:16 UTC
  • Revision ID: mbp@sourcefrog.net-20050510060716-0f939ce3ddea5d15
- New command update-stat-cache for testing
- work-cache always stored with unix newlines and in ascii

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# configobj.py
2
 
# A config file reader/writer that supports nested sections in config files.
3
 
# Copyright (C) 2005-2008 Michael Foord, Nicola Larosa
4
 
# E-mail: fuzzyman AT voidspace DOT org DOT uk
5
 
#         nico AT tekNico DOT net
6
 
 
7
 
# ConfigObj 4
8
 
# http://www.voidspace.org.uk/python/configobj.html
9
 
 
10
 
# Released subject to the BSD License
11
 
# Please see http://www.voidspace.org.uk/python/license.shtml
12
 
 
13
 
# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
14
 
# For information about bugfixes, updates and support, please join the
15
 
# ConfigObj mailing list:
16
 
# http://lists.sourceforge.net/lists/listinfo/configobj-develop
17
 
# Comments, suggestions and bug reports welcome.
18
 
 
19
 
from __future__ import generators
20
 
 
21
 
import sys
22
 
INTP_VER = sys.version_info[:2]
23
 
if INTP_VER < (2, 2):
24
 
    raise RuntimeError("Python v.2.2 or later needed")
25
 
 
26
 
import os, re
27
 
compiler = None
28
 
# Bzr modification: Disabled import of 'compiler' module
29
 
# bzr doesn't use the 'unrepr' feature of configobj, so importing compiler just
30
 
# wastes several milliseconds on every single bzr invocation.
31
 
#   -- Andrew Bennetts, 2008-10-14
32
 
#try:
33
 
#    import compiler
34
 
#except ImportError:
35
 
#    # for IronPython
36
 
#    pass
37
 
from types import StringTypes
38
 
from warnings import warn
39
 
try:
40
 
    from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE
41
 
except ImportError:
42
 
    # Python 2.2 does not have these
43
 
    # UTF-8
44
 
    BOM_UTF8 = '\xef\xbb\xbf'
45
 
    # UTF-16, little endian
46
 
    BOM_UTF16_LE = '\xff\xfe'
47
 
    # UTF-16, big endian
48
 
    BOM_UTF16_BE = '\xfe\xff'
49
 
    if sys.byteorder == 'little':
50
 
        # UTF-16, native endianness
51
 
        BOM_UTF16 = BOM_UTF16_LE
52
 
    else:
53
 
        # UTF-16, native endianness
54
 
        BOM_UTF16 = BOM_UTF16_BE
55
 
 
56
 
# A dictionary mapping BOM to
57
 
# the encoding to decode with, and what to set the
58
 
# encoding attribute to.
59
 
BOMS = {
60
 
    BOM_UTF8: ('utf_8', None),
61
 
    BOM_UTF16_BE: ('utf16_be', 'utf_16'),
62
 
    BOM_UTF16_LE: ('utf16_le', 'utf_16'),
63
 
    BOM_UTF16: ('utf_16', 'utf_16'),
64
 
    }
65
 
# All legal variants of the BOM codecs.
66
 
# TODO: the list of aliases is not meant to be exhaustive, is there a
67
 
#   better way ?
68
 
BOM_LIST = {
69
 
    'utf_16': 'utf_16',
70
 
    'u16': 'utf_16',
71
 
    'utf16': 'utf_16',
72
 
    'utf-16': 'utf_16',
73
 
    'utf16_be': 'utf16_be',
74
 
    'utf_16_be': 'utf16_be',
75
 
    'utf-16be': 'utf16_be',
76
 
    'utf16_le': 'utf16_le',
77
 
    'utf_16_le': 'utf16_le',
78
 
    'utf-16le': 'utf16_le',
79
 
    'utf_8': 'utf_8',
80
 
    'u8': 'utf_8',
81
 
    'utf': 'utf_8',
82
 
    'utf8': 'utf_8',
83
 
    'utf-8': 'utf_8',
84
 
    }
85
 
 
86
 
# Map of encodings to the BOM to write.
87
 
BOM_SET = {
88
 
    'utf_8': BOM_UTF8,
89
 
    'utf_16': BOM_UTF16,
90
 
    'utf16_be': BOM_UTF16_BE,
91
 
    'utf16_le': BOM_UTF16_LE,
92
 
    None: BOM_UTF8
93
 
    }
94
 
 
95
 
 
96
 
def match_utf8(encoding):
97
 
    return BOM_LIST.get(encoding.lower()) == 'utf_8'
98
 
 
99
 
 
100
 
# Quote strings used for writing values
101
 
squot = "'%s'"
102
 
dquot = '"%s"'
103
 
noquot = "%s"
104
 
wspace_plus = ' \r\t\n\v\t\'"'
105
 
tsquot = '"""%s"""'
106
 
tdquot = "'''%s'''"
107
 
 
108
 
try:
109
 
    enumerate
110
 
except NameError:
111
 
    def enumerate(obj):
112
 
        """enumerate for Python 2.2."""
113
 
        i = -1
114
 
        for item in obj:
115
 
            i += 1
116
 
            yield i, item
117
 
 
118
 
try:
119
 
    True, False
120
 
except NameError:
121
 
    True, False = 1, 0
122
 
 
123
 
 
124
 
__version__ = '4.5.2'
125
 
 
126
 
__revision__ = '$Id: configobj.py 156 2006-01-31 14:57:08Z fuzzyman $'
127
 
 
128
 
__docformat__ = "restructuredtext en"
129
 
 
130
 
__all__ = (
131
 
    '__version__',
132
 
    'DEFAULT_INDENT_TYPE',
133
 
    'DEFAULT_INTERPOLATION',
134
 
    'ConfigObjError',
135
 
    'NestingError',
136
 
    'ParseError',
137
 
    'DuplicateError',
138
 
    'ConfigspecError',
139
 
    'ConfigObj',
140
 
    'SimpleVal',
141
 
    'InterpolationError',
142
 
    'InterpolationLoopError',
143
 
    'MissingInterpolationOption',
144
 
    'RepeatSectionError',
145
 
    'ReloadError',
146
 
    'UnreprError',
147
 
    'UnknownType',
148
 
    '__docformat__',
149
 
    'flatten_errors',
150
 
)
151
 
 
152
 
DEFAULT_INTERPOLATION = 'configparser'
153
 
DEFAULT_INDENT_TYPE = '    '
154
 
MAX_INTERPOL_DEPTH = 10
155
 
 
156
 
OPTION_DEFAULTS = {
157
 
    'interpolation': True,
158
 
    'raise_errors': False,
159
 
    'list_values': True,
160
 
    'create_empty': False,
161
 
    'file_error': False,
162
 
    'configspec': None,
163
 
    'stringify': True,
164
 
    # option may be set to one of ('', ' ', '\t')
165
 
    'indent_type': None,
166
 
    'encoding': None,
167
 
    'default_encoding': None,
168
 
    'unrepr': False,
169
 
    'write_empty_values': False,
170
 
}
171
 
 
172
 
 
173
 
 
174
 
def getObj(s):
175
 
    s = "a=" + s
176
 
    if compiler is None:
177
 
        raise ImportError('compiler module not available')
178
 
    p = compiler.parse(s)
179
 
    return p.getChildren()[1].getChildren()[0].getChildren()[1]
180
 
 
181
 
 
182
 
class UnknownType(Exception):
183
 
    pass
184
 
 
185
 
 
186
 
class Builder(object):
187
 
    
188
 
    def build(self, o):
189
 
        m = getattr(self, 'build_' + o.__class__.__name__, None)
190
 
        if m is None:
191
 
            raise UnknownType(o.__class__.__name__)
192
 
        return m(o)
193
 
    
194
 
    def build_List(self, o):
195
 
        return map(self.build, o.getChildren())
196
 
    
197
 
    def build_Const(self, o):
198
 
        return o.value
199
 
    
200
 
    def build_Dict(self, o):
201
 
        d = {}
202
 
        i = iter(map(self.build, o.getChildren()))
203
 
        for el in i:
204
 
            d[el] = i.next()
205
 
        return d
206
 
    
207
 
    def build_Tuple(self, o):
208
 
        return tuple(self.build_List(o))
209
 
    
210
 
    def build_Name(self, o):
211
 
        if o.name == 'None':
212
 
            return None
213
 
        if o.name == 'True':
214
 
            return True
215
 
        if o.name == 'False':
216
 
            return False
217
 
        
218
 
        # An undefined Name
219
 
        raise UnknownType('Undefined Name')
220
 
    
221
 
    def build_Add(self, o):
222
 
        real, imag = map(self.build_Const, o.getChildren())
223
 
        try:
224
 
            real = float(real)
225
 
        except TypeError:
226
 
            raise UnknownType('Add')
227
 
        if not isinstance(imag, complex) or imag.real != 0.0:
228
 
            raise UnknownType('Add')
229
 
        return real+imag
230
 
    
231
 
    def build_Getattr(self, o):
232
 
        parent = self.build(o.expr)
233
 
        return getattr(parent, o.attrname)
234
 
    
235
 
    def build_UnarySub(self, o):
236
 
        return -self.build_Const(o.getChildren()[0])
237
 
    
238
 
    def build_UnaryAdd(self, o):
239
 
        return self.build_Const(o.getChildren()[0])
240
 
 
241
 
 
242
 
_builder = Builder()
243
 
 
244
 
 
245
 
def unrepr(s):
246
 
    if not s:
247
 
        return s
248
 
    return _builder.build(getObj(s))
249
 
 
250
 
 
251
 
 
252
 
class ConfigObjError(SyntaxError):
253
 
    """
254
 
    This is the base class for all errors that ConfigObj raises.
255
 
    It is a subclass of SyntaxError.
256
 
    """
257
 
    def __init__(self, msg='', line_number=None, line=''):
258
 
        self.line = line
259
 
        self.line_number = line_number
260
 
        self.msg = msg
261
 
        SyntaxError.__init__(self, msg)
262
 
 
263
 
 
264
 
class NestingError(ConfigObjError):
265
 
    """
266
 
    This error indicates a level of nesting that doesn't match.
267
 
    """
268
 
 
269
 
 
270
 
class ParseError(ConfigObjError):
271
 
    """
272
 
    This error indicates that a line is badly written.
273
 
    It is neither a valid ``key = value`` line,
274
 
    nor a valid section marker line.
275
 
    """
276
 
 
277
 
 
278
 
class ReloadError(IOError):
279
 
    """
280
 
    A 'reload' operation failed.
281
 
    This exception is a subclass of ``IOError``.
282
 
    """
283
 
    def __init__(self):
284
 
        IOError.__init__(self, 'reload failed, filename is not set.')
285
 
 
286
 
 
287
 
class DuplicateError(ConfigObjError):
288
 
    """
289
 
    The keyword or section specified already exists.
290
 
    """
291
 
 
292
 
 
293
 
class ConfigspecError(ConfigObjError):
294
 
    """
295
 
    An error occured whilst parsing a configspec.
296
 
    """
297
 
 
298
 
 
299
 
class InterpolationError(ConfigObjError):
300
 
    """Base class for the two interpolation errors."""
301
 
 
302
 
 
303
 
class InterpolationLoopError(InterpolationError):
304
 
    """Maximum interpolation depth exceeded in string interpolation."""
305
 
 
306
 
    def __init__(self, option):
307
 
        InterpolationError.__init__(
308
 
            self,
309
 
            'interpolation loop detected in value "%s".' % option)
310
 
 
311
 
 
312
 
class RepeatSectionError(ConfigObjError):
313
 
    """
314
 
    This error indicates additional sections in a section with a
315
 
    ``__many__`` (repeated) section.
316
 
    """
317
 
 
318
 
 
319
 
class MissingInterpolationOption(InterpolationError):
320
 
    """A value specified for interpolation was missing."""
321
 
 
322
 
    def __init__(self, option):
323
 
        InterpolationError.__init__(
324
 
            self,
325
 
            'missing option "%s" in interpolation.' % option)
326
 
 
327
 
 
328
 
class UnreprError(ConfigObjError):
329
 
    """An error parsing in unrepr mode."""
330
 
 
331
 
 
332
 
 
333
 
class InterpolationEngine(object):
334
 
    """
335
 
    A helper class to help perform string interpolation.
336
 
 
337
 
    This class is an abstract base class; its descendants perform
338
 
    the actual work.
339
 
    """
340
 
 
341
 
    # compiled regexp to use in self.interpolate()
342
 
    _KEYCRE = re.compile(r"%\(([^)]*)\)s")
343
 
 
344
 
    def __init__(self, section):
345
 
        # the Section instance that "owns" this engine
346
 
        self.section = section
347
 
 
348
 
 
349
 
    def interpolate(self, key, value):
350
 
        def recursive_interpolate(key, value, section, backtrail):
351
 
            """The function that does the actual work.
352
 
 
353
 
            ``value``: the string we're trying to interpolate.
354
 
            ``section``: the section in which that string was found
355
 
            ``backtrail``: a dict to keep track of where we've been,
356
 
            to detect and prevent infinite recursion loops
357
 
 
358
 
            This is similar to a depth-first-search algorithm.
359
 
            """
360
 
            # Have we been here already?
361
 
            if backtrail.has_key((key, section.name)):
362
 
                # Yes - infinite loop detected
363
 
                raise InterpolationLoopError(key)
364
 
            # Place a marker on our backtrail so we won't come back here again
365
 
            backtrail[(key, section.name)] = 1
366
 
 
367
 
            # Now start the actual work
368
 
            match = self._KEYCRE.search(value)
369
 
            while match:
370
 
                # The actual parsing of the match is implementation-dependent,
371
 
                # so delegate to our helper function
372
 
                k, v, s = self._parse_match(match)
373
 
                if k is None:
374
 
                    # That's the signal that no further interpolation is needed
375
 
                    replacement = v
376
 
                else:
377
 
                    # Further interpolation may be needed to obtain final value
378
 
                    replacement = recursive_interpolate(k, v, s, backtrail)
379
 
                # Replace the matched string with its final value
380
 
                start, end = match.span()
381
 
                value = ''.join((value[:start], replacement, value[end:]))
382
 
                new_search_start = start + len(replacement)
383
 
                # Pick up the next interpolation key, if any, for next time
384
 
                # through the while loop
385
 
                match = self._KEYCRE.search(value, new_search_start)
386
 
 
387
 
            # Now safe to come back here again; remove marker from backtrail
388
 
            del backtrail[(key, section.name)]
389
 
 
390
 
            return value
391
 
 
392
 
        # Back in interpolate(), all we have to do is kick off the recursive
393
 
        # function with appropriate starting values
394
 
        value = recursive_interpolate(key, value, self.section, {})
395
 
        return value
396
 
 
397
 
 
398
 
    def _fetch(self, key):
399
 
        """Helper function to fetch values from owning section.
400
 
 
401
 
        Returns a 2-tuple: the value, and the section where it was found.
402
 
        """
403
 
        # switch off interpolation before we try and fetch anything !
404
 
        save_interp = self.section.main.interpolation
405
 
        self.section.main.interpolation = False
406
 
 
407
 
        # Start at section that "owns" this InterpolationEngine
408
 
        current_section = self.section
409
 
        while True:
410
 
            # try the current section first
411
 
            val = current_section.get(key)
412
 
            if val is not None:
413
 
                break
414
 
            # try "DEFAULT" next
415
 
            val = current_section.get('DEFAULT', {}).get(key)
416
 
            if val is not None:
417
 
                break
418
 
            # move up to parent and try again
419
 
            # top-level's parent is itself
420
 
            if current_section.parent is current_section:
421
 
                # reached top level, time to give up
422
 
                break
423
 
            current_section = current_section.parent
424
 
 
425
 
        # restore interpolation to previous value before returning
426
 
        self.section.main.interpolation = save_interp
427
 
        if val is None:
428
 
            raise MissingInterpolationOption(key)
429
 
        return val, current_section
430
 
 
431
 
 
432
 
    def _parse_match(self, match):
433
 
        """Implementation-dependent helper function.
434
 
 
435
 
        Will be passed a match object corresponding to the interpolation
436
 
        key we just found (e.g., "%(foo)s" or "$foo"). Should look up that
437
 
        key in the appropriate config file section (using the ``_fetch()``
438
 
        helper function) and return a 3-tuple: (key, value, section)
439
 
 
440
 
        ``key`` is the name of the key we're looking for
441
 
        ``value`` is the value found for that key
442
 
        ``section`` is a reference to the section where it was found
443
 
 
444
 
        ``key`` and ``section`` should be None if no further
445
 
        interpolation should be performed on the resulting value
446
 
        (e.g., if we interpolated "$$" and returned "$").
447
 
        """
448
 
        raise NotImplementedError()
449
 
    
450
 
 
451
 
 
452
 
class ConfigParserInterpolation(InterpolationEngine):
453
 
    """Behaves like ConfigParser."""
454
 
    _KEYCRE = re.compile(r"%\(([^)]*)\)s")
455
 
 
456
 
    def _parse_match(self, match):
457
 
        key = match.group(1)
458
 
        value, section = self._fetch(key)
459
 
        return key, value, section
460
 
 
461
 
 
462
 
 
463
 
class TemplateInterpolation(InterpolationEngine):
464
 
    """Behaves like string.Template."""
465
 
    _delimiter = '$'
466
 
    _KEYCRE = re.compile(r"""
467
 
        \$(?:
468
 
          (?P<escaped>\$)              |   # Two $ signs
469
 
          (?P<named>[_a-z][_a-z0-9]*)  |   # $name format
470
 
          {(?P<braced>[^}]*)}              # ${name} format
471
 
        )
472
 
        """, re.IGNORECASE | re.VERBOSE)
473
 
 
474
 
    def _parse_match(self, match):
475
 
        # Valid name (in or out of braces): fetch value from section
476
 
        key = match.group('named') or match.group('braced')
477
 
        if key is not None:
478
 
            value, section = self._fetch(key)
479
 
            return key, value, section
480
 
        # Escaped delimiter (e.g., $$): return single delimiter
481
 
        if match.group('escaped') is not None:
482
 
            # Return None for key and section to indicate it's time to stop
483
 
            return None, self._delimiter, None
484
 
        # Anything else: ignore completely, just return it unchanged
485
 
        return None, match.group(), None
486
 
 
487
 
 
488
 
interpolation_engines = {
489
 
    'configparser': ConfigParserInterpolation,
490
 
    'template': TemplateInterpolation,
491
 
}
492
 
 
493
 
 
494
 
 
495
 
class Section(dict):
496
 
    """
497
 
    A dictionary-like object that represents a section in a config file.
498
 
    
499
 
    It does string interpolation if the 'interpolation' attribute
500
 
    of the 'main' object is set to True.
501
 
    
502
 
    Interpolation is tried first from this object, then from the 'DEFAULT'
503
 
    section of this object, next from the parent and its 'DEFAULT' section,
504
 
    and so on until the main object is reached.
505
 
    
506
 
    A Section will behave like an ordered dictionary - following the
507
 
    order of the ``scalars`` and ``sections`` attributes.
508
 
    You can use this to change the order of members.
509
 
    
510
 
    Iteration follows the order: scalars, then sections.
511
 
    """
512
 
 
513
 
    def __init__(self, parent, depth, main, indict=None, name=None):
514
 
        """
515
 
        * parent is the section above
516
 
        * depth is the depth level of this section
517
 
        * main is the main ConfigObj
518
 
        * indict is a dictionary to initialise the section with
519
 
        """
520
 
        if indict is None:
521
 
            indict = {}
522
 
        dict.__init__(self)
523
 
        # used for nesting level *and* interpolation
524
 
        self.parent = parent
525
 
        # used for the interpolation attribute
526
 
        self.main = main
527
 
        # level of nesting depth of this Section
528
 
        self.depth = depth
529
 
        # purely for information
530
 
        self.name = name
531
 
        #
532
 
        self._initialise()
533
 
        # we do this explicitly so that __setitem__ is used properly
534
 
        # (rather than just passing to ``dict.__init__``)
535
 
        for entry, value in indict.iteritems():
536
 
            self[entry] = value
537
 
            
538
 
            
539
 
    def _initialise(self):
540
 
        # the sequence of scalar values in this Section
541
 
        self.scalars = []
542
 
        # the sequence of sections in this Section
543
 
        self.sections = []
544
 
        # for comments :-)
545
 
        self.comments = {}
546
 
        self.inline_comments = {}
547
 
        # for the configspec
548
 
        self.configspec = {}
549
 
        self._order = []
550
 
        self._configspec_comments = {}
551
 
        self._configspec_inline_comments = {}
552
 
        self._cs_section_comments = {}
553
 
        self._cs_section_inline_comments = {}
554
 
        # for defaults
555
 
        self.defaults = []
556
 
        self.default_values = {}
557
 
 
558
 
 
559
 
    def _interpolate(self, key, value):
560
 
        try:
561
 
            # do we already have an interpolation engine?
562
 
            engine = self._interpolation_engine
563
 
        except AttributeError:
564
 
            # not yet: first time running _interpolate(), so pick the engine
565
 
            name = self.main.interpolation
566
 
            if name == True:  # note that "if name:" would be incorrect here
567
 
                # backwards-compatibility: interpolation=True means use default
568
 
                name = DEFAULT_INTERPOLATION
569
 
            name = name.lower()  # so that "Template", "template", etc. all work
570
 
            class_ = interpolation_engines.get(name, None)
571
 
            if class_ is None:
572
 
                # invalid value for self.main.interpolation
573
 
                self.main.interpolation = False
574
 
                return value
575
 
            else:
576
 
                # save reference to engine so we don't have to do this again
577
 
                engine = self._interpolation_engine = class_(self)
578
 
        # let the engine do the actual work
579
 
        return engine.interpolate(key, value)
580
 
 
581
 
 
582
 
    def __getitem__(self, key):
583
 
        """Fetch the item and do string interpolation."""
584
 
        val = dict.__getitem__(self, key)
585
 
        if self.main.interpolation and isinstance(val, StringTypes):
586
 
            return self._interpolate(key, val)
587
 
        return val
588
 
 
589
 
 
590
 
    def __setitem__(self, key, value, unrepr=False):
591
 
        """
592
 
        Correctly set a value.
593
 
        
594
 
        Making dictionary values Section instances.
595
 
        (We have to special case 'Section' instances - which are also dicts)
596
 
        
597
 
        Keys must be strings.
598
 
        Values need only be strings (or lists of strings) if
599
 
        ``main.stringify`` is set.
600
 
        
601
 
        `unrepr`` must be set when setting a value to a dictionary, without
602
 
        creating a new sub-section.
603
 
        """
604
 
        if not isinstance(key, StringTypes):
605
 
            raise ValueError('The key "%s" is not a string.' % key)
606
 
        
607
 
        # add the comment
608
 
        if not self.comments.has_key(key):
609
 
            self.comments[key] = []
610
 
            self.inline_comments[key] = ''
611
 
        # remove the entry from defaults
612
 
        if key in self.defaults:
613
 
            self.defaults.remove(key)
614
 
        #
615
 
        if isinstance(value, Section):
616
 
            if not self.has_key(key):
617
 
                self.sections.append(key)
618
 
            dict.__setitem__(self, key, value)
619
 
        elif isinstance(value, dict) and not unrepr:
620
 
            # First create the new depth level,
621
 
            # then create the section
622
 
            if not self.has_key(key):
623
 
                self.sections.append(key)
624
 
            new_depth = self.depth + 1
625
 
            dict.__setitem__(
626
 
                self,
627
 
                key,
628
 
                Section(
629
 
                    self,
630
 
                    new_depth,
631
 
                    self.main,
632
 
                    indict=value,
633
 
                    name=key))
634
 
        else:
635
 
            if not self.has_key(key):
636
 
                self.scalars.append(key)
637
 
            if not self.main.stringify:
638
 
                if isinstance(value, StringTypes):
639
 
                    pass
640
 
                elif isinstance(value, (list, tuple)):
641
 
                    for entry in value:
642
 
                        if not isinstance(entry, StringTypes):
643
 
                            raise TypeError('Value is not a string "%s".' % entry)
644
 
                else:
645
 
                    raise TypeError('Value is not a string "%s".' % value)
646
 
            dict.__setitem__(self, key, value)
647
 
 
648
 
 
649
 
    def __delitem__(self, key):
650
 
        """Remove items from the sequence when deleting."""
651
 
        dict. __delitem__(self, key)
652
 
        if key in self.scalars:
653
 
            self.scalars.remove(key)
654
 
        else:
655
 
            self.sections.remove(key)
656
 
        del self.comments[key]
657
 
        del self.inline_comments[key]
658
 
 
659
 
 
660
 
    def get(self, key, default=None):
661
 
        """A version of ``get`` that doesn't bypass string interpolation."""
662
 
        try:
663
 
            return self[key]
664
 
        except KeyError:
665
 
            return default
666
 
 
667
 
 
668
 
    def update(self, indict):
669
 
        """
670
 
        A version of update that uses our ``__setitem__``.
671
 
        """
672
 
        for entry in indict:
673
 
            self[entry] = indict[entry]
674
 
 
675
 
 
676
 
    def pop(self, key, *args):
677
 
        """
678
 
        'D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
679
 
        If key is not found, d is returned if given, otherwise KeyError is raised'
680
 
        """
681
 
        val = dict.pop(self, key, *args)
682
 
        if key in self.scalars:
683
 
            del self.comments[key]
684
 
            del self.inline_comments[key]
685
 
            self.scalars.remove(key)
686
 
        elif key in self.sections:
687
 
            del self.comments[key]
688
 
            del self.inline_comments[key]
689
 
            self.sections.remove(key)
690
 
        if self.main.interpolation and isinstance(val, StringTypes):
691
 
            return self._interpolate(key, val)
692
 
        return val
693
 
 
694
 
 
695
 
    def popitem(self):
696
 
        """Pops the first (key,val)"""
697
 
        sequence = (self.scalars + self.sections)
698
 
        if not sequence:
699
 
            raise KeyError(": 'popitem(): dictionary is empty'")
700
 
        key = sequence[0]
701
 
        val =  self[key]
702
 
        del self[key]
703
 
        return key, val
704
 
 
705
 
 
706
 
    def clear(self):
707
 
        """
708
 
        A version of clear that also affects scalars/sections
709
 
        Also clears comments and configspec.
710
 
        
711
 
        Leaves other attributes alone :
712
 
            depth/main/parent are not affected
713
 
        """
714
 
        dict.clear(self)
715
 
        self.scalars = []
716
 
        self.sections = []
717
 
        self.comments = {}
718
 
        self.inline_comments = {}
719
 
        self.configspec = {}
720
 
 
721
 
 
722
 
    def setdefault(self, key, default=None):
723
 
        """A version of setdefault that sets sequence if appropriate."""
724
 
        try:
725
 
            return self[key]
726
 
        except KeyError:
727
 
            self[key] = default
728
 
            return self[key]
729
 
 
730
 
 
731
 
    def items(self):
732
 
        """D.items() -> list of D's (key, value) pairs, as 2-tuples"""
733
 
        return zip((self.scalars + self.sections), self.values())
734
 
 
735
 
 
736
 
    def keys(self):
737
 
        """D.keys() -> list of D's keys"""
738
 
        return (self.scalars + self.sections)
739
 
 
740
 
 
741
 
    def values(self):
742
 
        """D.values() -> list of D's values"""
743
 
        return [self[key] for key in (self.scalars + self.sections)]
744
 
 
745
 
 
746
 
    def iteritems(self):
747
 
        """D.iteritems() -> an iterator over the (key, value) items of D"""
748
 
        return iter(self.items())
749
 
 
750
 
 
751
 
    def iterkeys(self):
752
 
        """D.iterkeys() -> an iterator over the keys of D"""
753
 
        return iter((self.scalars + self.sections))
754
 
 
755
 
    __iter__ = iterkeys
756
 
 
757
 
 
758
 
    def itervalues(self):
759
 
        """D.itervalues() -> an iterator over the values of D"""
760
 
        return iter(self.values())
761
 
 
762
 
 
763
 
    def __repr__(self):
764
 
        """x.__repr__() <==> repr(x)"""
765
 
        return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(self[key])))
766
 
            for key in (self.scalars + self.sections)])
767
 
 
768
 
    __str__ = __repr__
769
 
    __str__.__doc__ = "x.__str__() <==> str(x)"
770
 
 
771
 
 
772
 
    # Extra methods - not in a normal dictionary
773
 
 
774
 
    def dict(self):
775
 
        """
776
 
        Return a deepcopy of self as a dictionary.
777
 
        
778
 
        All members that are ``Section`` instances are recursively turned to
779
 
        ordinary dictionaries - by calling their ``dict`` method.
780
 
        
781
 
        >>> n = a.dict()
782
 
        >>> n == a
783
 
        1
784
 
        >>> n is a
785
 
        0
786
 
        """
787
 
        newdict = {}
788
 
        for entry in self:
789
 
            this_entry = self[entry]
790
 
            if isinstance(this_entry, Section):
791
 
                this_entry = this_entry.dict()
792
 
            elif isinstance(this_entry, list):
793
 
                # create a copy rather than a reference
794
 
                this_entry = list(this_entry)
795
 
            elif isinstance(this_entry, tuple):
796
 
                # create a copy rather than a reference
797
 
                this_entry = tuple(this_entry)
798
 
            newdict[entry] = this_entry
799
 
        return newdict
800
 
 
801
 
 
802
 
    def merge(self, indict):
803
 
        """
804
 
        A recursive update - useful for merging config files.
805
 
        
806
 
        >>> a = '''[section1]
807
 
        ...     option1 = True
808
 
        ...     [[subsection]]
809
 
        ...     more_options = False
810
 
        ...     # end of file'''.splitlines()
811
 
        >>> b = '''# File is user.ini
812
 
        ...     [section1]
813
 
        ...     option1 = False
814
 
        ...     # end of file'''.splitlines()
815
 
        >>> c1 = ConfigObj(b)
816
 
        >>> c2 = ConfigObj(a)
817
 
        >>> c2.merge(c1)
818
 
        >>> c2
819
 
        {'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}}
820
 
        """
821
 
        for key, val in indict.items():
822
 
            if (key in self and isinstance(self[key], dict) and
823
 
                                isinstance(val, dict)):
824
 
                self[key].merge(val)
825
 
            else:   
826
 
                self[key] = val
827
 
 
828
 
 
829
 
    def rename(self, oldkey, newkey):
830
 
        """
831
 
        Change a keyname to another, without changing position in sequence.
832
 
        
833
 
        Implemented so that transformations can be made on keys,
834
 
        as well as on values. (used by encode and decode)
835
 
        
836
 
        Also renames comments.
837
 
        """
838
 
        if oldkey in self.scalars:
839
 
            the_list = self.scalars
840
 
        elif oldkey in self.sections:
841
 
            the_list = self.sections
842
 
        else:
843
 
            raise KeyError('Key "%s" not found.' % oldkey)
844
 
        pos = the_list.index(oldkey)
845
 
        #
846
 
        val = self[oldkey]
847
 
        dict.__delitem__(self, oldkey)
848
 
        dict.__setitem__(self, newkey, val)
849
 
        the_list.remove(oldkey)
850
 
        the_list.insert(pos, newkey)
851
 
        comm = self.comments[oldkey]
852
 
        inline_comment = self.inline_comments[oldkey]
853
 
        del self.comments[oldkey]
854
 
        del self.inline_comments[oldkey]
855
 
        self.comments[newkey] = comm
856
 
        self.inline_comments[newkey] = inline_comment
857
 
 
858
 
 
859
 
    def walk(self, function, raise_errors=True,
860
 
            call_on_sections=False, **keywargs):
861
 
        """
862
 
        Walk every member and call a function on the keyword and value.
863
 
        
864
 
        Return a dictionary of the return values
865
 
        
866
 
        If the function raises an exception, raise the errror
867
 
        unless ``raise_errors=False``, in which case set the return value to
868
 
        ``False``.
869
 
        
870
 
        Any unrecognised keyword arguments you pass to walk, will be pased on
871
 
        to the function you pass in.
872
 
        
873
 
        Note: if ``call_on_sections`` is ``True`` then - on encountering a
874
 
        subsection, *first* the function is called for the *whole* subsection,
875
 
        and then recurses into it's members. This means your function must be
876
 
        able to handle strings, dictionaries and lists. This allows you
877
 
        to change the key of subsections as well as for ordinary members. The
878
 
        return value when called on the whole subsection has to be discarded.
879
 
        
880
 
        See  the encode and decode methods for examples, including functions.
881
 
        
882
 
        .. caution::
883
 
        
884
 
            You can use ``walk`` to transform the names of members of a section
885
 
            but you mustn't add or delete members.
886
 
        
887
 
        >>> config = '''[XXXXsection]
888
 
        ... XXXXkey = XXXXvalue'''.splitlines()
889
 
        >>> cfg = ConfigObj(config)
890
 
        >>> cfg
891
 
        {'XXXXsection': {'XXXXkey': 'XXXXvalue'}}
892
 
        >>> def transform(section, key):
893
 
        ...     val = section[key]
894
 
        ...     newkey = key.replace('XXXX', 'CLIENT1')
895
 
        ...     section.rename(key, newkey)
896
 
        ...     if isinstance(val, (tuple, list, dict)):
897
 
        ...         pass
898
 
        ...     else:
899
 
        ...         val = val.replace('XXXX', 'CLIENT1')
900
 
        ...         section[newkey] = val
901
 
        >>> cfg.walk(transform, call_on_sections=True)
902
 
        {'CLIENT1section': {'CLIENT1key': None}}
903
 
        >>> cfg
904
 
        {'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}}
905
 
        """
906
 
        out = {}
907
 
        # scalars first
908
 
        for i in range(len(self.scalars)):
909
 
            entry = self.scalars[i]
910
 
            try:
911
 
                val = function(self, entry, **keywargs)
912
 
                # bound again in case name has changed
913
 
                entry = self.scalars[i]
914
 
                out[entry] = val
915
 
            except Exception:
916
 
                if raise_errors:
917
 
                    raise
918
 
                else:
919
 
                    entry = self.scalars[i]
920
 
                    out[entry] = False
921
 
        # then sections
922
 
        for i in range(len(self.sections)):
923
 
            entry = self.sections[i]
924
 
            if call_on_sections:
925
 
                try:
926
 
                    function(self, entry, **keywargs)
927
 
                except Exception:
928
 
                    if raise_errors:
929
 
                        raise
930
 
                    else:
931
 
                        entry = self.sections[i]
932
 
                        out[entry] = False
933
 
                # bound again in case name has changed
934
 
                entry = self.sections[i]
935
 
            # previous result is discarded
936
 
            out[entry] = self[entry].walk(
937
 
                function,
938
 
                raise_errors=raise_errors,
939
 
                call_on_sections=call_on_sections,
940
 
                **keywargs)
941
 
        return out
942
 
 
943
 
 
944
 
    def decode(self, encoding):
945
 
        """
946
 
        Decode all strings and values to unicode, using the specified encoding.
947
 
        
948
 
        Works with subsections and list values.
949
 
        
950
 
        Uses the ``walk`` method.
951
 
        
952
 
        Testing ``encode`` and ``decode``.
953
 
        >>> m = ConfigObj(a)
954
 
        >>> m.decode('ascii')
955
 
        >>> def testuni(val):
956
 
        ...     for entry in val:
957
 
        ...         if not isinstance(entry, unicode):
958
 
        ...             print >> sys.stderr, type(entry)
959
 
        ...             raise AssertionError, 'decode failed.'
960
 
        ...         if isinstance(val[entry], dict):
961
 
        ...             testuni(val[entry])
962
 
        ...         elif not isinstance(val[entry], unicode):
963
 
        ...             raise AssertionError, 'decode failed.'
964
 
        >>> testuni(m)
965
 
        >>> m.encode('ascii')
966
 
        >>> a == m
967
 
        1
968
 
        """
969
 
        warn('use of ``decode`` is deprecated.', DeprecationWarning)
970
 
        def decode(section, key, encoding=encoding, warn=True):
971
 
            """ """
972
 
            val = section[key]
973
 
            if isinstance(val, (list, tuple)):
974
 
                newval = []
975
 
                for entry in val:
976
 
                    newval.append(entry.decode(encoding))
977
 
            elif isinstance(val, dict):
978
 
                newval = val
979
 
            else:
980
 
                newval = val.decode(encoding)
981
 
            newkey = key.decode(encoding)
982
 
            section.rename(key, newkey)
983
 
            section[newkey] = newval
984
 
        # using ``call_on_sections`` allows us to modify section names
985
 
        self.walk(decode, call_on_sections=True)
986
 
 
987
 
 
988
 
    def encode(self, encoding):
989
 
        """
990
 
        Encode all strings and values from unicode,
991
 
        using the specified encoding.
992
 
        
993
 
        Works with subsections and list values.
994
 
        Uses the ``walk`` method.
995
 
        """
996
 
        warn('use of ``encode`` is deprecated.', DeprecationWarning)
997
 
        def encode(section, key, encoding=encoding):
998
 
            """ """
999
 
            val = section[key]
1000
 
            if isinstance(val, (list, tuple)):
1001
 
                newval = []
1002
 
                for entry in val:
1003
 
                    newval.append(entry.encode(encoding))
1004
 
            elif isinstance(val, dict):
1005
 
                newval = val
1006
 
            else:
1007
 
                newval = val.encode(encoding)
1008
 
            newkey = key.encode(encoding)
1009
 
            section.rename(key, newkey)
1010
 
            section[newkey] = newval
1011
 
        self.walk(encode, call_on_sections=True)
1012
 
 
1013
 
 
1014
 
    def istrue(self, key):
1015
 
        """A deprecated version of ``as_bool``."""
1016
 
        warn('use of ``istrue`` is deprecated. Use ``as_bool`` method '
1017
 
                'instead.', DeprecationWarning)
1018
 
        return self.as_bool(key)
1019
 
 
1020
 
 
1021
 
    def as_bool(self, key):
1022
 
        """
1023
 
        Accepts a key as input. The corresponding value must be a string or
1024
 
        the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to
1025
 
        retain compatibility with Python 2.2.
1026
 
        
1027
 
        If the string is one of  ``True``, ``On``, ``Yes``, or ``1`` it returns 
1028
 
        ``True``.
1029
 
        
1030
 
        If the string is one of  ``False``, ``Off``, ``No``, or ``0`` it returns 
1031
 
        ``False``.
1032
 
        
1033
 
        ``as_bool`` is not case sensitive.
1034
 
        
1035
 
        Any other input will raise a ``ValueError``.
1036
 
        
1037
 
        >>> a = ConfigObj()
1038
 
        >>> a['a'] = 'fish'
1039
 
        >>> a.as_bool('a')
1040
 
        Traceback (most recent call last):
1041
 
        ValueError: Value "fish" is neither True nor False
1042
 
        >>> a['b'] = 'True'
1043
 
        >>> a.as_bool('b')
1044
 
        1
1045
 
        >>> a['b'] = 'off'
1046
 
        >>> a.as_bool('b')
1047
 
        0
1048
 
        """
1049
 
        val = self[key]
1050
 
        if val == True:
1051
 
            return True
1052
 
        elif val == False:
1053
 
            return False
1054
 
        else:
1055
 
            try:
1056
 
                if not isinstance(val, StringTypes):
1057
 
                    # TODO: Why do we raise a KeyError here?
1058
 
                    raise KeyError()
1059
 
                else:
1060
 
                    return self.main._bools[val.lower()]
1061
 
            except KeyError:
1062
 
                raise ValueError('Value "%s" is neither True nor False' % val)
1063
 
 
1064
 
 
1065
 
    def as_int(self, key):
1066
 
        """
1067
 
        A convenience method which coerces the specified value to an integer.
1068
 
        
1069
 
        If the value is an invalid literal for ``int``, a ``ValueError`` will
1070
 
        be raised.
1071
 
        
1072
 
        >>> a = ConfigObj()
1073
 
        >>> a['a'] = 'fish'
1074
 
        >>> a.as_int('a')
1075
 
        Traceback (most recent call last):
1076
 
        ValueError: invalid literal for int(): fish
1077
 
        >>> a['b'] = '1'
1078
 
        >>> a.as_int('b')
1079
 
        1
1080
 
        >>> a['b'] = '3.2'
1081
 
        >>> a.as_int('b')
1082
 
        Traceback (most recent call last):
1083
 
        ValueError: invalid literal for int(): 3.2
1084
 
        """
1085
 
        return int(self[key])
1086
 
 
1087
 
 
1088
 
    def as_float(self, key):
1089
 
        """
1090
 
        A convenience method which coerces the specified value to a float.
1091
 
        
1092
 
        If the value is an invalid literal for ``float``, a ``ValueError`` will
1093
 
        be raised.
1094
 
        
1095
 
        >>> a = ConfigObj()
1096
 
        >>> a['a'] = 'fish'
1097
 
        >>> a.as_float('a')
1098
 
        Traceback (most recent call last):
1099
 
        ValueError: invalid literal for float(): fish
1100
 
        >>> a['b'] = '1'
1101
 
        >>> a.as_float('b')
1102
 
        1.0
1103
 
        >>> a['b'] = '3.2'
1104
 
        >>> a.as_float('b')
1105
 
        3.2000000000000002
1106
 
        """
1107
 
        return float(self[key])
1108
 
 
1109
 
 
1110
 
    def restore_default(self, key):
1111
 
        """
1112
 
        Restore (and return) default value for the specified key.
1113
 
        
1114
 
        This method will only work for a ConfigObj that was created
1115
 
        with a configspec and has been validated.
1116
 
        
1117
 
        If there is no default value for this key, ``KeyError`` is raised.
1118
 
        """
1119
 
        default = self.default_values[key]
1120
 
        dict.__setitem__(self, key, default)
1121
 
        if key not in self.defaults:
1122
 
            self.defaults.append(key)
1123
 
        return default
1124
 
 
1125
 
    
1126
 
    def restore_defaults(self):
1127
 
        """
1128
 
        Recursively restore default values to all members
1129
 
        that have them.
1130
 
        
1131
 
        This method will only work for a ConfigObj that was created
1132
 
        with a configspec and has been validated.
1133
 
        
1134
 
        It doesn't delete or modify entries without default values.
1135
 
        """
1136
 
        for key in self.default_values:
1137
 
            self.restore_default(key)
1138
 
            
1139
 
        for section in self.sections:
1140
 
            self[section].restore_defaults()
1141
 
 
1142
 
 
1143
 
class ConfigObj(Section):
1144
 
    """An object to read, create, and write config files."""
1145
 
 
1146
 
    _keyword = re.compile(r'''^ # line start
1147
 
        (\s*)                   # indentation
1148
 
        (                       # keyword
1149
 
            (?:".*?")|          # double quotes
1150
 
            (?:'.*?')|          # single quotes
1151
 
            (?:[^'"=].*?)       # no quotes
1152
 
        )
1153
 
        \s*=\s*                 # divider
1154
 
        (.*)                    # value (including list values and comments)
1155
 
        $   # line end
1156
 
        ''',
1157
 
        re.VERBOSE)
1158
 
 
1159
 
    _sectionmarker = re.compile(r'''^
1160
 
        (\s*)                     # 1: indentation
1161
 
        ((?:\[\s*)+)              # 2: section marker open
1162
 
        (                         # 3: section name open
1163
 
            (?:"\s*\S.*?\s*")|    # at least one non-space with double quotes
1164
 
            (?:'\s*\S.*?\s*')|    # at least one non-space with single quotes
1165
 
            (?:[^'"\s].*?)        # at least one non-space unquoted
1166
 
        )                         # section name close
1167
 
        ((?:\s*\])+)              # 4: section marker close
1168
 
        \s*(\#.*)?                # 5: optional comment
1169
 
        $''',
1170
 
        re.VERBOSE)
1171
 
 
1172
 
    # this regexp pulls list values out as a single string
1173
 
    # or single values and comments
1174
 
    # FIXME: this regex adds a '' to the end of comma terminated lists
1175
 
    #   workaround in ``_handle_value``
1176
 
    _valueexp = re.compile(r'''^
1177
 
        (?:
1178
 
            (?:
1179
 
                (
1180
 
                    (?:
1181
 
                        (?:
1182
 
                            (?:".*?")|              # double quotes
1183
 
                            (?:'.*?')|              # single quotes
1184
 
                            (?:[^'",\#][^,\#]*?)    # unquoted
1185
 
                        )
1186
 
                        \s*,\s*                     # comma
1187
 
                    )*      # match all list items ending in a comma (if any)
1188
 
                )
1189
 
                (
1190
 
                    (?:".*?")|                      # double quotes
1191
 
                    (?:'.*?')|                      # single quotes
1192
 
                    (?:[^'",\#\s][^,]*?)|           # unquoted
1193
 
                    (?:(?<!,))                      # Empty value
1194
 
                )?          # last item in a list - or string value
1195
 
            )|
1196
 
            (,)             # alternatively a single comma - empty list
1197
 
        )
1198
 
        \s*(\#.*)?          # optional comment
1199
 
        $''',
1200
 
        re.VERBOSE)
1201
 
 
1202
 
    # use findall to get the members of a list value
1203
 
    _listvalueexp = re.compile(r'''
1204
 
        (
1205
 
            (?:".*?")|          # double quotes
1206
 
            (?:'.*?')|          # single quotes
1207
 
            (?:[^'",\#].*?)       # unquoted
1208
 
        )
1209
 
        \s*,\s*                 # comma
1210
 
        ''',
1211
 
        re.VERBOSE)
1212
 
 
1213
 
    # this regexp is used for the value
1214
 
    # when lists are switched off
1215
 
    _nolistvalue = re.compile(r'''^
1216
 
        (
1217
 
            (?:".*?")|          # double quotes
1218
 
            (?:'.*?')|          # single quotes
1219
 
            (?:[^'"\#].*?)|     # unquoted
1220
 
            (?:)                # Empty value
1221
 
        )
1222
 
        \s*(\#.*)?              # optional comment
1223
 
        $''',
1224
 
        re.VERBOSE)
1225
 
 
1226
 
    # regexes for finding triple quoted values on one line
1227
 
    _single_line_single = re.compile(r"^'''(.*?)'''\s*(#.*)?$")
1228
 
    _single_line_double = re.compile(r'^"""(.*?)"""\s*(#.*)?$')
1229
 
    _multi_line_single = re.compile(r"^(.*?)'''\s*(#.*)?$")
1230
 
    _multi_line_double = re.compile(r'^(.*?)"""\s*(#.*)?$')
1231
 
 
1232
 
    _triple_quote = {
1233
 
        "'''": (_single_line_single, _multi_line_single),
1234
 
        '"""': (_single_line_double, _multi_line_double),
1235
 
    }
1236
 
 
1237
 
    # Used by the ``istrue`` Section method
1238
 
    _bools = {
1239
 
        'yes': True, 'no': False,
1240
 
        'on': True, 'off': False,
1241
 
        '1': True, '0': False,
1242
 
        'true': True, 'false': False,
1243
 
        }
1244
 
 
1245
 
 
1246
 
    def __init__(self, infile=None, options=None, **kwargs):
1247
 
        """
1248
 
        Parse a config file or create a config file object.
1249
 
        
1250
 
        ``ConfigObj(infile=None, options=None, **kwargs)``
1251
 
        """
1252
 
        # init the superclass
1253
 
        Section.__init__(self, self, 0, self)
1254
 
        
1255
 
        if infile is None:
1256
 
            infile = []
1257
 
        if options is None:
1258
 
            options = {}
1259
 
        else:
1260
 
            options = dict(options)
1261
 
            
1262
 
        # keyword arguments take precedence over an options dictionary
1263
 
        options.update(kwargs)
1264
 
        
1265
 
        defaults = OPTION_DEFAULTS.copy()
1266
 
        # TODO: check the values too.
1267
 
        for entry in options:
1268
 
            if entry not in defaults:
1269
 
                raise TypeError('Unrecognised option "%s".' % entry)
1270
 
        
1271
 
        # Add any explicit options to the defaults
1272
 
        defaults.update(options)
1273
 
        self._initialise(defaults)
1274
 
        configspec = defaults['configspec']
1275
 
        self._original_configspec = configspec
1276
 
        self._load(infile, configspec)
1277
 
        
1278
 
        
1279
 
    def _load(self, infile, configspec):
1280
 
        if isinstance(infile, StringTypes):
1281
 
            self.filename = infile
1282
 
            if os.path.isfile(infile):
1283
 
                h = open(infile, 'rb')
1284
 
                infile = h.read() or []
1285
 
                h.close()
1286
 
            elif self.file_error:
1287
 
                # raise an error if the file doesn't exist
1288
 
                raise IOError('Config file not found: "%s".' % self.filename)
1289
 
            else:
1290
 
                # file doesn't already exist
1291
 
                if self.create_empty:
1292
 
                    # this is a good test that the filename specified
1293
 
                    # isn't impossible - like on a non-existent device
1294
 
                    h = open(infile, 'w')
1295
 
                    h.write('')
1296
 
                    h.close()
1297
 
                infile = []
1298
 
                
1299
 
        elif isinstance(infile, (list, tuple)):
1300
 
            infile = list(infile)
1301
 
            
1302
 
        elif isinstance(infile, dict):
1303
 
            # initialise self
1304
 
            # the Section class handles creating subsections
1305
 
            if isinstance(infile, ConfigObj):
1306
 
                # get a copy of our ConfigObj
1307
 
                infile = infile.dict()
1308
 
                
1309
 
            for entry in infile:
1310
 
                self[entry] = infile[entry]
1311
 
            del self._errors
1312
 
            
1313
 
            if configspec is not None:
1314
 
                self._handle_configspec(configspec)
1315
 
            else:
1316
 
                self.configspec = None
1317
 
            return
1318
 
        
1319
 
        elif getattr(infile, 'read', None) is not None:
1320
 
            # This supports file like objects
1321
 
            infile = infile.read() or []
1322
 
            # needs splitting into lines - but needs doing *after* decoding
1323
 
            # in case it's not an 8 bit encoding
1324
 
        else:
1325
 
            raise TypeError('infile must be a filename, file like object, or list of lines.')
1326
 
        
1327
 
        if infile:
1328
 
            # don't do it for the empty ConfigObj
1329
 
            infile = self._handle_bom(infile)
1330
 
            # infile is now *always* a list
1331
 
            #
1332
 
            # Set the newlines attribute (first line ending it finds)
1333
 
            # and strip trailing '\n' or '\r' from lines
1334
 
            for line in infile:
1335
 
                if (not line) or (line[-1] not in ('\r', '\n', '\r\n')):
1336
 
                    continue
1337
 
                for end in ('\r\n', '\n', '\r'):
1338
 
                    if line.endswith(end):
1339
 
                        self.newlines = end
1340
 
                        break
1341
 
                break
1342
 
 
1343
 
            infile = [line.rstrip('\r\n') for line in infile]
1344
 
            
1345
 
        self._parse(infile)
1346
 
        # if we had any errors, now is the time to raise them
1347
 
        if self._errors:
1348
 
            info = "at line %s." % self._errors[0].line_number
1349
 
            if len(self._errors) > 1:
1350
 
                msg = "Parsing failed with several errors.\nFirst error %s" % info
1351
 
                error = ConfigObjError(msg)
1352
 
            else:
1353
 
                error = self._errors[0]
1354
 
            # set the errors attribute; it's a list of tuples:
1355
 
            # (error_type, message, line_number)
1356
 
            error.errors = self._errors
1357
 
            # set the config attribute
1358
 
            error.config = self
1359
 
            raise error
1360
 
        # delete private attributes
1361
 
        del self._errors
1362
 
        
1363
 
        if configspec is None:
1364
 
            self.configspec = None
1365
 
        else:
1366
 
            self._handle_configspec(configspec)
1367
 
    
1368
 
    
1369
 
    def _initialise(self, options=None):
1370
 
        if options is None:
1371
 
            options = OPTION_DEFAULTS
1372
 
            
1373
 
        # initialise a few variables
1374
 
        self.filename = None
1375
 
        self._errors = []
1376
 
        self.raise_errors = options['raise_errors']
1377
 
        self.interpolation = options['interpolation']
1378
 
        self.list_values = options['list_values']
1379
 
        self.create_empty = options['create_empty']
1380
 
        self.file_error = options['file_error']
1381
 
        self.stringify = options['stringify']
1382
 
        self.indent_type = options['indent_type']
1383
 
        self.encoding = options['encoding']
1384
 
        self.default_encoding = options['default_encoding']
1385
 
        self.BOM = False
1386
 
        self.newlines = None
1387
 
        self.write_empty_values = options['write_empty_values']
1388
 
        self.unrepr = options['unrepr']
1389
 
        
1390
 
        self.initial_comment = []
1391
 
        self.final_comment = []
1392
 
        self.configspec = {}
1393
 
        
1394
 
        # Clear section attributes as well
1395
 
        Section._initialise(self)
1396
 
        
1397
 
        
1398
 
    def __repr__(self):
1399
 
        return ('ConfigObj({%s})' % 
1400
 
                ', '.join([('%s: %s' % (repr(key), repr(self[key]))) 
1401
 
                for key in (self.scalars + self.sections)]))
1402
 
    
1403
 
    
1404
 
    def _handle_bom(self, infile):
1405
 
        """
1406
 
        Handle any BOM, and decode if necessary.
1407
 
        
1408
 
        If an encoding is specified, that *must* be used - but the BOM should
1409
 
        still be removed (and the BOM attribute set).
1410
 
        
1411
 
        (If the encoding is wrongly specified, then a BOM for an alternative
1412
 
        encoding won't be discovered or removed.)
1413
 
        
1414
 
        If an encoding is not specified, UTF8 or UTF16 BOM will be detected and
1415
 
        removed. The BOM attribute will be set. UTF16 will be decoded to
1416
 
        unicode.
1417
 
        
1418
 
        NOTE: This method must not be called with an empty ``infile``.
1419
 
        
1420
 
        Specifying the *wrong* encoding is likely to cause a
1421
 
        ``UnicodeDecodeError``.
1422
 
        
1423
 
        ``infile`` must always be returned as a list of lines, but may be
1424
 
        passed in as a single string.
1425
 
        """
1426
 
        if ((self.encoding is not None) and
1427
 
            (self.encoding.lower() not in BOM_LIST)):
1428
 
            # No need to check for a BOM
1429
 
            # the encoding specified doesn't have one
1430
 
            # just decode
1431
 
            return self._decode(infile, self.encoding)
1432
 
        
1433
 
        if isinstance(infile, (list, tuple)):
1434
 
            line = infile[0]
1435
 
        else:
1436
 
            line = infile
1437
 
        if self.encoding is not None:
1438
 
            # encoding explicitly supplied
1439
 
            # And it could have an associated BOM
1440
 
            # TODO: if encoding is just UTF16 - we ought to check for both
1441
 
            # TODO: big endian and little endian versions.
1442
 
            enc = BOM_LIST[self.encoding.lower()]
1443
 
            if enc == 'utf_16':
1444
 
                # For UTF16 we try big endian and little endian
1445
 
                for BOM, (encoding, final_encoding) in BOMS.items():
1446
 
                    if not final_encoding:
1447
 
                        # skip UTF8
1448
 
                        continue
1449
 
                    if infile.startswith(BOM):
1450
 
                        ### BOM discovered
1451
 
                        ##self.BOM = True
1452
 
                        # Don't need to remove BOM
1453
 
                        return self._decode(infile, encoding)
1454
 
                    
1455
 
                # If we get this far, will *probably* raise a DecodeError
1456
 
                # As it doesn't appear to start with a BOM
1457
 
                return self._decode(infile, self.encoding)
1458
 
            
1459
 
            # Must be UTF8
1460
 
            BOM = BOM_SET[enc]
1461
 
            if not line.startswith(BOM):
1462
 
                return self._decode(infile, self.encoding)
1463
 
            
1464
 
            newline = line[len(BOM):]
1465
 
            
1466
 
            # BOM removed
1467
 
            if isinstance(infile, (list, tuple)):
1468
 
                infile[0] = newline
1469
 
            else:
1470
 
                infile = newline
1471
 
            self.BOM = True
1472
 
            return self._decode(infile, self.encoding)
1473
 
        
1474
 
        # No encoding specified - so we need to check for UTF8/UTF16
1475
 
        for BOM, (encoding, final_encoding) in BOMS.items():
1476
 
            if not line.startswith(BOM):
1477
 
                continue
1478
 
            else:
1479
 
                # BOM discovered
1480
 
                self.encoding = final_encoding
1481
 
                if not final_encoding:
1482
 
                    self.BOM = True
1483
 
                    # UTF8
1484
 
                    # remove BOM
1485
 
                    newline = line[len(BOM):]
1486
 
                    if isinstance(infile, (list, tuple)):
1487
 
                        infile[0] = newline
1488
 
                    else:
1489
 
                        infile = newline
1490
 
                    # UTF8 - don't decode
1491
 
                    if isinstance(infile, StringTypes):
1492
 
                        return infile.splitlines(True)
1493
 
                    else:
1494
 
                        return infile
1495
 
                # UTF16 - have to decode
1496
 
                return self._decode(infile, encoding)
1497
 
            
1498
 
        # No BOM discovered and no encoding specified, just return
1499
 
        if isinstance(infile, StringTypes):
1500
 
            # infile read from a file will be a single string
1501
 
            return infile.splitlines(True)
1502
 
        return infile
1503
 
 
1504
 
 
1505
 
    def _a_to_u(self, aString):
1506
 
        """Decode ASCII strings to unicode if a self.encoding is specified."""
1507
 
        if self.encoding:
1508
 
            return aString.decode('ascii')
1509
 
        else:
1510
 
            return aString
1511
 
 
1512
 
 
1513
 
    def _decode(self, infile, encoding):
1514
 
        """
1515
 
        Decode infile to unicode. Using the specified encoding.
1516
 
        
1517
 
        if is a string, it also needs converting to a list.
1518
 
        """
1519
 
        if isinstance(infile, StringTypes):
1520
 
            # can't be unicode
1521
 
            # NOTE: Could raise a ``UnicodeDecodeError``
1522
 
            return infile.decode(encoding).splitlines(True)
1523
 
        for i, line in enumerate(infile):
1524
 
            if not isinstance(line, unicode):
1525
 
                # NOTE: The isinstance test here handles mixed lists of unicode/string
1526
 
                # NOTE: But the decode will break on any non-string values
1527
 
                # NOTE: Or could raise a ``UnicodeDecodeError``
1528
 
                infile[i] = line.decode(encoding)
1529
 
        return infile
1530
 
 
1531
 
 
1532
 
    def _decode_element(self, line):
1533
 
        """Decode element to unicode if necessary."""
1534
 
        if not self.encoding:
1535
 
            return line
1536
 
        if isinstance(line, str) and self.default_encoding:
1537
 
            return line.decode(self.default_encoding)
1538
 
        return line
1539
 
 
1540
 
 
1541
 
    def _str(self, value):
1542
 
        """
1543
 
        Used by ``stringify`` within validate, to turn non-string values
1544
 
        into strings.
1545
 
        """
1546
 
        if not isinstance(value, StringTypes):
1547
 
            return str(value)
1548
 
        else:
1549
 
            return value
1550
 
 
1551
 
 
1552
 
    def _parse(self, infile):
1553
 
        """Actually parse the config file."""
1554
 
        temp_list_values = self.list_values
1555
 
        if self.unrepr:
1556
 
            self.list_values = False
1557
 
            
1558
 
        comment_list = []
1559
 
        done_start = False
1560
 
        this_section = self
1561
 
        maxline = len(infile) - 1
1562
 
        cur_index = -1
1563
 
        reset_comment = False
1564
 
        
1565
 
        while cur_index < maxline:
1566
 
            if reset_comment:
1567
 
                comment_list = []
1568
 
            cur_index += 1
1569
 
            line = infile[cur_index]
1570
 
            sline = line.strip()
1571
 
            # do we have anything on the line ?
1572
 
            if not sline or sline.startswith('#'):
1573
 
                reset_comment = False
1574
 
                comment_list.append(line)
1575
 
                continue
1576
 
            
1577
 
            if not done_start:
1578
 
                # preserve initial comment
1579
 
                self.initial_comment = comment_list
1580
 
                comment_list = []
1581
 
                done_start = True
1582
 
                
1583
 
            reset_comment = True
1584
 
            # first we check if it's a section marker
1585
 
            mat = self._sectionmarker.match(line)
1586
 
            if mat is not None:
1587
 
                # is a section line
1588
 
                (indent, sect_open, sect_name, sect_close, comment) = mat.groups()
1589
 
                if indent and (self.indent_type is None):
1590
 
                    self.indent_type = indent
1591
 
                cur_depth = sect_open.count('[')
1592
 
                if cur_depth != sect_close.count(']'):
1593
 
                    self._handle_error("Cannot compute the section depth at line %s.",
1594
 
                                       NestingError, infile, cur_index)
1595
 
                    continue
1596
 
                
1597
 
                if cur_depth < this_section.depth:
1598
 
                    # the new section is dropping back to a previous level
1599
 
                    try:
1600
 
                        parent = self._match_depth(this_section,
1601
 
                                                   cur_depth).parent
1602
 
                    except SyntaxError:
1603
 
                        self._handle_error("Cannot compute nesting level at line %s.",
1604
 
                                           NestingError, infile, cur_index)
1605
 
                        continue
1606
 
                elif cur_depth == this_section.depth:
1607
 
                    # the new section is a sibling of the current section
1608
 
                    parent = this_section.parent
1609
 
                elif cur_depth == this_section.depth + 1:
1610
 
                    # the new section is a child the current section
1611
 
                    parent = this_section
1612
 
                else:
1613
 
                    self._handle_error("Section too nested at line %s.",
1614
 
                                       NestingError, infile, cur_index)
1615
 
                    
1616
 
                sect_name = self._unquote(sect_name)
1617
 
                if parent.has_key(sect_name):
1618
 
                    self._handle_error('Duplicate section name at line %s.',
1619
 
                                       DuplicateError, infile, cur_index)
1620
 
                    continue
1621
 
                
1622
 
                # create the new section
1623
 
                this_section = Section(
1624
 
                    parent,
1625
 
                    cur_depth,
1626
 
                    self,
1627
 
                    name=sect_name)
1628
 
                parent[sect_name] = this_section
1629
 
                parent.inline_comments[sect_name] = comment
1630
 
                parent.comments[sect_name] = comment_list
1631
 
                continue
1632
 
            #
1633
 
            # it's not a section marker,
1634
 
            # so it should be a valid ``key = value`` line
1635
 
            mat = self._keyword.match(line)
1636
 
            if mat is None:
1637
 
                # it neither matched as a keyword
1638
 
                # or a section marker
1639
 
                self._handle_error(
1640
 
                    'Invalid line at line "%s".',
1641
 
                    ParseError, infile, cur_index)
1642
 
            else:
1643
 
                # is a keyword value
1644
 
                # value will include any inline comment
1645
 
                (indent, key, value) = mat.groups()
1646
 
                if indent and (self.indent_type is None):
1647
 
                    self.indent_type = indent
1648
 
                # check for a multiline value
1649
 
                if value[:3] in ['"""', "'''"]:
1650
 
                    try:
1651
 
                        (value, comment, cur_index) = self._multiline(
1652
 
                            value, infile, cur_index, maxline)
1653
 
                    except SyntaxError:
1654
 
                        self._handle_error(
1655
 
                            'Parse error in value at line %s.',
1656
 
                            ParseError, infile, cur_index)
1657
 
                        continue
1658
 
                    else:
1659
 
                        if self.unrepr:
1660
 
                            comment = ''
1661
 
                            try:
1662
 
                                value = unrepr(value)
1663
 
                            except Exception, e:
1664
 
                                if type(e) == UnknownType:
1665
 
                                    msg = 'Unknown name or type in value at line %s.'
1666
 
                                else:
1667
 
                                    msg = 'Parse error in value at line %s.'
1668
 
                                self._handle_error(msg, UnreprError, infile,
1669
 
                                    cur_index)
1670
 
                                continue
1671
 
                else:
1672
 
                    if self.unrepr:
1673
 
                        comment = ''
1674
 
                        try:
1675
 
                            value = unrepr(value)
1676
 
                        except Exception, e:
1677
 
                            if isinstance(e, UnknownType):
1678
 
                                msg = 'Unknown name or type in value at line %s.'
1679
 
                            else:
1680
 
                                msg = 'Parse error in value at line %s.'
1681
 
                            self._handle_error(msg, UnreprError, infile,
1682
 
                                cur_index)
1683
 
                            continue
1684
 
                    else:
1685
 
                        # extract comment and lists
1686
 
                        try:
1687
 
                            (value, comment) = self._handle_value(value)
1688
 
                        except SyntaxError:
1689
 
                            self._handle_error(
1690
 
                                'Parse error in value at line %s.',
1691
 
                                ParseError, infile, cur_index)
1692
 
                            continue
1693
 
                #
1694
 
                key = self._unquote(key)
1695
 
                if this_section.has_key(key):
1696
 
                    self._handle_error(
1697
 
                        'Duplicate keyword name at line %s.',
1698
 
                        DuplicateError, infile, cur_index)
1699
 
                    continue
1700
 
                # add the key.
1701
 
                # we set unrepr because if we have got this far we will never
1702
 
                # be creating a new section
1703
 
                this_section.__setitem__(key, value, unrepr=True)
1704
 
                this_section.inline_comments[key] = comment
1705
 
                this_section.comments[key] = comment_list
1706
 
                continue
1707
 
        #
1708
 
        if self.indent_type is None:
1709
 
            # no indentation used, set the type accordingly
1710
 
            self.indent_type = ''
1711
 
 
1712
 
        # preserve the final comment
1713
 
        if not self and not self.initial_comment:
1714
 
            self.initial_comment = comment_list
1715
 
        elif not reset_comment:
1716
 
            self.final_comment = comment_list
1717
 
        self.list_values = temp_list_values
1718
 
 
1719
 
 
1720
 
    def _match_depth(self, sect, depth):
1721
 
        """
1722
 
        Given a section and a depth level, walk back through the sections
1723
 
        parents to see if the depth level matches a previous section.
1724
 
        
1725
 
        Return a reference to the right section,
1726
 
        or raise a SyntaxError.
1727
 
        """
1728
 
        while depth < sect.depth:
1729
 
            if sect is sect.parent:
1730
 
                # we've reached the top level already
1731
 
                raise SyntaxError()
1732
 
            sect = sect.parent
1733
 
        if sect.depth == depth:
1734
 
            return sect
1735
 
        # shouldn't get here
1736
 
        raise SyntaxError()
1737
 
 
1738
 
 
1739
 
    def _handle_error(self, text, ErrorClass, infile, cur_index):
1740
 
        """
1741
 
        Handle an error according to the error settings.
1742
 
        
1743
 
        Either raise the error or store it.
1744
 
        The error will have occured at ``cur_index``
1745
 
        """
1746
 
        line = infile[cur_index]
1747
 
        cur_index += 1
1748
 
        message = text % cur_index
1749
 
        error = ErrorClass(message, cur_index, line)
1750
 
        if self.raise_errors:
1751
 
            # raise the error - parsing stops here
1752
 
            raise error
1753
 
        # store the error
1754
 
        # reraise when parsing has finished
1755
 
        self._errors.append(error)
1756
 
 
1757
 
 
1758
 
    def _unquote(self, value):
1759
 
        """Return an unquoted version of a value"""
1760
 
        if (value[0] == value[-1]) and (value[0] in ('"', "'")):
1761
 
            value = value[1:-1]
1762
 
        return value
1763
 
 
1764
 
 
1765
 
    def _quote(self, value, multiline=True):
1766
 
        """
1767
 
        Return a safely quoted version of a value.
1768
 
        
1769
 
        Raise a ConfigObjError if the value cannot be safely quoted.
1770
 
        If multiline is ``True`` (default) then use triple quotes
1771
 
        if necessary.
1772
 
        
1773
 
        Don't quote values that don't need it.
1774
 
        Recursively quote members of a list and return a comma joined list.
1775
 
        Multiline is ``False`` for lists.
1776
 
        Obey list syntax for empty and single member lists.
1777
 
        
1778
 
        If ``list_values=False`` then the value is only quoted if it contains
1779
 
        a ``\n`` (is multiline) or '#'.
1780
 
        
1781
 
        If ``write_empty_values`` is set, and the value is an empty string, it
1782
 
        won't be quoted.
1783
 
        """
1784
 
        if multiline and self.write_empty_values and value == '':
1785
 
            # Only if multiline is set, so that it is used for values not
1786
 
            # keys, and not values that are part of a list
1787
 
            return ''
1788
 
        
1789
 
        if multiline and isinstance(value, (list, tuple)):
1790
 
            if not value:
1791
 
                return ','
1792
 
            elif len(value) == 1:
1793
 
                return self._quote(value[0], multiline=False) + ','
1794
 
            return ', '.join([self._quote(val, multiline=False)
1795
 
                for val in value])
1796
 
        if not isinstance(value, StringTypes):
1797
 
            if self.stringify:
1798
 
                value = str(value)
1799
 
            else:
1800
 
                raise TypeError('Value "%s" is not a string.' % value)
1801
 
 
1802
 
        if not value:
1803
 
            return '""'
1804
 
        
1805
 
        no_lists_no_quotes = not self.list_values and '\n' not in value and '#' not in value
1806
 
        need_triple = multiline and ((("'" in value) and ('"' in value)) or ('\n' in value ))
1807
 
        hash_triple_quote = multiline and not need_triple and ("'" in value) and ('"' in value) and ('#' in value)
1808
 
        check_for_single = (no_lists_no_quotes or not need_triple) and not hash_triple_quote
1809
 
        
1810
 
        if check_for_single:
1811
 
            if not self.list_values:
1812
 
                # we don't quote if ``list_values=False``
1813
 
                quot = noquot
1814
 
            # for normal values either single or double quotes will do
1815
 
            elif '\n' in value:
1816
 
                # will only happen if multiline is off - e.g. '\n' in key
1817
 
                raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1818
 
            elif ((value[0] not in wspace_plus) and
1819
 
                    (value[-1] not in wspace_plus) and
1820
 
                    (',' not in value)):
1821
 
                quot = noquot
1822
 
            else:
1823
 
                quot = self._get_single_quote(value)
1824
 
        else:
1825
 
            # if value has '\n' or "'" *and* '"', it will need triple quotes
1826
 
            quot = self._get_triple_quote(value)
1827
 
        
1828
 
        if quot == noquot and '#' in value and self.list_values:
1829
 
            quot = self._get_single_quote(value)
1830
 
                
1831
 
        return quot % value
1832
 
    
1833
 
    
1834
 
    def _get_single_quote(self, value):
1835
 
        if ("'" in value) and ('"' in value):
1836
 
            raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1837
 
        elif '"' in value:
1838
 
            quot = squot
1839
 
        else:
1840
 
            quot = dquot
1841
 
        return quot
1842
 
    
1843
 
    
1844
 
    def _get_triple_quote(self, value):
1845
 
        if (value.find('"""') != -1) and (value.find("'''") != -1):
1846
 
            raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1847
 
        if value.find('"""') == -1:
1848
 
            quot = tdquot
1849
 
        else:
1850
 
            quot = tsquot 
1851
 
        return quot
1852
 
 
1853
 
 
1854
 
    def _handle_value(self, value):
1855
 
        """
1856
 
        Given a value string, unquote, remove comment,
1857
 
        handle lists. (including empty and single member lists)
1858
 
        """
1859
 
        # do we look for lists in values ?
1860
 
        if not self.list_values:
1861
 
            mat = self._nolistvalue.match(value)
1862
 
            if mat is None:
1863
 
                raise SyntaxError()
1864
 
            # NOTE: we don't unquote here
1865
 
            return mat.groups()
1866
 
        #
1867
 
        mat = self._valueexp.match(value)
1868
 
        if mat is None:
1869
 
            # the value is badly constructed, probably badly quoted,
1870
 
            # or an invalid list
1871
 
            raise SyntaxError()
1872
 
        (list_values, single, empty_list, comment) = mat.groups()
1873
 
        if (list_values == '') and (single is None):
1874
 
            # change this if you want to accept empty values
1875
 
            raise SyntaxError()
1876
 
        # NOTE: note there is no error handling from here if the regex
1877
 
        # is wrong: then incorrect values will slip through
1878
 
        if empty_list is not None:
1879
 
            # the single comma - meaning an empty list
1880
 
            return ([], comment)
1881
 
        if single is not None:
1882
 
            # handle empty values
1883
 
            if list_values and not single:
1884
 
                # FIXME: the '' is a workaround because our regex now matches
1885
 
                #   '' at the end of a list if it has a trailing comma
1886
 
                single = None
1887
 
            else:
1888
 
                single = single or '""'
1889
 
                single = self._unquote(single)
1890
 
        if list_values == '':
1891
 
            # not a list value
1892
 
            return (single, comment)
1893
 
        the_list = self._listvalueexp.findall(list_values)
1894
 
        the_list = [self._unquote(val) for val in the_list]
1895
 
        if single is not None:
1896
 
            the_list += [single]
1897
 
        return (the_list, comment)
1898
 
 
1899
 
 
1900
 
    def _multiline(self, value, infile, cur_index, maxline):
1901
 
        """Extract the value, where we are in a multiline situation."""
1902
 
        quot = value[:3]
1903
 
        newvalue = value[3:]
1904
 
        single_line = self._triple_quote[quot][0]
1905
 
        multi_line = self._triple_quote[quot][1]
1906
 
        mat = single_line.match(value)
1907
 
        if mat is not None:
1908
 
            retval = list(mat.groups())
1909
 
            retval.append(cur_index)
1910
 
            return retval
1911
 
        elif newvalue.find(quot) != -1:
1912
 
            # somehow the triple quote is missing
1913
 
            raise SyntaxError()
1914
 
        #
1915
 
        while cur_index < maxline:
1916
 
            cur_index += 1
1917
 
            newvalue += '\n'
1918
 
            line = infile[cur_index]
1919
 
            if line.find(quot) == -1:
1920
 
                newvalue += line
1921
 
            else:
1922
 
                # end of multiline, process it
1923
 
                break
1924
 
        else:
1925
 
            # we've got to the end of the config, oops...
1926
 
            raise SyntaxError()
1927
 
        mat = multi_line.match(line)
1928
 
        if mat is None:
1929
 
            # a badly formed line
1930
 
            raise SyntaxError()
1931
 
        (value, comment) = mat.groups()
1932
 
        return (newvalue + value, comment, cur_index)
1933
 
 
1934
 
 
1935
 
    def _handle_configspec(self, configspec):
1936
 
        """Parse the configspec."""
1937
 
        # FIXME: Should we check that the configspec was created with the 
1938
 
        #        correct settings ? (i.e. ``list_values=False``)
1939
 
        if not isinstance(configspec, ConfigObj):
1940
 
            try:
1941
 
                configspec = ConfigObj(configspec,
1942
 
                                       raise_errors=True,
1943
 
                                       file_error=True,
1944
 
                                       list_values=False)
1945
 
            except ConfigObjError, e:
1946
 
                # FIXME: Should these errors have a reference
1947
 
                #        to the already parsed ConfigObj ?
1948
 
                raise ConfigspecError('Parsing configspec failed: %s' % e)
1949
 
            except IOError, e:
1950
 
                raise IOError('Reading configspec failed: %s' % e)
1951
 
        
1952
 
        self._set_configspec_value(configspec, self)
1953
 
 
1954
 
 
1955
 
    def _set_configspec_value(self, configspec, section):
1956
 
        """Used to recursively set configspec values."""
1957
 
        if '__many__' in configspec.sections:
1958
 
            section.configspec['__many__'] = configspec['__many__']
1959
 
            if len(configspec.sections) > 1:
1960
 
                # FIXME: can we supply any useful information here ?
1961
 
                raise RepeatSectionError()
1962
 
            
1963
 
        if getattr(configspec, 'initial_comment', None) is not None:
1964
 
            section._configspec_initial_comment = configspec.initial_comment
1965
 
            section._configspec_final_comment = configspec.final_comment
1966
 
            section._configspec_encoding = configspec.encoding
1967
 
            section._configspec_BOM = configspec.BOM
1968
 
            section._configspec_newlines = configspec.newlines
1969
 
            section._configspec_indent_type = configspec.indent_type
1970
 
            
1971
 
        for entry in configspec.scalars:
1972
 
            section._configspec_comments[entry] = configspec.comments[entry]
1973
 
            section._configspec_inline_comments[entry] = configspec.inline_comments[entry]
1974
 
            section.configspec[entry] = configspec[entry]
1975
 
            section._order.append(entry)
1976
 
            
1977
 
        for entry in configspec.sections:
1978
 
            if entry == '__many__':
1979
 
                continue
1980
 
            
1981
 
            section._cs_section_comments[entry] = configspec.comments[entry]
1982
 
            section._cs_section_inline_comments[entry] = configspec.inline_comments[entry]
1983
 
            if not section.has_key(entry):
1984
 
                section[entry] = {}
1985
 
            self._set_configspec_value(configspec[entry], section[entry])
1986
 
 
1987
 
 
1988
 
    def _handle_repeat(self, section, configspec):
1989
 
        """Dynamically assign configspec for repeated section."""
1990
 
        try:
1991
 
            section_keys = configspec.sections
1992
 
            scalar_keys = configspec.scalars
1993
 
        except AttributeError:
1994
 
            section_keys = [entry for entry in configspec 
1995
 
                                if isinstance(configspec[entry], dict)]
1996
 
            scalar_keys = [entry for entry in configspec 
1997
 
                                if not isinstance(configspec[entry], dict)]
1998
 
            
1999
 
        if '__many__' in section_keys and len(section_keys) > 1:
2000
 
            # FIXME: can we supply any useful information here ?
2001
 
            raise RepeatSectionError()
2002
 
        
2003
 
        scalars = {}
2004
 
        sections = {}
2005
 
        for entry in scalar_keys:
2006
 
            val = configspec[entry]
2007
 
            scalars[entry] = val
2008
 
        for entry in section_keys:
2009
 
            val = configspec[entry]
2010
 
            if entry == '__many__':
2011
 
                scalars[entry] = val
2012
 
                continue
2013
 
            sections[entry] = val
2014
 
            
2015
 
        section.configspec = scalars
2016
 
        for entry in sections:
2017
 
            if not section.has_key(entry):
2018
 
                section[entry] = {}
2019
 
            self._handle_repeat(section[entry], sections[entry])
2020
 
 
2021
 
 
2022
 
    def _write_line(self, indent_string, entry, this_entry, comment):
2023
 
        """Write an individual line, for the write method"""
2024
 
        # NOTE: the calls to self._quote here handles non-StringType values.
2025
 
        if not self.unrepr:
2026
 
            val = self._decode_element(self._quote(this_entry))
2027
 
        else:
2028
 
            val = repr(this_entry)
2029
 
        return '%s%s%s%s%s' % (indent_string,
2030
 
                               self._decode_element(self._quote(entry, multiline=False)),
2031
 
                               self._a_to_u(' = '),
2032
 
                               val,
2033
 
                               self._decode_element(comment))
2034
 
 
2035
 
 
2036
 
    def _write_marker(self, indent_string, depth, entry, comment):
2037
 
        """Write a section marker line"""
2038
 
        return '%s%s%s%s%s' % (indent_string,
2039
 
                               self._a_to_u('[' * depth),
2040
 
                               self._quote(self._decode_element(entry), multiline=False),
2041
 
                               self._a_to_u(']' * depth),
2042
 
                               self._decode_element(comment))
2043
 
 
2044
 
 
2045
 
    def _handle_comment(self, comment):
2046
 
        """Deal with a comment."""
2047
 
        if not comment:
2048
 
            return ''
2049
 
        start = self.indent_type
2050
 
        if not comment.startswith('#'):
2051
 
            start += self._a_to_u(' # ')
2052
 
        return (start + comment)
2053
 
 
2054
 
 
2055
 
    # Public methods
2056
 
 
2057
 
    def write(self, outfile=None, section=None):
2058
 
        """
2059
 
        Write the current ConfigObj as a file
2060
 
        
2061
 
        tekNico: FIXME: use StringIO instead of real files
2062
 
        
2063
 
        >>> filename = a.filename
2064
 
        >>> a.filename = 'test.ini'
2065
 
        >>> a.write()
2066
 
        >>> a.filename = filename
2067
 
        >>> a == ConfigObj('test.ini', raise_errors=True)
2068
 
        1
2069
 
        """
2070
 
        if self.indent_type is None:
2071
 
            # this can be true if initialised from a dictionary
2072
 
            self.indent_type = DEFAULT_INDENT_TYPE
2073
 
            
2074
 
        out = []
2075
 
        cs = self._a_to_u('#')
2076
 
        csp = self._a_to_u('# ')
2077
 
        if section is None:
2078
 
            int_val = self.interpolation
2079
 
            self.interpolation = False
2080
 
            section = self
2081
 
            for line in self.initial_comment:
2082
 
                line = self._decode_element(line)
2083
 
                stripped_line = line.strip()
2084
 
                if stripped_line and not stripped_line.startswith(cs):
2085
 
                    line = csp + line
2086
 
                out.append(line)
2087
 
                
2088
 
        indent_string = self.indent_type * section.depth
2089
 
        for entry in (section.scalars + section.sections):
2090
 
            if entry in section.defaults:
2091
 
                # don't write out default values
2092
 
                continue
2093
 
            for comment_line in section.comments[entry]:
2094
 
                comment_line = self._decode_element(comment_line.lstrip())
2095
 
                if comment_line and not comment_line.startswith(cs):
2096
 
                    comment_line = csp + comment_line
2097
 
                out.append(indent_string + comment_line)
2098
 
            this_entry = section[entry]
2099
 
            comment = self._handle_comment(section.inline_comments[entry])
2100
 
            
2101
 
            if isinstance(this_entry, dict):
2102
 
                # a section
2103
 
                out.append(self._write_marker(
2104
 
                    indent_string,
2105
 
                    this_entry.depth,
2106
 
                    entry,
2107
 
                    comment))
2108
 
                out.extend(self.write(section=this_entry))
2109
 
            else:
2110
 
                out.append(self._write_line(
2111
 
                    indent_string,
2112
 
                    entry,
2113
 
                    this_entry,
2114
 
                    comment))
2115
 
                
2116
 
        if section is self:
2117
 
            for line in self.final_comment:
2118
 
                line = self._decode_element(line)
2119
 
                stripped_line = line.strip()
2120
 
                if stripped_line and not stripped_line.startswith(cs):
2121
 
                    line = csp + line
2122
 
                out.append(line)
2123
 
            self.interpolation = int_val
2124
 
            
2125
 
        if section is not self:
2126
 
            return out
2127
 
        
2128
 
        if (self.filename is None) and (outfile is None):
2129
 
            # output a list of lines
2130
 
            # might need to encode
2131
 
            # NOTE: This will *screw* UTF16, each line will start with the BOM
2132
 
            if self.encoding:
2133
 
                out = [l.encode(self.encoding) for l in out]
2134
 
            if (self.BOM and ((self.encoding is None) or
2135
 
                (BOM_LIST.get(self.encoding.lower()) == 'utf_8'))):
2136
 
                # Add the UTF8 BOM
2137
 
                if not out:
2138
 
                    out.append('')
2139
 
                out[0] = BOM_UTF8 + out[0]
2140
 
            return out
2141
 
        
2142
 
        # Turn the list to a string, joined with correct newlines
2143
 
        newline = self.newlines or os.linesep
2144
 
        output = self._a_to_u(newline).join(out)
2145
 
        if self.encoding:
2146
 
            output = output.encode(self.encoding)
2147
 
        if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)):
2148
 
            # Add the UTF8 BOM
2149
 
            output = BOM_UTF8 + output
2150
 
            
2151
 
        if not output.endswith(newline):
2152
 
            output += newline
2153
 
        if outfile is not None:
2154
 
            outfile.write(output)
2155
 
        else:
2156
 
            h = open(self.filename, 'wb')
2157
 
            h.write(output)
2158
 
            h.close()
2159
 
 
2160
 
 
2161
 
    def validate(self, validator, preserve_errors=False, copy=False,
2162
 
                 section=None):
2163
 
        """
2164
 
        Test the ConfigObj against a configspec.
2165
 
        
2166
 
        It uses the ``validator`` object from *validate.py*.
2167
 
        
2168
 
        To run ``validate`` on the current ConfigObj, call: ::
2169
 
        
2170
 
            test = config.validate(validator)
2171
 
        
2172
 
        (Normally having previously passed in the configspec when the ConfigObj
2173
 
        was created - you can dynamically assign a dictionary of checks to the
2174
 
        ``configspec`` attribute of a section though).
2175
 
        
2176
 
        It returns ``True`` if everything passes, or a dictionary of
2177
 
        pass/fails (True/False). If every member of a subsection passes, it
2178
 
        will just have the value ``True``. (It also returns ``False`` if all
2179
 
        members fail).
2180
 
        
2181
 
        In addition, it converts the values from strings to their native
2182
 
        types if their checks pass (and ``stringify`` is set).
2183
 
        
2184
 
        If ``preserve_errors`` is ``True`` (``False`` is default) then instead
2185
 
        of a marking a fail with a ``False``, it will preserve the actual
2186
 
        exception object. This can contain info about the reason for failure.
2187
 
        For example the ``VdtValueTooSmallError`` indicates that the value
2188
 
        supplied was too small. If a value (or section) is missing it will
2189
 
        still be marked as ``False``.
2190
 
        
2191
 
        You must have the validate module to use ``preserve_errors=True``.
2192
 
        
2193
 
        You can then use the ``flatten_errors`` function to turn your nested
2194
 
        results dictionary into a flattened list of failures - useful for
2195
 
        displaying meaningful error messages.
2196
 
        """
2197
 
        if section is None:
2198
 
            if self.configspec is None:
2199
 
                raise ValueError('No configspec supplied.')
2200
 
            if preserve_errors:
2201
 
                # We do this once to remove a top level dependency on the validate module
2202
 
                # Which makes importing configobj faster
2203
 
                from validate import VdtMissingValue
2204
 
                self._vdtMissingValue = VdtMissingValue
2205
 
            section = self
2206
 
        #
2207
 
        spec_section = section.configspec
2208
 
        if copy and getattr(section, '_configspec_initial_comment', None) is not None:
2209
 
            section.initial_comment = section._configspec_initial_comment
2210
 
            section.final_comment = section._configspec_final_comment
2211
 
            section.encoding = section._configspec_encoding
2212
 
            section.BOM = section._configspec_BOM
2213
 
            section.newlines = section._configspec_newlines
2214
 
            section.indent_type = section._configspec_indent_type
2215
 
            
2216
 
        if '__many__' in section.configspec:
2217
 
            many = spec_section['__many__']
2218
 
            # dynamically assign the configspecs
2219
 
            # for the sections below
2220
 
            for entry in section.sections:
2221
 
                self._handle_repeat(section[entry], many)
2222
 
        #
2223
 
        out = {}
2224
 
        ret_true = True
2225
 
        ret_false = True
2226
 
        order = [k for k in section._order if k in spec_section]
2227
 
        order += [k for k in spec_section if k not in order]
2228
 
        for entry in order:
2229
 
            if entry == '__many__':
2230
 
                continue
2231
 
            if (not entry in section.scalars) or (entry in section.defaults):
2232
 
                # missing entries
2233
 
                # or entries from defaults
2234
 
                missing = True
2235
 
                val = None
2236
 
                if copy and not entry in section.scalars:
2237
 
                    # copy comments
2238
 
                    section.comments[entry] = (
2239
 
                        section._configspec_comments.get(entry, []))
2240
 
                    section.inline_comments[entry] = (
2241
 
                        section._configspec_inline_comments.get(entry, ''))
2242
 
                #
2243
 
            else:
2244
 
                missing = False
2245
 
                val = section[entry]
2246
 
            try:
2247
 
                check = validator.check(spec_section[entry],
2248
 
                                        val,
2249
 
                                        missing=missing
2250
 
                                        )
2251
 
            except validator.baseErrorClass, e:
2252
 
                if not preserve_errors or isinstance(e, self._vdtMissingValue):
2253
 
                    out[entry] = False
2254
 
                else:
2255
 
                    # preserve the error
2256
 
                    out[entry] = e
2257
 
                    ret_false = False
2258
 
                ret_true = False
2259
 
            else:
2260
 
                try: 
2261
 
                    section.default_values.pop(entry, None)
2262
 
                except AttributeError: 
2263
 
                    # For Python 2.2 compatibility
2264
 
                    try:
2265
 
                        del section.default_values[entry]
2266
 
                    except KeyError:
2267
 
                        pass
2268
 
                    
2269
 
                if getattr(validator, 'get_default_value', None) is not None:
2270
 
                    try: 
2271
 
                        section.default_values[entry] = validator.get_default_value(spec_section[entry])
2272
 
                    except KeyError:
2273
 
                        # No default
2274
 
                        pass
2275
 
                    
2276
 
                ret_false = False
2277
 
                out[entry] = True
2278
 
                if self.stringify or missing:
2279
 
                    # if we are doing type conversion
2280
 
                    # or the value is a supplied default
2281
 
                    if not self.stringify:
2282
 
                        if isinstance(check, (list, tuple)):
2283
 
                            # preserve lists
2284
 
                            check = [self._str(item) for item in check]
2285
 
                        elif missing and check is None:
2286
 
                            # convert the None from a default to a ''
2287
 
                            check = ''
2288
 
                        else:
2289
 
                            check = self._str(check)
2290
 
                    if (check != val) or missing:
2291
 
                        section[entry] = check
2292
 
                if not copy and missing and entry not in section.defaults:
2293
 
                    section.defaults.append(entry)
2294
 
        # Missing sections will have been created as empty ones when the
2295
 
        # configspec was read.
2296
 
        for entry in section.sections:
2297
 
            # FIXME: this means DEFAULT is not copied in copy mode
2298
 
            if section is self and entry == 'DEFAULT':
2299
 
                continue
2300
 
            if copy:
2301
 
                section.comments[entry] = section._cs_section_comments[entry]
2302
 
                section.inline_comments[entry] = (
2303
 
                    section._cs_section_inline_comments[entry])
2304
 
            check = self.validate(validator, preserve_errors=preserve_errors,
2305
 
                copy=copy, section=section[entry])
2306
 
            out[entry] = check
2307
 
            if check == False:
2308
 
                ret_true = False
2309
 
            elif check == True:
2310
 
                ret_false = False
2311
 
            else:
2312
 
                ret_true = False
2313
 
                ret_false = False
2314
 
        #
2315
 
        if ret_true:
2316
 
            return True
2317
 
        elif ret_false:
2318
 
            return False
2319
 
        return out
2320
 
 
2321
 
 
2322
 
    def reset(self):
2323
 
        """Clear ConfigObj instance and restore to 'freshly created' state."""
2324
 
        self.clear()
2325
 
        self._initialise()
2326
 
        # FIXME: Should be done by '_initialise', but ConfigObj constructor (and reload)
2327
 
        #        requires an empty dictionary
2328
 
        self.configspec = None
2329
 
        # Just to be sure ;-)
2330
 
        self._original_configspec = None
2331
 
        
2332
 
        
2333
 
    def reload(self):
2334
 
        """
2335
 
        Reload a ConfigObj from file.
2336
 
        
2337
 
        This method raises a ``ReloadError`` if the ConfigObj doesn't have
2338
 
        a filename attribute pointing to a file.
2339
 
        """
2340
 
        if not isinstance(self.filename, StringTypes):
2341
 
            raise ReloadError()
2342
 
 
2343
 
        filename = self.filename
2344
 
        current_options = {}
2345
 
        for entry in OPTION_DEFAULTS:
2346
 
            if entry == 'configspec':
2347
 
                continue
2348
 
            current_options[entry] = getattr(self, entry)
2349
 
            
2350
 
        configspec = self._original_configspec
2351
 
        current_options['configspec'] = configspec
2352
 
            
2353
 
        self.clear()
2354
 
        self._initialise(current_options)
2355
 
        self._load(filename, configspec)
2356
 
        
2357
 
 
2358
 
 
2359
 
class SimpleVal(object):
2360
 
    """
2361
 
    A simple validator.
2362
 
    Can be used to check that all members expected are present.
2363
 
    
2364
 
    To use it, provide a configspec with all your members in (the value given
2365
 
    will be ignored). Pass an instance of ``SimpleVal`` to the ``validate``
2366
 
    method of your ``ConfigObj``. ``validate`` will return ``True`` if all
2367
 
    members are present, or a dictionary with True/False meaning
2368
 
    present/missing. (Whole missing sections will be replaced with ``False``)
2369
 
    """
2370
 
    
2371
 
    def __init__(self):
2372
 
        self.baseErrorClass = ConfigObjError
2373
 
    
2374
 
    def check(self, check, member, missing=False):
2375
 
        """A dummy check method, always returns the value unchanged."""
2376
 
        if missing:
2377
 
            raise self.baseErrorClass()
2378
 
        return member
2379
 
 
2380
 
 
2381
 
# Check / processing functions for options
2382
 
def flatten_errors(cfg, res, levels=None, results=None):
2383
 
    """
2384
 
    An example function that will turn a nested dictionary of results
2385
 
    (as returned by ``ConfigObj.validate``) into a flat list.
2386
 
    
2387
 
    ``cfg`` is the ConfigObj instance being checked, ``res`` is the results
2388
 
    dictionary returned by ``validate``.
2389
 
    
2390
 
    (This is a recursive function, so you shouldn't use the ``levels`` or
2391
 
    ``results`` arguments - they are used by the function.
2392
 
    
2393
 
    Returns a list of keys that failed. Each member of the list is a tuple :
2394
 
    ::
2395
 
    
2396
 
        ([list of sections...], key, result)
2397
 
    
2398
 
    If ``validate`` was called with ``preserve_errors=False`` (the default)
2399
 
    then ``result`` will always be ``False``.
2400
 
 
2401
 
    *list of sections* is a flattened list of sections that the key was found
2402
 
    in.
2403
 
    
2404
 
    If the section was missing then key will be ``None``.
2405
 
    
2406
 
    If the value (or section) was missing then ``result`` will be ``False``.
2407
 
    
2408
 
    If ``validate`` was called with ``preserve_errors=True`` and a value
2409
 
    was present, but failed the check, then ``result`` will be the exception
2410
 
    object returned. You can use this as a string that describes the failure.
2411
 
    
2412
 
    For example *The value "3" is of the wrong type*.
2413
 
    
2414
 
    >>> import validate
2415
 
    >>> vtor = validate.Validator()
2416
 
    >>> my_ini = '''
2417
 
    ...     option1 = True
2418
 
    ...     [section1]
2419
 
    ...     option1 = True
2420
 
    ...     [section2]
2421
 
    ...     another_option = Probably
2422
 
    ...     [section3]
2423
 
    ...     another_option = True
2424
 
    ...     [[section3b]]
2425
 
    ...     value = 3
2426
 
    ...     value2 = a
2427
 
    ...     value3 = 11
2428
 
    ...     '''
2429
 
    >>> my_cfg = '''
2430
 
    ...     option1 = boolean()
2431
 
    ...     option2 = boolean()
2432
 
    ...     option3 = boolean(default=Bad_value)
2433
 
    ...     [section1]
2434
 
    ...     option1 = boolean()
2435
 
    ...     option2 = boolean()
2436
 
    ...     option3 = boolean(default=Bad_value)
2437
 
    ...     [section2]
2438
 
    ...     another_option = boolean()
2439
 
    ...     [section3]
2440
 
    ...     another_option = boolean()
2441
 
    ...     [[section3b]]
2442
 
    ...     value = integer
2443
 
    ...     value2 = integer
2444
 
    ...     value3 = integer(0, 10)
2445
 
    ...         [[[section3b-sub]]]
2446
 
    ...         value = string
2447
 
    ...     [section4]
2448
 
    ...     another_option = boolean()
2449
 
    ...     '''
2450
 
    >>> cs = my_cfg.split('\\n')
2451
 
    >>> ini = my_ini.split('\\n')
2452
 
    >>> cfg = ConfigObj(ini, configspec=cs)
2453
 
    >>> res = cfg.validate(vtor, preserve_errors=True)
2454
 
    >>> errors = []
2455
 
    >>> for entry in flatten_errors(cfg, res):
2456
 
    ...     section_list, key, error = entry
2457
 
    ...     section_list.insert(0, '[root]')
2458
 
    ...     if key is not None:
2459
 
    ...        section_list.append(key)
2460
 
    ...     else:
2461
 
    ...         section_list.append('[missing]')
2462
 
    ...     section_string = ', '.join(section_list)
2463
 
    ...     errors.append((section_string, ' = ', error))
2464
 
    >>> errors.sort()
2465
 
    >>> for entry in errors:
2466
 
    ...     print entry[0], entry[1], (entry[2] or 0)
2467
 
    [root], option2  =  0
2468
 
    [root], option3  =  the value "Bad_value" is of the wrong type.
2469
 
    [root], section1, option2  =  0
2470
 
    [root], section1, option3  =  the value "Bad_value" is of the wrong type.
2471
 
    [root], section2, another_option  =  the value "Probably" is of the wrong type.
2472
 
    [root], section3, section3b, section3b-sub, [missing]  =  0
2473
 
    [root], section3, section3b, value2  =  the value "a" is of the wrong type.
2474
 
    [root], section3, section3b, value3  =  the value "11" is too big.
2475
 
    [root], section4, [missing]  =  0
2476
 
    """
2477
 
    if levels is None:
2478
 
        # first time called
2479
 
        levels = []
2480
 
        results = []
2481
 
    if res is True:
2482
 
        return results
2483
 
    if res is False:
2484
 
        results.append((levels[:], None, False))
2485
 
        if levels:
2486
 
            levels.pop()
2487
 
        return results
2488
 
    for (key, val) in res.items():
2489
 
        if val == True:
2490
 
            continue
2491
 
        if isinstance(cfg.get(key), dict):
2492
 
            # Go down one level
2493
 
            levels.append(key)
2494
 
            flatten_errors(cfg[key], val, levels, results)
2495
 
            continue
2496
 
        results.append((levels[:], key, val))
2497
 
    #
2498
 
    # Go up one level
2499
 
    if levels:
2500
 
        levels.pop()
2501
 
    #
2502
 
    return results
2503
 
 
2504
 
 
2505
 
"""*A programming language is a medium of expression.* - Paul Graham"""