39
42
from types import StringTypes
41
# the UTF8 BOM - from codecs module
42
BOM_UTF8 = '\xef\xbb\xbf'
46
__revision__ = '$Id: configobj.py 135 2005-10-12 14:52:28Z fuzzyman $'
43
from warnings import warn
44
from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE
46
# A dictionary mapping BOM to
47
# the encoding to decode with, and what to set the
48
# encoding attribute to.
50
BOM_UTF8: ('utf_8', None),
51
BOM_UTF16_BE: ('utf16_be', 'utf_16'),
52
BOM_UTF16_LE: ('utf16_le', 'utf_16'),
53
BOM_UTF16: ('utf_16', 'utf_16'),
55
# All legal variants of the BOM codecs.
56
# TODO: the list of aliases is not meant to be exhaustive, is there a
63
'utf16_be': 'utf16_be',
64
'utf_16_be': 'utf16_be',
65
'utf-16be': 'utf16_be',
66
'utf16_le': 'utf16_le',
67
'utf_16_le': 'utf16_le',
68
'utf-16le': 'utf16_le',
76
# Map of encodings to the BOM to write.
80
'utf16_be': BOM_UTF16_BE,
81
'utf16_le': BOM_UTF16_LE,
86
from validate import VdtMissingValue
88
VdtMissingValue = None
94
"""enumerate for Python 2.2."""
106
__version__ = '4.2.0beta2'
108
__revision__ = '$Id: configobj.py 156 2006-01-31 14:57:08Z fuzzyman $'
48
110
__docformat__ = "restructuredtext en"
112
# NOTE: Does it make sense to have the following in __all__ ?
113
# NOTE: DEFAULT_INDENT_TYPE, NUM_INDENT_SPACES, MAX_INTERPOL_DEPTH
114
# NOTE: If used as from configobj import...
115
# NOTE: They are effectively read only
53
118
'DEFAULT_INDENT_TYPE',
54
119
'NUM_INDENT_SPACES',
55
120
'MAX_INTERPOL_DEPTH',
507
602
return value when called on the whole subsection has to be discarded.
509
604
See the encode and decode methods for examples, including functions.
608
You can use ``walk`` to transform the names of members of a section
609
but you mustn't add or delete members.
611
>>> config = '''[XXXXsection]
612
... XXXXkey = XXXXvalue'''.splitlines()
613
>>> cfg = ConfigObj(config)
615
{'XXXXsection': {'XXXXkey': 'XXXXvalue'}}
616
>>> def transform(section, key):
617
... val = section[key]
618
... newkey = key.replace('XXXX', 'CLIENT1')
619
... section.rename(key, newkey)
620
... if isinstance(val, (tuple, list, dict)):
623
... val = val.replace('XXXX', 'CLIENT1')
624
... section[newkey] = val
625
>>> cfg.walk(transform, call_on_sections=True)
626
{'CLIENT1section': {'CLIENT1key': None}}
628
{'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}}
513
for entry in self.scalars[:]:
632
for i in range(len(self.scalars)):
633
entry = self.scalars[i]
515
out[entry] = function(self, entry, **keywargs)
635
val = function(self, entry, **keywargs)
636
# bound again in case name has changed
637
entry = self.scalars[i]
516
639
except Exception:
643
entry = self.scalars[i]
520
644
out[entry] = False
522
for entry in self.sections[:]:
646
for i in range(len(self.sections)):
647
entry = self.sections[i]
523
648
if call_on_sections:
525
650
function(self, entry, **keywargs)
602
730
section[newkey] = newval
603
731
self.walk(encode, call_on_sections=True)
733
def istrue(self, key):
734
"""A deprecated version of ``as_bool``."""
735
warn('use of ``istrue`` is deprecated. Use ``as_bool`` method '
736
'instead.', DeprecationWarning)
737
return self.as_bool(key)
739
def as_bool(self, key):
741
Accepts a key as input. The corresponding value must be a string or
742
the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to
743
retain compatibility with Python 2.2.
745
If the string is one of ``True``, ``On``, ``Yes``, or ``1`` it returns
748
If the string is one of ``False``, ``Off``, ``No``, or ``0`` it returns
751
``as_bool`` is not case sensitive.
753
Any other input will raise a ``ValueError``.
758
Traceback (most recent call last):
759
ValueError: Value "fish" is neither True nor False
774
if not isinstance(val, StringTypes):
777
return self.main._bools[val.lower()]
779
raise ValueError('Value "%s" is neither True nor False' % val)
781
def as_int(self, key):
783
A convenience method which coerces the specified value to an integer.
785
If the value is an invalid literal for ``int``, a ``ValueError`` will
791
Traceback (most recent call last):
792
ValueError: invalid literal for int(): fish
798
Traceback (most recent call last):
799
ValueError: invalid literal for int(): 3.2
801
return int(self[key])
803
def as_float(self, key):
805
A convenience method which coerces the specified value to a float.
807
If the value is an invalid literal for ``float``, a ``ValueError`` will
813
Traceback (most recent call last):
814
ValueError: invalid literal for float(): fish
822
return float(self[key])
605
825
class ConfigObj(Section):
607
827
An object to read, create, and write config files.
754
984
self.file_error = defaults['file_error']
755
985
self.stringify = defaults['stringify']
756
986
self.indent_type = defaults['indent_type']
757
# used by the write method
987
self.encoding = defaults['encoding']
988
self.default_encoding = defaults['default_encoding']
760
992
self.initial_comment = []
761
993
self.final_comment = []
763
995
if isinstance(infile, StringTypes):
764
self.filename = os.path.abspath(infile)
765
if os.path.isfile(self.filename):
766
infile = open(self.filename).readlines()
996
self.filename = infile
997
if os.path.isfile(infile):
998
infile = open(infile).read() or []
767
999
elif self.file_error:
768
1000
# raise an error if the file doesn't exist
769
1001
raise IOError, 'Config file not found: "%s".' % self.filename
786
1018
infile = infile.dict()
787
1019
for entry in infile:
788
1020
self[entry] = infile[entry]
790
1021
del self._errors
791
1022
if defaults['configspec'] is not None:
792
1023
self._handle_configspec(defaults['configspec'])
794
1025
self.configspec = None
796
elif hasattr(infile, 'seek'):
797
# this supports StringIO instances and even file objects
798
self.filename = infile
800
infile = infile.readlines()
801
self.filename.seek(0)
1027
elif hasattr(infile, 'read'):
1028
# This supports file like objects
1029
infile = infile.read() or []
1030
# needs splitting into lines - but needs doing *after* decoding
1031
# in case it's not an 8 bit encoding
803
1033
raise TypeError, ('infile must be a filename,'
804
' StringIO instance, or a file as a list.')
806
# strip trailing '\n' from lines
807
infile = [line.rstrip('\n') for line in infile]
809
# remove the UTF8 BOM if it is there
810
# FIXME: support other BOM
811
if infile and infile[0].startswith(BOM_UTF8):
812
infile[0] = infile[0][3:]
1034
' file like object, or list of lines.')
1037
# don't do it for the empty ConfigObj
1038
infile = self._handle_bom(infile)
1039
# infile is now *always* a list
1041
# Set the newlines attribute (first line ending it finds)
1042
# and strip trailing '\n' or '\r' from lines
1044
if (not line) or (line[-1] not in '\r\n'):
1046
for end in ('\r\n', '\n', '\r'):
1047
if line.endswith(end):
1051
infile = [line.rstrip('\r\n') for line in infile]
817
1053
self._parse(infile)
818
1054
# if we had any errors, now is the time to raise them
833
1069
self._handle_configspec(defaults['configspec'])
1071
def _handle_bom(self, infile):
1073
Handle any BOM, and decode if necessary.
1075
If an encoding is specified, that *must* be used - but the BOM should
1076
still be removed (and the BOM attribute set).
1078
(If the encoding is wrongly specified, then a BOM for an alternative
1079
encoding won't be discovered or removed.)
1081
If an encoding is not specified, UTF8 or UTF16 BOM will be detected and
1082
removed. The BOM attribute will be set. UTF16 will be decoded to
1085
NOTE: This method must not be called with an empty ``infile``.
1087
Specifying the *wrong* encoding is likely to cause a
1088
``UnicodeDecodeError``.
1090
``infile`` must always be returned as a list of lines, but may be
1091
passed in as a single string.
1093
if ((self.encoding is not None) and
1094
(self.encoding.lower() not in BOM_LIST)):
1095
# No need to check for a BOM
1096
# encoding specified doesn't have one
1098
return self._decode(infile, self.encoding)
1100
if isinstance(infile, (list, tuple)):
1104
if self.encoding is not None:
1105
# encoding explicitly supplied
1106
# And it could have an associated BOM
1107
# TODO: if encoding is just UTF16 - we ought to check for both
1108
# TODO: big endian and little endian versions.
1109
enc = BOM_LIST[self.encoding.lower()]
1111
# For UTF16 we try big endian and little endian
1112
for BOM, (encoding, final_encoding) in BOMS.items():
1113
if not final_encoding:
1116
if infile.startswith(BOM):
1119
# Don't need to remove BOM
1120
return self._decode(infile, encoding)
1122
# If we get this far, will *probably* raise a DecodeError
1123
# As it doesn't appear to start with a BOM
1124
return self._decode(infile, self.encoding)
1128
if not line.startswith(BOM):
1129
return self._decode(infile, self.encoding)
1131
newline = line[len(BOM):]
1134
if isinstance(infile, (list, tuple)):
1139
return self._decode(infile, self.encoding)
1141
# No encoding specified - so we need to check for UTF8/UTF16
1142
for BOM, (encoding, final_encoding) in BOMS.items():
1143
if not line.startswith(BOM):
1147
self.encoding = final_encoding
1148
if not final_encoding:
1152
newline = line[len(BOM):]
1153
if isinstance(infile, (list, tuple)):
1157
# UTF8 - don't decode
1158
if isinstance(infile, StringTypes):
1159
return infile.splitlines(True)
1162
# UTF16 - have to decode
1163
return self._decode(infile, encoding)
1165
# No BOM discovered and no encoding specified, just return
1166
if isinstance(infile, StringTypes):
1167
# infile read from a file will be a single string
1168
return infile.splitlines(True)
1172
def _a_to_u(self, string):
1173
"""Decode ascii strings to unicode if a self.encoding is specified."""
1174
if not self.encoding:
1177
return string.decode('ascii')
1179
def _decode(self, infile, encoding):
1181
Decode infile to unicode. Using the specified encoding.
1183
if is a string, it also needs converting to a list.
1185
if isinstance(infile, StringTypes):
1187
# NOTE: Could raise a ``UnicodeDecodeError``
1188
return infile.decode(encoding).splitlines(True)
1189
for i, line in enumerate(infile):
1190
if not isinstance(line, unicode):
1191
# NOTE: The isinstance test here handles mixed lists of unicode/string
1192
# NOTE: But the decode will break on any non-string values
1193
# NOTE: Or could raise a ``UnicodeDecodeError``
1194
infile[i] = line.decode(encoding)
1197
def _decode_element(self, line):
1198
"""Decode element to unicode if necessary."""
1199
if not self.encoding:
1201
if isinstance(line, str) and self.default_encoding:
1202
return line.decode(self.default_encoding)
1205
def _str(self, value):
1207
Used by ``stringify`` within validate, to turn non-string values
1210
if not isinstance(value, StringTypes):
835
1215
def _parse(self, infile):
837
1217
Actually parse the config file
1393
1783
def _write_line(self, indent_string, entry, this_entry, comment):
1394
1784
"""Write an individual line, for the write method"""
1395
return '%s%s = %s%s' % (
1785
# NOTE: the calls to self._quote here handles non-StringType values.
1786
return '%s%s%s%s%s' % (
1397
self._quote(entry, multiline=False),
1398
self._quote(this_entry),
1788
self._decode_element(self._quote(entry, multiline=False)),
1789
self._a_to_u(' = '),
1790
self._decode_element(self._quote(this_entry)),
1791
self._decode_element(comment))
1401
1793
def _write_marker(self, indent_string, depth, entry, comment):
1402
1794
"""Write a section marker line"""
1403
1795
return '%s%s%s%s%s' % (
1406
self._quote(entry, multiline=False),
1797
self._a_to_u('[' * depth),
1798
self._quote(self._decode_element(entry), multiline=False),
1799
self._a_to_u(']' * depth),
1800
self._decode_element(comment))
1410
1802
def _handle_comment(self, comment):
1489
1881
['a = %(a)s', '[DEFAULT]', 'a = fish']
1492
1883
if self.indent_type is None:
1493
1884
# this can be true if initialised from a dictionary
1494
1885
self.indent_type = DEFAULT_INDENT_TYPE
1888
cs = self._a_to_u('#')
1889
csp = self._a_to_u('# ')
1498
1890
if section is None:
1499
1891
int_val = self.interpolation
1500
1892
self.interpolation = False
1503
1894
for line in self.initial_comment:
1895
line = self._decode_element(line)
1504
1896
stripped_line = line.strip()
1505
if stripped_line and not stripped_line.startswith('#'):
1897
if stripped_line and not stripped_line.startswith(cs):
1507
1899
out.append(line)
1509
indent_string = self._compute_indent_string(section.depth)
1901
indent_string = self._a_to_u(
1902
self._compute_indent_string(section.depth))
1510
1903
for entry in (section.scalars + section.sections):
1511
1904
if entry in section.defaults:
1512
1905
# don't write out default values
1514
1907
for comment_line in section.comments[entry]:
1515
comment_line = comment_line.lstrip()
1516
if comment_line and not comment_line.startswith('#'):
1517
comment_line = '#' + comment_line
1908
comment_line = self._decode_element(comment_line.lstrip())
1909
if comment_line and not comment_line.startswith(cs):
1910
comment_line = csp + comment_line
1518
1911
out.append(indent_string + comment_line)
1519
1912
this_entry = section[entry]
1520
1913
comment = self._handle_comment(section.inline_comments[entry])
1538
1931
for line in self.final_comment:
1932
line = self._decode_element(line)
1539
1933
stripped_line = line.strip()
1540
if stripped_line and not stripped_line.startswith('#'):
1934
if stripped_line and not stripped_line.startswith(cs):
1542
1936
out.append(line)
1544
if int_val != 'test':
1545
1937
self.interpolation = int_val
1547
if (return_list) or (self.filename is None):
1550
if isinstance(self.filename, StringTypes):
1939
if section is not self:
1942
if (self.filename is None) and (outfile is None):
1943
# output a list of lines
1944
# might need to encode
1945
# NOTE: This will *screw* UTF16, each line will start with the BOM
1947
out = [l.encode(self.encoding) for l in out]
1948
if (self.BOM and ((self.encoding is None) or
1949
(BOM_LIST.get(self.encoding.lower()) == 'utf_8'))):
1953
out[0] = BOM_UTF8 + out[0]
1956
# Turn the list to a string, joined with correct newlines
1957
output = (self._a_to_u(self.newlines or os.linesep)
1960
output = output.encode(self.encoding)
1961
if (self.BOM and ((self.encoding is None) or
1962
(BOM_LIST.get(self.encoding.lower()) == 'utf_8'))):
1964
output = BOM_UTF8 + output
1965
if outfile is not None:
1966
outfile.write(output)
1551
1968
h = open(self.filename, 'w')
1552
h.write(self.BOM or '')
1553
h.write('\n'.join(out))
1556
self.filename.seek(0)
1557
self.filename.write(self.BOM or '')
1558
self.filename.write('\n'.join(out))
1559
# if we have a stored file object (or StringIO)
1560
# we *don't* close it
1562
def validate(self, validator, section=None):
1972
def validate(self, validator, preserve_errors=False, section=None):
1564
1974
Test the ConfigObj against a configspec.
1581
1991
In addition, it converts the values from strings to their native
1582
1992
types if their checks pass (and ``stringify`` is set).
1994
If ``preserve_errors`` is ``True`` (``False`` is default) then instead
1995
of a marking a fail with a ``False``, it will preserve the actual
1996
exception object. This can contain info about the reason for failure.
1997
For example the ``VdtValueTooSmallError`` indeicates that the value
1998
supplied was too small. If a value (or section) is missing it will
1999
still be marked as ``False``.
2001
You must have the validate module to use ``preserve_errors=True``.
2003
You can then use the ``flatten_errors`` function to turn your nested
2004
results dictionary into a flattened list of failures - useful for
2005
displaying meaningful error messages.
1585
2008
... from validate import Validator
1586
2009
... except ImportError:
1587
... print >> sys.stderr, 'Cannot import the Validator object, skipping the realted tests'
2010
... print >> sys.stderr, 'Cannot import the Validator object, skipping the related tests'
1589
2012
... config = '''
1670
2093
>>> val_res == {'key2': True, 'section': True, 'key': False}
1672
2095
>>> configspec = '''
1673
... test1='integer(30,50, default=40)'
1674
... test2='string(default="hello")'
1675
... test3='integer(default=3)'
1676
... test4='float(6.0, default=6.0)'
2096
... test1=integer(30,50, default=40)
2097
... test2=string(default="hello")
2098
... test3=integer(default=3)
2099
... test4=float(6.0, default=6.0)
1678
... test1='integer(30,50, default=40)'
1679
... test2='string(default="hello")'
1680
... test3='integer(default=3)'
1681
... test4='float(6.0, default=6.0)'
2101
... test1=integer(30,50, default=40)
2102
... test2=string(default="hello")
2103
... test3=integer(default=3)
2104
... test4=float(6.0, default=6.0)
1682
2105
... [[sub section]]
1683
... test1='integer(30,50, default=40)'
1684
... test2='string(default="hello")'
1685
... test3='integer(default=3)'
1686
... test4='float(6.0, default=6.0)'
2106
... test1=integer(30,50, default=40)
2107
... test2=string(default="hello")
2108
... test3=integer(default=3)
2109
... test4=float(6.0, default=6.0)
1687
2110
... '''.split('\\n')
1688
2111
>>> default_test = ConfigObj(['test1=30'], configspec=configspec)
1689
2112
>>> default_test
1990
2430
if not self.stringify:
1991
2431
if isinstance(check, (list, tuple)):
1992
2432
# preserve lists
1993
check = [str(item) for item in check]
2433
check = [self._str(item) for item in check]
1994
2434
elif missing and check is None:
1995
2435
# convert the None from a default to a ''
2438
check = self._str(check)
1999
2439
if (check != val) or missing:
2000
2440
section[entry] = check
2001
2441
if missing and entry not in section.defaults:
2002
2442
section.defaults.append(entry)
2444
# FIXME: Will this miss missing sections ?
2004
2445
for entry in section.sections:
2005
check = self.validate(validator, section[entry])
2446
if section is self and entry == 'DEFAULT':
2448
check = self.validate(validator, preserve_errors=preserve_errors,
2449
section=section[entry])
2006
2450
out[entry] = check
2007
2451
if check == False:
2008
2452
ret_true = False
2080
2524
raise self.baseErrorClass
2527
# Check / processing functions for options
2528
def flatten_errors(cfg, res, levels=None, results=None):
2530
An example function that will turn a nested dictionary of results
2531
(as returned by ``ConfigObj.validate``) into a flat list.
2533
``cfg`` is the ConfigObj instance being checked, ``res`` is the results
2534
dictionary returned by ``validate``.
2536
(This is a recursive function, so you shouldn't use the ``levels`` or
2537
``results`` arguments - they are used by the function.
2539
Returns a list of keys that failed. Each member of the list is a tuple :
2542
([list of sections...], key, result)
2544
If ``validate`` was called with ``preserve_errors=False`` (the default)
2545
then ``result`` will always be ``False``.
2547
*list of sections* is a flattened list of sections that the key was found
2550
If the section was missing then key will be ``None``.
2552
If the value (or section) was missing then ``result`` will be ``False``.
2554
If ``validate`` was called with ``preserve_errors=True`` and a value
2555
was present, but failed the check, then ``result`` will be the exception
2556
object returned. You can use this as a string that describes the failure.
2558
For example *The value "3" is of the wrong type*.
2560
# FIXME: is the ordering of the output arbitrary ?
2562
>>> vtor = validate.Validator()
2568
... another_option = Probably
2570
... another_option = True
2577
... option1 = boolean()
2578
... option2 = boolean()
2579
... option3 = boolean(default=Bad_value)
2581
... option1 = boolean()
2582
... option2 = boolean()
2583
... option3 = boolean(default=Bad_value)
2585
... another_option = boolean()
2587
... another_option = boolean()
2590
... value2 = integer
2591
... value3 = integer(0, 10)
2592
... [[[section3b-sub]]]
2595
... another_option = boolean()
2597
>>> cs = my_cfg.split('\\n')
2598
>>> ini = my_ini.split('\\n')
2599
>>> cfg = ConfigObj(ini, configspec=cs)
2600
>>> res = cfg.validate(vtor, preserve_errors=True)
2602
>>> for entry in flatten_errors(cfg, res):
2603
... section_list, key, error = entry
2604
... section_list.insert(0, '[root]')
2605
... if key is not None:
2606
... section_list.append(key)
2608
... section_list.append('[missing]')
2609
... section_string = ', '.join(section_list)
2610
... errors.append((section_string, ' = ', error))
2612
>>> for entry in errors:
2613
... print entry[0], entry[1], (entry[2] or 0)
2615
[root], option3 = the value "Bad_value" is of the wrong type.
2616
[root], section1, option2 = 0
2617
[root], section1, option3 = the value "Bad_value" is of the wrong type.
2618
[root], section2, another_option = the value "Probably" is of the wrong type.
2619
[root], section3, section3b, section3b-sub, [missing] = 0
2620
[root], section3, section3b, value2 = the value "a" is of the wrong type.
2621
[root], section3, section3b, value3 = the value "11" is too big.
2622
[root], section4, [missing] = 0
2631
results.append((levels[:], None, False))
2635
for (key, val) in res.items():
2638
if isinstance(cfg.get(key), dict):
2641
flatten_errors(cfg[key], val, levels, results)
2643
results.append((levels[:], key, val))
2083
2652
# FIXME: test error code for badly built multiline values
2084
2653
# FIXME: test handling of StringIO
2085
2654
# FIXME: test interpolation with writing
2146
2715
>>> t2.inline_comments['b'] = ''
2147
2716
>>> del t2['a']
2148
2717
>>> assert t2.write() == ['','b = b', '']
2719
# Test ``list_values=False`` stuff
2721
... key1 = no quotes
2722
... key2 = 'single quotes'
2723
... key3 = "double quotes"
2724
... key4 = "list", 'with', several, "quotes"
2726
>>> cfg = ConfigObj(c.splitlines(), list_values=False)
2727
>>> cfg == {'key1': 'no quotes', 'key2': "'single quotes'",
2728
... 'key3': '"double quotes"',
2729
... 'key4': '"list", \\'with\\', several, "quotes"'
2732
>>> cfg = ConfigObj(list_values=False)
2733
>>> cfg['key1'] = 'Multiline\\nValue'
2734
>>> cfg['key2'] = '''"Value" with 'quotes' !'''
2736
["key1 = '''Multiline\\nValue'''", 'key2 = "Value" with \\'quotes\\' !']
2737
>>> cfg.list_values = True
2738
>>> cfg.write() == ["key1 = '''Multiline\\nValue'''",
2739
... 'key2 = \\'\\'\\'"Value" with \\'quotes\\' !\\'\\'\\'']
2742
Test flatten_errors:
2744
>>> from validate import Validator, VdtValueTooSmallError
2760
... '''.split('\\n')
2761
>>> configspec = '''
2762
... test1= integer(30,50)
2765
... test4=float(6.0)
2767
... test1=integer(30,50)
2770
... test4=float(6.0)
2772
... test1=integer(30,50)
2775
... test4=float(6.0)
2776
... '''.split('\\n')
2777
>>> val = Validator()
2778
>>> c1 = ConfigObj(config, configspec=configspec)
2779
>>> res = c1.validate(val)
2780
>>> flatten_errors(c1, res) == [([], 'test4', False), (['section',
2781
... 'sub section'], 'test4', False), (['section'], 'test4', False)]
2783
>>> res = c1.validate(val, preserve_errors=True)
2784
>>> check = flatten_errors(c1, res)
2788
(['section', 'sub section'], 'test4')
2790
(['section'], 'test4')
2791
>>> for entry in check:
2792
... isinstance(entry[2], VdtValueTooSmallError)
2793
... print str(entry[2])
2795
the value "5.0" is too small.
2797
the value "5.0" is too small.
2799
the value "5.0" is too small.
2801
Test unicode handling, BOM, write witha file like object and line endings :
2803
... # initial comment
2804
... # inital comment 2
2806
... test1 = some value
2808
... test2 = another value # inline comment
2809
... # section comment
2810
... [section] # inline comment
2811
... test = test # another inline comment
2815
... # final comment2
2817
>>> u = u_base.encode('utf_8').splitlines(True)
2818
>>> u[0] = BOM_UTF8 + u[0]
2819
>>> uc = ConfigObj(u)
2820
>>> uc.encoding = None
2823
>>> uc == {'test1': 'some value', 'test2': 'another value',
2824
... 'section': {'test': 'test', 'test2': 'test2'}}
2826
>>> uc = ConfigObj(u, encoding='utf_8', default_encoding='latin-1')
2829
>>> isinstance(uc['test1'], unicode)
2835
>>> uc['latin1'] = "This costs lot's of "
2836
>>> a_list = uc.write()
2839
>>> isinstance(a_list[0], str)
2841
>>> a_list[0].startswith(BOM_UTF8)
2843
>>> u = u_base.replace('\\n', '\\r\\n').encode('utf_8').splitlines(True)
2844
>>> uc = ConfigObj(u)
2847
>>> uc.newlines = '\\r'
2848
>>> from cStringIO import StringIO
2849
>>> file_like = StringIO()
2850
>>> uc.write(file_like)
2851
>>> file_like.seek(0)
2852
>>> uc2 = ConfigObj(file_like)
2855
>>> uc2.filename == None
2857
>>> uc2.newlines == '\\r'
2151
2861
if __name__ == '__main__':
2238
With list values off, ConfigObj can incorrectly unquote values. (This makes
2239
it impossible to use listquote to handle your list values for you - for
2240
nested lists. Not handling quotes at all would be better for this)
2953
Better support for configuration from multiple files, including tracking
2954
*where* the original file came from and writing changes to the correct
2958
Make ``newline`` an option (as well as an attribute) ?
2960
``UTF16`` encoded files, when returned as a list of lines, will have the
2961
BOM at the start of every line. Should this be removed from all but the
2964
Option to set warning type for unicode decode ? (Defaults to strict).
2245
2966
A method to optionally remove uniform indentation from multiline values.
2246
2967
(do as an example of using ``walk`` - along with string-escape)
2969
Should the results dictionary from validate be an ordered dictionary if
2970
`odict <http://www.voidspace.org.uk/python/odict.html>`_ is available ?
2972
Implement a better ``__repr__`` ? (``ConfigObj({})``)
2974
Implement some of the sequence methods (which include slicing) from the
2248
2977
INCOMPATIBLE CHANGES
2249
2978
====================
3110
Removed ``BOM_UTF8`` from ``__all__``.
3112
The ``BOM`` attribute has become a boolean. (Defaults to ``False``.) It is
3113
*only* ``True`` for the ``UTF16`` encoding.
3115
File like objects no longer need a ``seek`` attribute.
3117
ConfigObj no longer keeps a reference to file like objects. Instead the
3118
``write`` method takes a file like object as an optional argument. (Which
3119
will be used in preference of the ``filename`` attribute if htat exists as
3122
Full unicode support added. New options/attributes ``encoding``,
3123
``default_encoding``.
3125
utf16 files decoded to unicode.
3127
If ``BOM`` is ``True``, but no encoding specified, then the utf8 BOM is
3128
written out at the start of the file. (It will normally only be ``True`` if
3129
the utf8 BOM was found when the file was read.)
3131
File paths are *not* converted to absolute paths, relative paths will
3132
remain relative as the ``filename`` attribute.
3134
Fixed bug where ``final_comment`` wasn't returned if ``write`` is returning
3140
Added ``True``, ``False``, and ``enumerate`` if they are not defined.
3141
(``True`` and ``False`` are needed for *early* versions of Python 2.2,
3142
``enumerate`` is needed for all versions ofPython 2.2)
3144
Deprecated ``istrue``, replaced it with ``as_bool``.
3146
Added ``as_int`` and ``as_float``.
3148
utf8 and utf16 BOM handled in an endian agnostic way.
3153
Validation no longer done on the 'DEFAULT' section (only in the root
3154
level). This allows interpolation in configspecs.
3156
Change in validation syntax implemented in validate 0.2.1
3163
Added ``merge``, a recursive update.
3165
Added ``preserve_errors`` to ``validate`` and the ``flatten_errors``
3168
Thanks to Matthew Brett for suggestions and helping me iron out bugs.
3170
Fixed bug where a config file is *all* comment, the comment will now be
3171
``initial_comment`` rather than ``final_comment``.
3176
Fixed bug in ``create_empty``. Thanks to Paul Jimenez for the report.
3181
Fixed bug in ``Section.walk`` when transforming names as well as values.
3183
Added the ``istrue`` method. (Fetches the boolean equivalent of a string
3186
Fixed ``list_values=False`` - they are now only quoted/unquoted if they
3187
are multiline values.
3189
List values are written as ``item, item`` rather than ``item,item``.
2381
3196
Fixed typo in ``write`` method. (Testing for the wrong value when resetting
2382
3197
``interpolation``).