summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfuzzyman <devnull@localhost>2009-04-13 21:48:54 +0000
committerfuzzyman <devnull@localhost>2009-04-13 21:48:54 +0000
commit108e4e2b5101dd4b2a6ea1556bbad6bd8b8cf93d (patch)
tree36c7907b1f5e3b3c71ba9ac0a029224dc4401bf9
parentf535ca5aa5d5a444b48d837a5f5e5178568c9856 (diff)
downloadconfigobj-git-108e4e2b5101dd4b2a6ea1556bbad6bd8b8cf93d.tar.gz
Various minor doc, test and code changes
-rw-r--r--configobj.py112
-rw-r--r--docs/configobj.txt49
-rw-r--r--docs/validate.txt10
-rw-r--r--test_configobj.py17
-rw-r--r--validate.py76
5 files changed, 116 insertions, 148 deletions
diff --git a/configobj.py b/configobj.py
index 7675f84..90fcdc8 100644
--- a/configobj.py
+++ b/configobj.py
@@ -30,7 +30,6 @@ except ImportError:
# for IronPython
pass
-from warnings import warn
try:
from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE
@@ -816,7 +815,7 @@ class Section(dict):
>>> c2 = ConfigObj(a)
>>> c2.merge(c1)
>>> c2
- {'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}}
+ ConfigObj({'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}})
"""
for key, val in indict.items():
if (key in self and isinstance(self[key], dict) and
@@ -888,7 +887,7 @@ class Section(dict):
... XXXXkey = XXXXvalue'''.splitlines()
>>> cfg = ConfigObj(config)
>>> cfg
- {'XXXXsection': {'XXXXkey': 'XXXXvalue'}}
+ ConfigObj({'XXXXsection': {'XXXXkey': 'XXXXvalue'}})
>>> def transform(section, key):
... val = section[key]
... newkey = key.replace('XXXX', 'CLIENT1')
@@ -901,7 +900,7 @@ class Section(dict):
>>> cfg.walk(transform, call_on_sections=True)
{'CLIENT1section': {'CLIENT1key': None}}
>>> cfg
- {'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}}
+ ConfigObj({'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}})
"""
out = {}
# scalars first
@@ -941,83 +940,6 @@ class Section(dict):
return out
- def decode(self, encoding):
- """
- Decode all strings and values to unicode, using the specified encoding.
-
- Works with subsections and list values.
-
- Uses the ``walk`` method.
-
- Testing ``encode`` and ``decode``.
- >>> m = ConfigObj(a)
- >>> m.decode('ascii')
- >>> def testuni(val):
- ... for entry in val:
- ... if not isinstance(entry, unicode):
- ... print >> sys.stderr, type(entry)
- ... raise AssertionError, 'decode failed.'
- ... if isinstance(val[entry], dict):
- ... testuni(val[entry])
- ... elif not isinstance(val[entry], unicode):
- ... raise AssertionError, 'decode failed.'
- >>> testuni(m)
- >>> m.encode('ascii')
- >>> a == m
- 1
- """
- warn('use of ``decode`` is deprecated.', DeprecationWarning)
- def decode(section, key, encoding=encoding, warn=True):
- """ """
- val = section[key]
- if isinstance(val, (list, tuple)):
- newval = []
- for entry in val:
- newval.append(entry.decode(encoding))
- elif isinstance(val, dict):
- newval = val
- else:
- newval = val.decode(encoding)
- newkey = key.decode(encoding)
- section.rename(key, newkey)
- section[newkey] = newval
- # using ``call_on_sections`` allows us to modify section names
- self.walk(decode, call_on_sections=True)
-
-
- def encode(self, encoding):
- """
- Encode all strings and values from unicode,
- using the specified encoding.
-
- Works with subsections and list values.
- Uses the ``walk`` method.
- """
- warn('use of ``encode`` is deprecated.', DeprecationWarning)
- def encode(section, key, encoding=encoding):
- """ """
- val = section[key]
- if isinstance(val, (list, tuple)):
- newval = []
- for entry in val:
- newval.append(entry.encode(encoding))
- elif isinstance(val, dict):
- newval = val
- else:
- newval = val.encode(encoding)
- newkey = key.encode(encoding)
- section.rename(key, newkey)
- section[newkey] = newval
- self.walk(encode, call_on_sections=True)
-
-
- def istrue(self, key):
- """A deprecated version of ``as_bool``."""
- warn('use of ``istrue`` is deprecated. Use ``as_bool`` method '
- 'instead.', DeprecationWarning)
- return self.as_bool(key)
-
-
def as_bool(self, key):
"""
Accepts a key as input. The corresponding value must be a string or
@@ -1073,14 +995,14 @@ class Section(dict):
>>> a['a'] = 'fish'
>>> a.as_int('a')
Traceback (most recent call last):
- ValueError: invalid literal for int(): fish
+ ValueError: invalid literal for int() with base 10: 'fish'
>>> a['b'] = '1'
>>> a.as_int('b')
1
>>> a['b'] = '3.2'
>>> a.as_int('b')
Traceback (most recent call last):
- ValueError: invalid literal for int(): 3.2
+ ValueError: invalid literal for int() with base 10: '3.2'
"""
return int(self[key])
@@ -1105,7 +1027,29 @@ class Section(dict):
3.2000000000000002
"""
return float(self[key])
-
+
+
+ def as_list(self, key):
+ """
+ A convenience method which fetches the specified value, guaranteeing
+ that it is a list.
+
+ >>> a = ConfigObj()
+ >>> a['a'] = 1
+ >>> a.as_list('a')
+ [1]
+ >>> a['a'] = (1,)
+ >>> a.as_list('a')
+ [1]
+ >>> a['a'] = [1]
+ >>> a.as_list('a')
+ [1]
+ """
+ result = self[key]
+ if isinstance(result, (tuple, list)):
+ return list(result)
+ return [result]
+
def restore_default(self, key):
"""
diff --git a/docs/configobj.txt b/docs/configobj.txt
index cec093c..1cb79c5 100644
--- a/docs/configobj.txt
+++ b/docs/configobj.txt
@@ -1274,9 +1274,6 @@ Section Methods
This method renames a key, without affecting its position in the sequence.
- It is mainly implemented for the ``encode`` and ``decode`` methods, which
- provide some Unicode support.
-
* **merge**
``merge(indict)``
@@ -1315,24 +1312,6 @@ Section Methods
This method can be used to transform values and names. See `walking a
section`_ for examples and explanation.
-* **decode**
-
- ``decode(encoding)``
-
- This method decodes names and values into Unicode objects, using the
- supplied encoding.
-
-* **encode**
-
- ``encode(encoding)``
-
- This method is the opposite of ``decode`` {sm;:!:}.
-
- It encodes names and values using the supplied encoding. If any of your
- names/values are strings rather than Unicode, Python will have to do an
- implicit decode first. (This method uses ``sys.defaultencoding`` for
- implicit decodes.)
-
* **as_bool**
``as_bool(key)``
@@ -1353,11 +1332,6 @@ Section Methods
false, no, off, 0
- .. note::
-
- In ConfigObj 4.1.0, this method was called ``istrue``. That method is
- now deprecated and will issue a warning when used. It will go away
- in a future release.
* **as_int**
@@ -1367,6 +1341,7 @@ Section Methods
It raises a ``ValueError`` if the conversion can't be done.
+
* **as_float**
``as_float(key)``
@@ -1375,6 +1350,17 @@ Section Methods
It raises a ``ValueError`` if the conversion can't be done.
+
+* **as_list**
+
+ ``as_list(key)``
+
+ This returns the value contained in the specified key as a list.
+
+ If it isn't a list it will be wrapped as a list so that you can
+ guarantee the returned value will be a list.
+
+
* **restore_default**
``restore_default(key)``
@@ -1451,13 +1437,6 @@ note that walk discards the return value when it calls your function.
Examples
--------
-Examples that use the walk method are the ``encode`` and ``decode`` methods.
-They both define a function and pass it to walk. Because these functions
-transform names as well as values (from byte strings to Unicode) they set
-``call_on_sections=True``.
-
-To see how they do it, *read the source Luke* {sm;:cool:}.
-
You can use this for transforming all values in your ConfigObj. For example
you might like the nested lists from ConfigObj 3. This was provided by the
listquote_ module. You could switch off the parsing for list values
@@ -1533,7 +1512,6 @@ change the values if appropriate.
# Because ``walk`` doesn't recognise the ``encode`` argument
# it passes it to our function.
config.walk(string_escape, encode=True)
-
{-coloring}
Here's a simple example of using ``walk`` to transform names and values. One
@@ -2345,6 +2323,9 @@ From version 4 it lists all releases and changes.
* You can now have normal sections inside configspec sections that use __many__
* You can now create an empty ConfigObj with a configspec, programmatically set values and then validate
* A section that was supplied as a value (or vice-versa) in the actual config file would cause an exception during validation (the config file is still broken of course, but it is now handled gracefully)
+* Added ``as_list`` method
+* Removed the deprecated ``istrue``, ``encode`` and ``decode`` methods
+* Running test_configobj now also runs the doctests in the configobj module
As a consequence of the changes to configspec handling, when you create a ConfigObj instance and provide a configspec, the configspec attribute is only set on the ConfigObj instance - it isn't set on the sections until you validate. You also can't set the configspec attribute to be a dictionary. This wasn't documented but did work previously.
diff --git a/docs/validate.txt b/docs/validate.txt
index cbb3ce2..45522a9 100644
--- a/docs/validate.txt
+++ b/docs/validate.txt
@@ -8,7 +8,7 @@
:Authors: `Michael Foord`_, `Nicola Larosa`_, `Mark Andrews`_
-:Version: Validate 0.3.2
+:Version: Validate 1.0.0
:Date: 2008/02/24
:Homepage: `Validate Homepage`_
:License: `BSD License`_
@@ -614,6 +614,14 @@ to specify arguments for 'mixed_lists'.
CHANGELOG
=========
+2009/04/13 - Version 1.0.0
+--------------------------
+
+BUGFIX: can now handle multiline strings.
+
+As there are no known bugs or outstanding feature requests I am marking this 1.0.
+
+
2008/02/24 - Version 0.3.2
--------------------------
diff --git a/test_configobj.py b/test_configobj.py
index 51697b5..2d105a0 100644
--- a/test_configobj.py
+++ b/test_configobj.py
@@ -1984,6 +1984,20 @@ def _test_pickle():
>>> new.validate(v)
1
"""
+
+def _test_as_list():
+ """
+ >>> a = ConfigObj()
+ >>> a['a'] = 1
+ >>> a.as_list('a')
+ [1]
+ >>> a['a'] = (1,)
+ >>> a.as_list('a')
+ [1]
+ >>> a['a'] = [1]
+ >>> a.as_list('a')
+ [1]
+ """
# drop support for Python 2.2 ?
@@ -2080,6 +2094,9 @@ if __name__ == '__main__':
'oneTabCfg': oneTabCfg, 'twoTabsCfg': twoTabsCfg,
'tabsAndSpacesCfg': tabsAndSpacesCfg})
doctest.testmod(m, globs=globs)
+
+ import configobj
+ doctest.testmod(configobj, globs=globs)
# Man alive I prefer unittest ;-) \ No newline at end of file
diff --git a/validate.py b/validate.py
index a5512e8..aeba0dc 100644
--- a/validate.py
+++ b/validate.py
@@ -167,13 +167,7 @@ __all__ = (
)
-import sys
-INTP_VER = sys.version_info[:2]
-if INTP_VER < (2, 2):
- raise RuntimeError("Python v.2.2 or later needed")
-
import re
-StringTypes = (str, unicode)
_list_arg = re.compile(r'''
@@ -197,7 +191,7 @@ _list_arg = re.compile(r'''
)
\)
)
-''', re.VERBOSE) # two groups
+''', re.VERBOSE | re.DOTALL) # two groups
_list_members = re.compile(r'''
(
@@ -208,7 +202,7 @@ _list_members = re.compile(r'''
(?:
(?:\s*,\s*)|(?:\s*$) # comma
)
-''', re.VERBOSE) # one group
+''', re.VERBOSE | re.DOTALL) # one group
_paramstring = r'''
(?:
@@ -475,9 +469,9 @@ class Validator(object):
... # check that value is of the correct type.
... # possible valid inputs are integers or strings
... # that represent integers
- ... if not isinstance(value, (int, long, StringTypes)):
+ ... if not isinstance(value, (int, long, basestring)):
... raise VdtTypeError(value)
- ... elif isinstance(value, StringTypes):
+ ... elif isinstance(value, basestring):
... # if we are given a string
... # attempt to convert to an integer
... try:
@@ -525,10 +519,10 @@ class Validator(object):
"""
# this regex does the initial parsing of the checks
- _func_re = re.compile(r'(.+?)\((.*)\)')
+ _func_re = re.compile(r'(.+?)\((.*)\)', re.DOTALL)
# this regex takes apart keyword arguments
- _key_arg = re.compile(r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.*)$')
+ _key_arg = re.compile(r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.*)$', re.DOTALL)
# this regex finds keyword=list(....) type values
@@ -539,8 +533,8 @@ class Validator(object):
# These regexes check a set of arguments for validity
# and then pull the members out
- _paramfinder = re.compile(_paramstring, re.VERBOSE)
- _matchfinder = re.compile(_matchstring, re.VERBOSE)
+ _paramfinder = re.compile(_paramstring, re.VERBOSE | re.DOTALL)
+ _matchfinder = re.compile(_matchstring, re.VERBOSE | re.DOTALL)
def __init__(self, functions=None):
@@ -756,7 +750,7 @@ def _is_num_param(names, values, to_float=False):
for (name, val) in zip(names, values):
if val is None:
out_params.append(val)
- elif isinstance(val, (int, long, float, StringTypes)):
+ elif isinstance(val, (int, long, float, basestring)):
try:
out_params.append(fun(val))
except ValueError, e:
@@ -813,9 +807,9 @@ def is_integer(value, min=None, max=None):
0
"""
(min_val, max_val) = _is_num_param(('min', 'max'), (min, max))
- if not isinstance(value, (int, long, StringTypes)):
+ if not isinstance(value, (int, long, basestring)):
raise VdtTypeError(value)
- if isinstance(value, StringTypes):
+ if isinstance(value, basestring):
# if it's a string - does it represent an integer ?
try:
value = int(value)
@@ -865,7 +859,7 @@ def is_float(value, min=None, max=None):
"""
(min_val, max_val) = _is_num_param(
('min', 'max'), (min, max), to_float=True)
- if not isinstance(value, (int, long, float, StringTypes)):
+ if not isinstance(value, (int, long, float, basestring)):
raise VdtTypeError(value)
if not isinstance(value, float):
# if it's a string - does it represent a float ?
@@ -930,7 +924,7 @@ def is_boolean(value):
VdtTypeError: the value "up" is of the wrong type.
"""
- if isinstance(value, StringTypes):
+ if isinstance(value, basestring):
try:
return bool_dict[value.lower()]
except KeyError:
@@ -973,7 +967,7 @@ def is_ip_addr(value):
Traceback (most recent call last):
VdtTypeError: the value "0" is of the wrong type.
"""
- if not isinstance(value, StringTypes):
+ if not isinstance(value, basestring):
raise VdtTypeError(value)
value = value.strip()
try:
@@ -1015,7 +1009,7 @@ def is_list(value, min=None, max=None):
VdtTypeError: the value "12" is of the wrong type.
"""
(min_len, max_len) = _is_num_param(('min', 'max'), (min, max))
- if isinstance(value, StringTypes):
+ if isinstance(value, basestring):
raise VdtTypeError(value)
try:
num_members = len(value)
@@ -1084,7 +1078,7 @@ def is_string(value, min=None, max=None):
Traceback (most recent call last):
VdtValueTooLongError: the value "1234" is too long.
"""
- if not isinstance(value, StringTypes):
+ if not isinstance(value, basestring):
raise VdtTypeError(value)
(min_len, max_len) = _is_num_param(('min', 'max'), (min, max))
try:
@@ -1190,7 +1184,7 @@ def is_string_list(value, min=None, max=None):
Traceback (most recent call last):
VdtTypeError: the value "hello" is of the wrong type.
"""
- if isinstance(value, StringTypes):
+ if isinstance(value, basestring):
raise VdtTypeError(value)
return [is_string(mem) for mem in is_list(value, min, max)]
@@ -1272,10 +1266,7 @@ def is_mixed_list(value, *args):
... 'yoda',
... '" for parameter "mixed_list".',
... )
- >>> if INTP_VER == (2, 2):
- ... res_str = "".join(res_seq)
- ... else:
- ... res_str = "'".join(res_seq)
+ >>> res_str = "'".join(res_seq)
>>> try:
... vtor.check('mixed_list("yoda")', ('a'))
... except VdtParamError, err:
@@ -1309,7 +1300,7 @@ def is_option(value, *options):
Traceback (most recent call last):
VdtTypeError: the value "0" is of the wrong type.
"""
- if not isinstance(value, StringTypes):
+ if not isinstance(value, basestring):
raise VdtTypeError(value)
if not value in options:
raise VdtValueError(value)
@@ -1410,14 +1401,41 @@ def _test2():
3
"""
+def _test3():
+ r"""
+ >>> vtor.check('string(default="")', '', missing=True)
+ ''
+ >>> vtor.check('string(default="\n")', '', missing=True)
+ '\n'
+ >>> print vtor.check('string(default="\n")', '', missing=True),
+ <BLANKLINE>
+ >>> vtor.check('string()', '\n')
+ '\n'
+ >>> vtor.check('string(default="\n\n\n")', '', missing=True)
+ '\n\n\n'
+ >>> vtor.check('string()', 'random \n text goes here\n\n')
+ 'random \n text goes here\n\n'
+ >>> vtor.check('string(default=" \nrandom text\ngoes \n here\n\n ")',
+ ... '', missing=True)
+ ' \nrandom text\ngoes \n here\n\n '
+ >>> vtor.check("string(default='\n\n\n')", '', missing=True)
+ '\n\n\n'
+ >>> vtor.check("option('\n','a','b',default='\n')", '', missing=True)
+ '\n'
+ >>> vtor.check("string_list()", ['foo', '\n', 'bar'])
+ ['foo', '\n', 'bar']
+ >>> vtor.check("string_list(default=list('\n'))", '', missing=True)
+ ['\n']
+ """
+
if __name__ == '__main__':
# run the code tests in doctest format
+ import sys
import doctest
m = sys.modules.get('__main__')
globs = m.__dict__.copy()
globs.update({
- 'INTP_VER': INTP_VER,
'vtor': Validator(),
})
doctest.testmod(m, globs=globs)