summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore36
-rw-r--r--LICENSE39
-rw-r--r--README.md4
-rw-r--r--configobj.py128
-rw-r--r--configobj.wpr17
-rw-r--r--extras/ConfigPersist.py242
-rw-r--r--extras/configpersist.txt266
-rw-r--r--functionaltests/test_configobj.py2
-rw-r--r--test_configobj.py281
-rw-r--r--validate.py32
10 files changed, 302 insertions, 745 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ded6067
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,36 @@
+*.py[cod]
+
+# C extensions
+*.so
+
+# Packages
+*.egg
+*.egg-info
+dist
+build
+eggs
+parts
+bin
+var
+sdist
+develop-eggs
+.installed.cfg
+lib
+lib64
+__pycache__
+
+# Installer logs
+pip-log.txt
+
+# Unit test / coverage reports
+.coverage
+.tox
+nosetests.xml
+
+# Translations
+*.mo
+
+# Mr Developer
+.mr.developer.cfg
+.project
+.pydevproject
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..a989b12
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,39 @@
+Copyright (c):
+2003-2010, Michael Foord
+2014, Eli Courtwright, Rob Dennis
+All rights reserved.
+E-mails :
+fuzzyman AT voidspace DOT org DOT uk
+eli AT courtwright DOT org
+rdennis AT gmail DOT com
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the names of Michael Foord, Eli Courtwright or Rob Dennis,
+ nor the name of Voidspace, may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3313554
--- /dev/null
+++ b/README.md
@@ -0,0 +1,4 @@
+configobj
+=========
+
+Python 3+ compatible port of the configobj library
diff --git a/configobj.py b/configobj.py
index c1f6e6d..f438ba2 100644
--- a/configobj.py
+++ b/configobj.py
@@ -16,7 +16,7 @@
# http://lists.sourceforge.net/lists/listinfo/configobj-develop
# Comments, suggestions and bug reports welcome.
-from __future__ import generators
+
import os
import re
@@ -161,7 +161,7 @@ class Builder(object):
return m(o)
def build_List(self, o):
- return map(self.build, o.getChildren())
+ return list(map(self.build, o.getChildren()))
def build_Const(self, o):
return o.value
@@ -170,7 +170,7 @@ class Builder(object):
d = {}
i = iter(map(self.build, o.getChildren()))
for el in i:
- d[el] = i.next()
+ d[el] = next(i)
return d
def build_Tuple(self, o):
@@ -188,7 +188,7 @@ class Builder(object):
raise UnknownType('Undefined Name')
def build_Add(self, o):
- real, imag = map(self.build_Const, o.getChildren())
+ real, imag = list(map(self.build_Const, o.getChildren()))
try:
real = float(real)
except TypeError:
@@ -214,6 +214,12 @@ _builder = Builder()
def unrepr(s):
if not s:
return s
+
+ # this is supposed to be safe
+ import ast
+ return ast.literal_eval(s)
+
+ # this is no longer valid
return _builder.build(getObj(s))
@@ -518,7 +524,7 @@ class Section(dict):
self._initialise()
# we do this explicitly so that __setitem__ is used properly
# (rather than just passing to ``dict.__init__``)
- for entry, value in indict.iteritems():
+ for entry, value in indict.items():
self[entry] = value
@@ -566,11 +572,11 @@ class Section(dict):
"""Fetch the item and do string interpolation."""
val = dict.__getitem__(self, key)
if self.main.interpolation:
- if isinstance(val, basestring):
+ if isinstance(val, str):
return self._interpolate(key, val)
if isinstance(val, list):
def _check(entry):
- if isinstance(entry, basestring):
+ if isinstance(entry, str):
return self._interpolate(key, entry)
return entry
new = [_check(entry) for entry in val]
@@ -593,7 +599,7 @@ class Section(dict):
``unrepr`` must be set when setting a value to a dictionary, without
creating a new sub-section.
"""
- if not isinstance(key, basestring):
+ if not isinstance(key, str):
raise ValueError('The key "%s" is not a string.' % key)
# add the comment
@@ -627,11 +633,11 @@ class Section(dict):
if key not in self:
self.scalars.append(key)
if not self.main.stringify:
- if isinstance(value, basestring):
+ if isinstance(value, str):
pass
elif isinstance(value, (list, tuple)):
for entry in value:
- if not isinstance(entry, basestring):
+ if not isinstance(entry, str):
raise TypeError('Value is not a string "%s".' % entry)
else:
raise TypeError('Value is not a string "%s".' % value)
@@ -721,7 +727,7 @@ class Section(dict):
def items(self):
"""D.items() -> list of D's (key, value) pairs, as 2-tuples"""
- return zip((self.scalars + self.sections), self.values())
+ return list(zip((self.scalars + self.sections), list(self.values())))
def keys(self):
@@ -736,7 +742,7 @@ class Section(dict):
def iteritems(self):
"""D.iteritems() -> an iterator over the (key, value) items of D"""
- return iter(self.items())
+ return iter(list(self.items()))
def iterkeys(self):
@@ -748,7 +754,7 @@ class Section(dict):
def itervalues(self):
"""D.itervalues() -> an iterator over the values of D"""
- return iter(self.values())
+ return iter(list(self.values()))
def __repr__(self):
@@ -814,7 +820,7 @@ class Section(dict):
>>> c2
ConfigObj({'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}})
"""
- for key, val in indict.items():
+ for key, val in list(indict.items()):
if (key in self and isinstance(self[key], dict) and
isinstance(val, dict)):
self[key].merge(val)
@@ -972,7 +978,7 @@ class Section(dict):
return False
else:
try:
- if not isinstance(val, basestring):
+ if not isinstance(val, str):
# TODO: Why do we raise a KeyError here?
raise KeyError()
else:
@@ -1013,15 +1019,15 @@ class Section(dict):
>>> a = ConfigObj()
>>> a['a'] = 'fish'
- >>> a.as_float('a')
+ >>> a.as_float('a') #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
ValueError: invalid literal for float(): fish
>>> a['b'] = '1'
>>> a.as_float('b')
1.0
>>> a['b'] = '3.2'
- >>> a.as_float('b')
- 3.2000000000000002
+ >>> a.as_float('b') #doctest: +ELLIPSIS
+ 3.2...
"""
return float(self[key])
@@ -1224,7 +1230,7 @@ class ConfigObj(Section):
for entry in options:
if entry not in OPTION_DEFAULTS:
raise TypeError('Unrecognised option "%s".' % entry)
- for entry, value in OPTION_DEFAULTS.items():
+ for entry, value in list(OPTION_DEFAULTS.items()):
if entry not in options:
options[entry] = value
keyword_value = _options[entry]
@@ -1243,7 +1249,7 @@ class ConfigObj(Section):
def _load(self, infile, configspec):
- if isinstance(infile, basestring):
+ if isinstance(infile, str):
self.filename = infile
if os.path.isfile(infile):
h = open(infile, 'rb')
@@ -1296,7 +1302,7 @@ class ConfigObj(Section):
# in case it's not an 8 bit encoding
else:
raise TypeError('infile must be a filename, file like object, or list of lines.')
-
+
if infile:
# don't do it for the empty ConfigObj
infile = self._handle_bom(infile)
@@ -1305,7 +1311,7 @@ class ConfigObj(Section):
# Set the newlines attribute (first line ending it finds)
# and strip trailing '\n' or '\r' from lines
for line in infile:
- if (not line) or (line[-1] not in ('\r', '\n', '\r\n')):
+ if (not line) or (line[-1] not in ('\r', '\n')):
continue
for end in ('\r\n', '\n', '\r'):
if line.endswith(end):
@@ -1313,6 +1319,7 @@ class ConfigObj(Section):
break
break
+ assert all(isinstance(line, str) for line in infile), repr(infile)
infile = [line.rstrip('\r\n') for line in infile]
self._parse(infile)
@@ -1423,7 +1430,7 @@ class ConfigObj(Section):
enc = BOM_LIST[self.encoding.lower()]
if enc == 'utf_16':
# For UTF16 we try big endian and little endian
- for BOM, (encoding, final_encoding) in BOMS.items():
+ for BOM, (encoding, final_encoding) in list(BOMS.items()):
if not final_encoding:
# skip UTF8
continue
@@ -1453,8 +1460,8 @@ class ConfigObj(Section):
return self._decode(infile, self.encoding)
# No encoding specified - so we need to check for UTF8/UTF16
- for BOM, (encoding, final_encoding) in BOMS.items():
- if not line.startswith(BOM):
+ for BOM, (encoding, final_encoding) in list(BOMS.items()):
+ if not isinstance(line, bytes) or not line.startswith(BOM):
continue
else:
# BOM discovered
@@ -1468,25 +1475,25 @@ class ConfigObj(Section):
infile[0] = newline
else:
infile = newline
- # UTF8 - don't decode
- if isinstance(infile, basestring):
+ # UTF-8
+ if isinstance(infile, str):
return infile.splitlines(True)
else:
- return infile
+ return self._decode(infile, 'utf-8')
# UTF16 - have to decode
return self._decode(infile, encoding)
- # No BOM discovered and no encoding specified, just return
- if isinstance(infile, basestring):
- # infile read from a file will be a single string
- return infile.splitlines(True)
- return infile
+ # No BOM discovered and no encoding specified, default to UTF-8
+ if isinstance(infile, bytes):
+ return infile.decode('utf-8').splitlines(True)
+ else:
+ return self._decode(infile, 'utf-8')
def _a_to_u(self, aString):
"""Decode ASCII strings to unicode if a self.encoding is specified."""
- if self.encoding:
- return aString.decode('ascii')
+ if isinstance(aString, bytes) and self.encoding:
+ return aString.decode(self.encoding)
else:
return aString
@@ -1497,12 +1504,13 @@ class ConfigObj(Section):
if is a string, it also needs converting to a list.
"""
- if isinstance(infile, basestring):
- # can't be unicode
+ if isinstance(infile, str):
+ return infile.splitlines(True)
+ if isinstance(infile, bytes):
# NOTE: Could raise a ``UnicodeDecodeError``
return infile.decode(encoding).splitlines(True)
for i, line in enumerate(infile):
- if not isinstance(line, unicode):
+ if not isinstance(line, str):
# NOTE: The isinstance test here handles mixed lists of unicode/string
# NOTE: But the decode will break on any non-string values
# NOTE: Or could raise a ``UnicodeDecodeError``
@@ -1512,19 +1520,19 @@ class ConfigObj(Section):
def _decode_element(self, line):
"""Decode element to unicode if necessary."""
- if not self.encoding:
- return line
- if isinstance(line, str) and self.default_encoding:
+ if isinstance(line, bytes) and self.default_encoding:
return line.decode(self.default_encoding)
- return line
+ else:
+ return line
+ # TODO: this may need to be modified
def _str(self, value):
"""
Used by ``stringify`` within validate, to turn non-string values
into strings.
"""
- if not isinstance(value, basestring):
+ if not isinstance(value, str):
return str(value)
else:
return value
@@ -1615,10 +1623,8 @@ class ConfigObj(Section):
# so it should be a valid ``key = value`` line
mat = self._keyword.match(line)
if mat is None:
- # it neither matched as a keyword
- # or a section marker
self._handle_error(
- 'Invalid line at line "%s".',
+ 'Invalid line ({!r}) (matched as neither section nor keyword) at line "%s".'.format(line),
ParseError, infile, cur_index)
else:
# is a keyword value
@@ -1633,7 +1639,7 @@ class ConfigObj(Section):
value, infile, cur_index, maxline)
except SyntaxError:
self._handle_error(
- 'Parse error in value at line %s.',
+ 'Parse error in multiline value at line %s.',
ParseError, infile, cur_index)
continue
else:
@@ -1641,11 +1647,11 @@ class ConfigObj(Section):
comment = ''
try:
value = unrepr(value)
- except Exception, e:
+ except Exception as e:
if type(e) == UnknownType:
msg = 'Unknown name or type in value at line %s.'
else:
- msg = 'Parse error in value at line %s.'
+ msg = 'Parse error from unrepr-ing multiline value at line %s.'
self._handle_error(msg, UnreprError, infile,
cur_index)
continue
@@ -1654,11 +1660,11 @@ class ConfigObj(Section):
comment = ''
try:
value = unrepr(value)
- except Exception, e:
+ except Exception as e:
if isinstance(e, UnknownType):
msg = 'Unknown name or type in value at line %s.'
else:
- msg = 'Parse error in value at line %s.'
+ msg = 'Parse error from unrepr-ing value at line %s.'
self._handle_error(msg, UnreprError, infile,
cur_index)
continue
@@ -1777,7 +1783,7 @@ class ConfigObj(Section):
return self._quote(value[0], multiline=False) + ','
return ', '.join([self._quote(val, multiline=False)
for val in value])
- if not isinstance(value, basestring):
+ if not isinstance(value, str):
if self.stringify:
value = str(value)
else:
@@ -1929,11 +1935,11 @@ class ConfigObj(Section):
raise_errors=True,
file_error=True,
_inspec=True)
- except ConfigObjError, e:
+ except ConfigObjError as e:
# FIXME: Should these errors have a reference
# to the already parsed ConfigObj ?
raise ConfigspecError('Parsing configspec failed: %s' % e)
- except IOError, e:
+ except IOError as e:
raise IOError('Reading configspec failed: %s' % e)
self.configspec = configspec
@@ -2108,7 +2114,7 @@ class ConfigObj(Section):
if outfile is not None:
outfile.write(output)
else:
- h = open(self.filename, 'wb')
+ h = open(self.filename, 'w')
h.write(output)
h.close()
@@ -2189,7 +2195,7 @@ class ConfigObj(Section):
val,
missing=missing
)
- except validator.baseErrorClass, e:
+ except validator.baseErrorClass as e:
if not preserve_errors or isinstance(e, self._vdtMissingValue):
out[entry] = False
else:
@@ -2338,7 +2344,7 @@ class ConfigObj(Section):
This method raises a ``ReloadError`` if the ConfigObj doesn't have
a filename attribute pointing to a file.
"""
- if not isinstance(self.filename, basestring):
+ if not isinstance(self.filename, str):
raise ReloadError()
filename = self.filename
@@ -2416,13 +2422,13 @@ def flatten_errors(cfg, res, levels=None, results=None):
levels = []
results = []
if res == True:
- return results
+ return sorted(results)
if res == False or isinstance(res, Exception):
results.append((levels[:], None, res))
if levels:
levels.pop()
- return results
- for (key, val) in res.items():
+ return sorted(results)
+ for (key, val) in list(res.items()):
if val == True:
continue
if isinstance(cfg.get(key), dict):
@@ -2436,7 +2442,7 @@ def flatten_errors(cfg, res, levels=None, results=None):
if levels:
levels.pop()
#
- return results
+ return sorted(results)
def get_extra_values(conf, _prepend=()):
diff --git a/configobj.wpr b/configobj.wpr
deleted file mode 100644
index e61d18c..0000000
--- a/configobj.wpr
+++ /dev/null
@@ -1,17 +0,0 @@
-#!wing
-#!version=3.0
-##################################################################
-# Wing IDE project file #
-##################################################################
-[project attributes]
-proj.directory-list = [{'dirloc': loc('.'),
- 'excludes': [u'configobj.py.orig',
- u'configobj.py.rej',
- u'configobj_speedup.patch'],
- 'filter': '*',
- 'include_hidden': False,
- 'recursive': True,
- 'watch_for_changes': True}]
-proj.file-type = 'shared'
-testing.test-framework = {loc('test_configobj.py'): ':internal doctest',
- loc('validate.py'): ':internal doctest'}
diff --git a/extras/ConfigPersist.py b/extras/ConfigPersist.py
deleted file mode 100644
index 42dca37..0000000
--- a/extras/ConfigPersist.py
+++ /dev/null
@@ -1,242 +0,0 @@
-# ConfigPersist.py
-# Functions for using ConfigObj for data persistence
-# Copyright (C) 2005 Michael Foord
-# E-mail: fuzzyman AT voidspace DOT org DOT uk
-
-# Released subject to the BSD License
-# Please see http://www.voidspace.org.uk/python/license.shtml
-
-# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
-# For information about bugfixes, updates and support, please join the
-# ConfigObj mailing list:
-# http://lists.sourceforge.net/lists/listinfo/configobj-develop
-# Comments, suggestions and bug reports welcome.
-
-"""
-Functions for doing data persistence with ConfigObj.
-
-It requires access to the validate module and ConfigObj.
-"""
-
-__version__ = '0.1.0'
-
-__all__ = (
- 'add_configspec',
- 'write_configspec',
- 'add_typeinfo',
- 'typeinfo_to_configspec',
- 'vtor',
- 'store',
- 'restore',
- 'save_configspec',
- '__version__'
- )
-
-from configobj import ConfigObj
-
-try:
- from validate import Validator
-except ImportError:
- vtor = None
-else:
- vtor = Validator()
-
-def add_configspec(config):
- """
- A function that adds a configspec to a ConfigObj.
-
- Will only work for ConfigObj instances using basic datatypes :
-
- * floats
- * strings
- * ints
- * booleans
- * Lists of the above
- """
- config.configspec = {}
- for entry in config:
- val = config[entry]
- if isinstance(val, dict):
- # a subsection
- add_configspec(val)
- elif isinstance(val, bool):
- config.configspec[entry] = 'boolean'
- elif isinstance(val, int):
- config.configspec[entry] = 'integer'
- elif isinstance(val, float):
- config.configspec[entry] = 'float'
- elif isinstance(val, str):
- config.configspec[entry] = 'string'
- elif isinstance(val, (list, tuple)):
- list_type = None
- out_list = []
- for mem in val:
- if isinstance(mem, str):
- this = 'string'
- elif isinstance(mem, bool):
- this = 'boolean'
- elif isinstance(mem, int):
- this = 'integer'
- elif isinstance(mem, float):
- this = 'float'
- else:
- raise TypeError('List member "%s" is an innapropriate type.' % mem)
- if list_type and this != list_type:
- list_type = 'mixed'
- elif list_type is None:
- list_type = this
- out_list.append(this)
- if list_type is None:
- l = 'list(%s)'
- else:
- list_type = {'integer': 'int', 'boolean': 'bool',
- 'mixed': 'mixed', 'float': 'float',
- 'string': 'string' }[list_type]
- l = '%s_list(%%s)' % list_type
- config.configspec[entry] = l % str(out_list)[1:-1]
- #
- else:
- raise TypeError('Value "%s" is an innapropriate type.' % val)
-
-def write_configspec(config):
- """Return the configspec (of a ConfigObj) as a list of lines."""
- out = []
- for entry in config:
- val = config[entry]
- if isinstance(val, dict):
- # a subsection
- m = config.main._write_marker('', val.depth, entry, '')
- out.append(m)
- out += write_configspec(val)
- else:
- name = config.main._quote(entry, multiline=False)
- out.append("%s = %s" % (name, config.configspec[entry]))
- #
- return out
-
-def add_typeinfo(config):
- """
- Turns the configspec attribute of each section into a member of the
- section. (Called ``__types__``).
-
- You must have already called ``add_configspec`` on the ConfigObj.
- """
- for entry in config.sections:
- add_typeinfo(config[entry])
- config['__types__'] = config.configspec
-
-def typeinfo_to_configspec(config):
- """Turns the '__types__' member of each section into a configspec."""
- for entry in config.sections:
- if entry == '__types__':
- continue
- typeinfo_to_configspec(config[entry])
- config.configspec = config['__types__']
- del config['__types__']
-
-def store(config):
- """"
- Passed a ConfigObj instance add type info and save.
-
- Returns the result of calling ``config.write()``.
- """
- add_configspec(config)
- add_typeinfo(config)
- return config.write()
-
-def restore(stored):
- """
- Restore a ConfigObj saved using the ``store`` function.
-
- Takes a filename or list of lines, returns the ConfigObj instance.
-
- Uses the built-in Validator instance of this module (vtor).
-
- Raises an ImportError if the validate module isn't available
- """
- if vtor is None:
- raise ImportError('Failed to import the validate module.')
- config = ConfigObj(stored)
- typeinfo_to_configspec(config)
- config.validate(vtor)
- return config
-
-def save_configspec(config):
- """Creates a configspec and returns it as a list of lines."""
- add_configspec(config)
- return write_configspec(config)
-
-def _test():
- """
- A dummy function for the sake of doctest.
-
- First test add_configspec
- >>> from configobj import ConfigObj
- >>> from validate import Validator
- >>> vtor = Validator()
- >>> config = ConfigObj()
- >>> config['member 1'] = 3
- >>> config['member 2'] = 3.0
- >>> config['member 3'] = True
- >>> config['member 4'] = [3, 3.0, True]
- >>> add_configspec(config)
- >>> assert config.configspec == { 'member 2': 'float',
- ... 'member 3': 'boolean', 'member 1': 'integer',
- ... 'member 4': "mixed_list('integer', 'float', 'boolean')"}
- >>> assert config.validate(vtor) == True
-
- Next test write_configspec - including a nested section
- >>> config['section 1'] = config.copy()
- >>> add_configspec(config)
- >>> a = config.write()
- >>> configspec = write_configspec(config)
- >>> b = ConfigObj(a, configspec=configspec)
- >>> assert b.validate(vtor) == True
- >>> assert b == config
-
- Next test add_typeinfo and typeinfo_to_configspec
- >>> orig = ConfigObj(config)
- >>> add_typeinfo(config)
- >>> a = ConfigObj(config.write())
- >>> typeinfo_to_configspec(a)
- >>> assert a.validate(vtor) == True
- >>> assert a == orig
- >>> typeinfo_to_configspec(config)
- >>> assert config.validate(vtor) == True
- >>> assert config == orig
-
- Test store and restore
- >>> a = store(config)
- >>> b = restore(a)
- >>> assert b == orig
-
- Test save_configspec
- >>> a = save_configspec(orig)
- >>> b = ConfigObj(b, configspec=a)
- >>> b.validate(vtor)
- 1
- """
-
-if __name__ == '__main__':
- # run the code tests in doctest format
- #
- import doctest
- doctest.testmod()
-
-"""
-ISSUES
-======
-
-TODO
-====
-
-
-CHANGELOG
-=========
-
-2005/09/07
-----------
-
-Module created.
-
-""" \ No newline at end of file
diff --git a/extras/configpersist.txt b/extras/configpersist.txt
deleted file mode 100644
index 196b209..0000000
--- a/extras/configpersist.txt
+++ /dev/null
@@ -1,266 +0,0 @@
-=================================
- Data Persistence with ConfigObj
-=================================
---------------------------
- The ConfigPersist Module
---------------------------
-
-:Author: Michael Foord
-:Contact: fuzzyman@voidspace.org.uk
-:Version: 0.1.0
-:Date: 2005/09/07
-:License: `BSD License`_ [#]_
-:Online Version: `ConfigPersist online`_
-
-.. _`configpersist online`: http://www.voidspace.org.uk/python/configpersist.html
-.. _BSD License: BSD-LICENSE.txt
-
-.. contents:: Data Persistence
-
-Introduction
-============
-
-This module contains various functions for data persistence with ConfigObj_.
-
-ConfigObj_ is a pure python module for the easy reading and writing of
-application configuration data. It uses an *ini* file like syntax - similar to
-the ConfigParser_ module - but with much greater power.
-
-ConfigObj in conjunction with validate_ can store nested sections (like
-dictionaries) - with values as integers, floats, strings, booleans, and lists
-of these values. This makes it ideal for certain types of human readable (and
-writeable) data persistence.
-
-For a discussion of this idea (out of which this module was born) - see
-`ConfigObj for Data Persistence`_.
-
-You can find ConfigObj, and other useful modules, over at the
-`Voidspace Modules Page`_.
-
-.. _Voidspace Modules Page: /python/modules.shtml
-.. _validate: /python/validate.html
-.. _ConfigParser: http://docs.python.org/lib/module-configparser.html
-.. _ConfigObj for Data Persistence: /python/articles/configobj_for_data_persistence.shtml
-.. _ConfigObj: /python/configobj.html
-
-Downloading
-===========
-
-You can download the ConfigPersist module from : ConfigPersist.py_
-
-.. _ConfigPersist.py: /cgi-bin/voidspace/downman.py?file=ConfigPersist.py
-
-Limitations
-===========
-
-.. hint::
-
- This is an extract from the `ConfigObj for Data Persistence`_ article.
-
-ConfigObj can't just be used to represent arbitrary data structures - even if
-all the members are allowed types.
-
- * Although dictionaries can be nested, they can't be inside lists.
- * Lists also can't be nested inside each other [#]_.
- * Values other than strings need a schema (a ``configspec``) to convert
- them back into the right type.
- * Dictionary keys must be strings.
- * It is actually impossible to store a string containing single triple
- quotes (``'''``) *and* double triple quotes (``"""``).
- * List members cannot contain carriage returns. (Single line values only). [#]_
-
-ConfigObj *isn't* a data persistence module - this list of restrictions tells
-you that much. However if you examine the typical data structures used in your
-programs you may find that these restrictions aren't a problem for many of them.
-
-So Why Do It ?
---------------
-
-Why would we want to do this ? Well, the usual method for preserving data
-structures is the Python pickle_ module. This can store and retrieve a much
-wider range of objects - with *none* of the restrictions above.
-
-However :
-
- * Pickles aren't human readable or writeable. This makes ConfigObj ideal
- for debugging or where you want to manually modify the data.
- * Pickles are unsafe - a maliciously crafted pickle can cause arbitrary
- code execution.
- * ConfigObj is slightly easier to use - ``data = ConfigObj(filename)`` and
- ``data.write()``.
-
-Of these three reasons the first is overwhelmingly the most compelling.
-
-.. _pickle: http://docs.python.org/lib/module-pickle.html
-
-The Functions
-=============
-
-The first three functions provide the highest level interface to this module.
-You can use these without needing to the other functions.
-
-save_configspec_ could be useful to anyone using the ConfigObj module - not
-just for data persistence.
-
-
-store
------
-
-::
-
- store(config)
-
-
-Passed a ConfigObj instance add type info and save.
-
-Returns the result of calling ``config.write()``.
-
-.. caution::
-
- This function modifies the ConfigObj instance by adding the ``__types__``
- data.
-
- You can call typeinfo_to_configspec_ to reverse this.
-
-
-restore
--------
-
-::
-
- restore(stored)
-
-Restore a ConfigObj saved using the ``store`` function.
-
-Takes a filename or list of lines, returns the ConfigObj instance.
-
-Uses the built-in ``Validator`` instance of this module (vtor).
-
-Raises an ``ImportError`` if the validate module isn't available.
-
-
-save_configspec
----------------
-
-::
-
- save_configspec(config)
-
-Creates a configspec for a ConfigObj (which must be comprised of the basic
-datatypes) and returns it as a list of lines.
-
-Lower Level Functions
----------------------
-
-These functions provide a slightly lower level interface to adding type info to
-a ConfigObj. They are still very easy to use though.
-
-add_configspec
-~~~~~~~~~~~~~~
-
-::
-
- add_configspec(config)
-
-A function that adds a configspec to a ConfigObj.
-
-Will only work for ConfigObj instances using basic datatypes :
-
- * floats
- * strings
- * ints
- * booleans
- * Lists of the above
-
-write_configspec
-~~~~~~~~~~~~~~~~
-
-::
-
- write_configspec(config)
-
-Return the configspec (of a ConfigObj) as a list of lines.
-
-You must first call ``add_configspec``. You can use save_configspec_ which does
-both in one step.
-
-add_typeinfo
-~~~~~~~~~~~~
-
-::
-
- add_typeinfo(config)
-
-Turns the configspec attribute of each section into a member of the
-section. (Called ``__types__``).
-
-You must have already called ``add_configspec`` on the ConfigObj.
-
-typeinfo_to_configspec
-~~~~~~~~~~~~~~~~~~~~~~
-
-::
-
- typeinfo_to_configspec(config)
-
-Turns the ``__types__`` member of each section into a configspec.
-
-(The opposite of ``add_typeinfo``).
-
-vtor
-~~~~
-
-This object isn't actually a function - it's the ``Validator`` instance used
-by this module.
-
-If the validate module isn't available - this object will be ``None``.
-
-
-CHANGELOG
-=========
-
-See the source code for CHANGELOG (and TODO/ISSUES).
-
-Footnotes
-=========
-
-.. [#] Online at http://www.voidspace.org.uk/python/license.shtml
-.. [#] We could remove this restriction by using the listquote_ module to parse
- ConfigObj values. Unfortunately a bug in ConfigObj means that this is
- currently not possible.
-.. [#] List members can also not contain both types of quote. We could remove
- these last two restrictions using the ``quote_unescape`` function from
- listquote - it's a bit ungainly though. Note however that the ``walk``
- method of ConfigObj is ideal for transforming values in this way.
- It will recursively walk the values and apply a function to them all.
-
-.. _listquote: /python/listquote.html
-
-.. raw:: html
-
- <center>
- <a href="http://sourceforge.net/donate/index.php?group_id=123265">
- <img src="http://images.sourceforge.net/images/project-support.jpg" width="88" height="32" border="0" alt="Support This Project" />
- </a>
- <a href="http://sourceforge.net">
- <img src="http://sourceforge.net/sflogo.php?group_id=123265&amp;type=1" width="88" height="31" border="0" alt="SourceForge.net Logo" />
- </a>
- <br />
- <a href="http://www.python.org">
- <img src="images/powered_by_python.jpg" width="602" height="186" border="0" />
- </a>
- <a href="http://www.opensource.org">
- <img src="images/osi-certified-120x100.gif" width="120" height="100" border="0" />
- <br /><strong>Certified Open Source</strong>
- </a>
- <br /><br />
- <script type="text/javascript" language="JavaScript">var site="s16atlantibots"</script>
- <script type="text/javascript" language="JavaScript1.2" src="http://s16.sitemeter.com/js/counter.js?site=s16atlantibots"></script>
- <noscript>
- <a href="http://s16.sitemeter.com/stats.asp?site=s16atlantibots">
- <img src="http://s16.sitemeter.com/meter.asp?site=s16atlantibots" alt="Site Meter" border=0 />
- </a>
- </noscript>
- <br />
- </center>
-
diff --git a/functionaltests/test_configobj.py b/functionaltests/test_configobj.py
index 8afa37f..1704536 100644
--- a/functionaltests/test_configobj.py
+++ b/functionaltests/test_configobj.py
@@ -1,4 +1,4 @@
-from __future__ import with_statement
+
import os
try:
diff --git a/test_configobj.py b/test_configobj.py
index d4a3410..748ab41 100644
--- a/test_configobj.py
+++ b/test_configobj.py
@@ -18,8 +18,8 @@
# Comments, suggestions and bug reports welcome.
-from __future__ import generators
-from StringIO import StringIO
+
+from io import StringIO
import os
import sys
@@ -58,42 +58,42 @@ def _error_test():
"""
Testing the error classes.
- >>> raise ConfigObjError
+ >>> raise ConfigObjError #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- ConfigObjError
+ configobj.ConfigObjError:
- >>> raise NestingError
+ >>> raise NestingError #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- NestingError
+ configobj.NestingError:
- >>> raise ParseError
+ >>> raise ParseError #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- ParseError
+ configobj.ParseError:
- >>> raise DuplicateError
+ >>> raise DuplicateError #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- DuplicateError
+ configobj.DuplicateError:
- >>> raise ConfigspecError
+ >>> raise ConfigspecError #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- ConfigspecError
+ configobj.ConfigspecError:
- >>> raise InterpolationLoopError('yoda')
+ >>> raise InterpolationLoopError('yoda') #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- InterpolationLoopError: interpolation loop detected in value "yoda".
+ configobj.InterpolationLoopError: interpolation loop detected in value "yoda".
- >>> raise RepeatSectionError
+ >>> raise RepeatSectionError #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- RepeatSectionError
+ configobj.RepeatSectionError:
- >>> raise MissingInterpolationOption('yoda')
+ >>> raise MissingInterpolationOption('yoda') #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- MissingInterpolationOption: missing option "yoda" in interpolation.
+ configobj.MissingInterpolationOption: missing option "yoda" in interpolation.
- >>> raise ReloadError()
+ >>> raise ReloadError() #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- ReloadError: reload failed, filename is not set.
+ configobj.ReloadError: reload failed, filename is not set.
>>> try:
... raise ReloadError()
... except IOError:
@@ -218,17 +218,17 @@ def _test_reset():
def _test_reload():
"""
>>> c = ConfigObj(StringIO())
- >>> c.reload()
+ >>> c.reload() #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- ReloadError: reload failed, filename is not set.
+ configobj.ReloadError: reload failed, filename is not set.
>>> c = ConfigObj()
- >>> c.reload()
+ >>> c.reload() #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- ReloadError: reload failed, filename is not set.
+ configobj.ReloadError: reload failed, filename is not set.
>>> c = ConfigObj([])
- >>> c.reload()
+ >>> c.reload() #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- ReloadError: reload failed, filename is not set.
+ configobj.ReloadError: reload failed, filename is not set.
We need to use a real file as reload is only for files loaded from
the filesystem.
@@ -254,6 +254,7 @@ def _test_reload():
... test3=3
... test4=5.0
... ''')
+ 338
>>> h.close()
>>> configspec = '''
... test1= integer(30,50)
@@ -415,20 +416,19 @@ def _doctest():
>>> val = Validator()
>>> c1 = ConfigObj(config, configspec=configspec)
>>> res = c1.validate(val)
- >>> flatten_errors(c1, res) == [([], 'test4', False), (['section',
- ... 'sub section'], 'test4', False), (['section'], 'test4', False)]
+ >>> flatten_errors(c1, res) == [([], 'test4', False), (['section'], 'test4', False), (['section', 'sub section'], 'test4', False)]
True
>>> res = c1.validate(val, preserve_errors=True)
>>> check = flatten_errors(c1, res)
>>> check[0][:2]
([], 'test4')
>>> check[1][:2]
- (['section', 'sub section'], 'test4')
- >>> check[2][:2]
(['section'], 'test4')
+ >>> check[2][:2]
+ (['section', 'sub section'], 'test4')
>>> for entry in check:
... isinstance(entry[2], VdtValueTooSmallError)
- ... print str(entry[2])
+ ... print(str(entry[2]))
True
the value "5.0" is too small.
True
@@ -464,7 +464,7 @@ def _doctest():
>>> uc = ConfigObj(u, encoding='utf_8', default_encoding='latin-1')
>>> uc.BOM
1
- >>> isinstance(uc['test1'], unicode)
+ >>> isinstance(uc['test1'], str)
1
>>> uc.encoding
'utf_8'
@@ -474,7 +474,7 @@ def _doctest():
>>> a_list = uc.write()
>>> len(a_list)
15
- >>> isinstance(a_list[0], str)
+ >>> isinstance(a_list[0], bytes)
1
>>> a_list[0].startswith(BOM_UTF8)
1
@@ -486,6 +486,7 @@ def _doctest():
>>> file_like = StringIO()
>>> uc.write(file_like)
>>> file_like.seek(0)
+ 0
>>> uc2 = ConfigObj(file_like)
>>> uc2 == uc
1
@@ -593,7 +594,7 @@ def _doctest():
>>> a['a'] = 'fish'
>>> try:
... a.as_int('a') #doctest: +ELLIPSIS
- ... except ValueError, e:
+ ... except ValueError as e:
... err_mess = str(e)
>>> err_mess.startswith('invalid literal for int()')
1
@@ -603,22 +604,22 @@ def _doctest():
>>> a['b'] = '3.2'
>>> try:
... a.as_int('b') #doctest: +ELLIPSIS
- ... except ValueError, e:
+ ... except ValueError as e:
... err_mess = str(e)
>>> err_mess.startswith('invalid literal for int()')
1
>>> a = ConfigObj()
>>> a['a'] = 'fish'
- >>> a.as_float('a')
+ >>> a.as_float('a') #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
ValueError: invalid literal for float(): fish
>>> a['b'] = '1'
>>> a.as_float('b')
1.0
>>> a['b'] = '3.2'
- >>> a.as_float('b')
- 3.2000000000000002
+ >>> a.as_float('b') #doctest: +ELLIPSIS
+ 3.2...
Test # with unrepr
>>> a = '''
@@ -692,9 +693,9 @@ def _test_configobj():
... [ "hello" ]
... member = value
... '''
- >>> ConfigObj(c.split('\\n'), raise_errors = True)
+ >>> ConfigObj(c.split('\\n'), raise_errors = True) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- DuplicateError: Duplicate section name at line 6.
+ configobj.DuplicateError: Duplicate section name at line 6.
>>> d = '''
... [hello]
@@ -706,9 +707,9 @@ def _test_configobj():
... [ "and again" ]
... member = value
... '''
- >>> ConfigObj(d.split('\\n'), raise_errors = True)
+ >>> ConfigObj(d.split('\\n'), raise_errors = True) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- DuplicateError: Duplicate keyword name at line 7.
+ configobj.DuplicateError: Duplicate keyword name at line 7.
Testing ConfigParser-style interpolation
@@ -750,12 +751,12 @@ def _test_configobj():
Testing the interpolation errors.
>>> c.interpolation = True
- >>> c['section']['d']
+ >>> c['section']['d'] #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- MissingInterpolationOption: missing option "not_here" in interpolation.
- >>> c['section']['e']
+ configobj.MissingInterpolationOption: missing option "not_here" in interpolation.
+ >>> c['section']['e'] #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- InterpolationLoopError: interpolation loop detected in value "e".
+ configobj.InterpolationLoopError: interpolation loop detected in value "e".
Testing Template-style interpolation
@@ -810,17 +811,17 @@ def _test_configobj():
Testing our quoting.
- >>> i._quote('\"""\\'\\'\\'')
+ >>> i._quote('\"""\\'\\'\\'') #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- ConfigObjError: Value \"\"""'''" cannot be safely quoted.
+ configobj.ConfigObjError: Value \"\"""'''" cannot be safely quoted.
>>> try:
... i._quote('\\n', multiline=False)
- ... except ConfigObjError, e:
+ ... except ConfigObjError as e:
... e.msg
'Value "\\n" cannot be safely quoted.'
- >>> i._quote(' "\\' ', multiline=False)
+ >>> i._quote(' "\\' ', multiline=False) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- ConfigObjError: Value " "' " cannot be safely quoted.
+ configobj.ConfigObjError: Value " "' " cannot be safely quoted.
Testing with "stringify" off.
>>> c.stringify = False
@@ -915,7 +916,7 @@ def _test_configobj():
... '''
>>> try:
... ConfigObj(testconfig4.split('\\n'))
- ... except ConfigObjError, e:
+ ... except ConfigObjError as e:
... len(e.errors)
4
@@ -929,7 +930,7 @@ def _test_configobj():
... '''
>>> try:
... ConfigObj(testconfig5.split('\\n'))
- ... except ConfigObjError, e:
+ ... except ConfigObjError as e:
... len(e.errors)
4
@@ -1019,18 +1020,12 @@ def _test_configobj():
>>> ConfigObj(cfg).write() == cfg
1
>>> cfg = ['[sect]', ' [[sect]]', ' foo = bar']
- >>> ConfigObj(cfg).write() == cfg
- 1
- >>> ConfigObj(oneTabCfg).write() == oneTabCfg
- 1
- >>> ConfigObj(twoTabsCfg).write() == twoTabsCfg
- 1
- >>> ConfigObj(tabsAndSpacesCfg).write() == tabsAndSpacesCfg
- 1
- >>> ConfigObj(cfg, indent_type=chr(9)).write() == oneTabCfg
- 1
- >>> ConfigObj(oneTabCfg, indent_type=' ').write() == cfg
- 1
+ >>> assert ConfigObj(cfg).write() == cfg
+ >>> assert ConfigObj(oneTabCfg).write() == oneTabCfg
+ >>> assert ConfigObj(twoTabsCfg).write() == twoTabsCfg
+ >>> assert ConfigObj(tabsAndSpacesCfg).write() == [s.decode('utf-8') for s in tabsAndSpacesCfg]
+ >>> assert ConfigObj(cfg, indent_type=chr(9)).write() == oneTabCfg
+ >>> assert ConfigObj(oneTabCfg, indent_type=' ').write() == cfg
"""
@@ -1090,9 +1085,9 @@ def _test_validate():
... },
... }
1
- >>> val.check(c1.configspec['test4'], c1['test4'])
+ >>> val.check(c1.configspec['test4'], c1['test4']) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- VdtValueTooSmallError: the value "5.0" is too small.
+ validate.VdtValueTooSmallError: the value "5.0" is too small.
>>> val_test_config = '''
... key = 0
@@ -1541,7 +1536,7 @@ def _test_validate():
... errors.append('%s%s%s' % (section_string, ' = ', error or 'missing'))
>>> errors.sort()
>>> for entry in errors:
- ... print entry
+ ... print(entry)
[root], option2 = missing
[root], option3 = the value "Bad_value" is of the wrong type.
[root], section1, option2 = missing
@@ -1561,31 +1556,31 @@ def _test_errors():
... key = "value"
... key2 = "value
... '''.splitlines()
- >>> c = ConfigObj(bad_syntax)
+ >>> c = ConfigObj(bad_syntax) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- ParseError: Parse error in value at line 3.
- >>> c = ConfigObj(bad_syntax, raise_errors=True)
+ configobj.ParseError: Parse error in value at line 3.
+ >>> c = ConfigObj(bad_syntax, raise_errors=True) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- ParseError: Parse error in value at line 3.
- >>> c = ConfigObj(bad_syntax, raise_errors=True, unrepr=True)
+ configobj.ParseError: Parse error in value at line 3.
+ >>> c = ConfigObj(bad_syntax, raise_errors=True, unrepr=True) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- UnreprError: Parse error in value at line 3.
+ configobj.UnreprError: Parse error in value at line 3.
>>> try:
... c = ConfigObj(bad_syntax)
- ... except Exception, e:
- ... pass
+ ... except Exception as exc:
+ ... e = exc
>>> assert(isinstance(e, ConfigObjError))
- >>> print e
+ >>> print(e)
Parse error in value at line 3.
>>> len(e.errors) == 1
1
>>> try:
... c = ConfigObj(bad_syntax, unrepr=True)
- ... except Exception, e:
- ... pass
+ ... except Exception as exc:
+ ... e = exc
>>> assert(isinstance(e, ConfigObjError))
- >>> print e
- Parse error in value at line 3.
+ >>> print(e)
+ Parse error from unrepr-ing value at line 3.
>>> len(e.errors) == 1
1
>>> the_error = e.errors[0]
@@ -1598,31 +1593,31 @@ def _test_errors():
... '''.splitlines()
>>> try:
... c = ConfigObj(multiple_bad_syntax)
- ... except ConfigObjError, e:
+ ... except ConfigObjError as e:
... str(e)
'Parsing failed with several errors.\\nFirst error at line 3.'
- >>> c = ConfigObj(multiple_bad_syntax, raise_errors=True)
+ >>> c = ConfigObj(multiple_bad_syntax, raise_errors=True) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- ParseError: Parse error in value at line 3.
- >>> c = ConfigObj(multiple_bad_syntax, raise_errors=True, unrepr=True)
+ configobj.ParseError: Parse error in value at line 3.
+ >>> c = ConfigObj(multiple_bad_syntax, raise_errors=True, unrepr=True) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- UnreprError: Parse error in value at line 3.
+ configobj.UnreprError: Parse error in value at line 3.
>>> try:
... c = ConfigObj(multiple_bad_syntax)
- ... except Exception, e:
- ... pass
+ ... except Exception as exc:
+ ... e = exc
>>> assert(isinstance(e, ConfigObjError))
- >>> print e
+ >>> print(e)
Parsing failed with several errors.
First error at line 3.
>>> len(e.errors) == 2
1
>>> try:
... c = ConfigObj(multiple_bad_syntax, unrepr=True)
- ... except Exception, e:
- ... pass
+ ... except Exception as exc:
+ ... e = exc
>>> assert(isinstance(e, ConfigObjError))
- >>> print e
+ >>> print(e)
Parsing failed with several errors.
First error at line 3.
>>> len(e.errors) == 2
@@ -1635,12 +1630,12 @@ def _test_errors():
... key2 = value
... '''.splitlines()
>>> c = ConfigObj(unknown_name)
- >>> c = ConfigObj(unknown_name, unrepr=True)
+ >>> c = ConfigObj(unknown_name, unrepr=True) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- UnreprError: Unknown name or type in value at line 3.
- >>> c = ConfigObj(unknown_name, raise_errors=True, unrepr=True)
+ configobj.UnreprError: Unknown name or type in value at line 3.
+ >>> c = ConfigObj(unknown_name, raise_errors=True, unrepr=True) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- UnreprError: Unknown name or type in value at line 3.
+ configobj.UnreprError: Unknown name or type in value at line 3.
"""
@@ -1711,14 +1706,16 @@ def _test_lineendings():
NOTE: Need to use a real file because this code is only
exercised when reading from the filesystem.
- >>> h = open('temp', 'wb')
+ >>> h = open('temp', 'w')
>>> h.write('\\r\\n')
+ 2
>>> h.close()
>>> c = ConfigObj('temp')
>>> c.newlines
'\\r\\n'
- >>> h = open('temp', 'wb')
+ >>> h = open('temp', 'w')
>>> h.write('\\n')
+ 1
>>> h.close()
>>> c = ConfigObj('temp')
>>> c.newlines
@@ -1774,9 +1771,9 @@ def _test_many_check():
>>> c.validate(v)
1
>>> type(c['a'])
- <type 'int'>
+ <class 'int'>
>>> type(c['b'])
- <type 'int'>
+ <class 'int'>
>>> spec = ['[name]', '__many__ = integer()']
@@ -1786,9 +1783,9 @@ def _test_many_check():
>>> c.validate(v)
1
>>> type(c['name']['a'])
- <type 'int'>
+ <class 'int'>
>>> type(c['name']['b'])
- <type 'int'>
+ <class 'int'>
>>> spec = ['[__many__]', '__many__ = integer()']
@@ -1798,9 +1795,9 @@ def _test_many_check():
>>> c.validate(v)
1
>>> type(c['name']['hello'])
- <type 'int'>
+ <class 'int'>
>>> type(c['thing']['fish'])
- <type 'int'>
+ <class 'int'>
>>> spec = '''
@@ -1831,25 +1828,25 @@ def _test_many_check():
>>> c.validate(v)
1
>>> type(c['fish'])
- <type 'int'>
+ <class 'int'>
>>> type(c['buggle'])
- <type 'int'>
+ <class 'int'>
>>> c['hi']['one']
1
>>> c['hi']['two']
0
>>> type(c['hi']['bye']['odd'])
- <type 'float'>
+ <class 'float'>
>>> type(c['hi']['bye']['whoops'])
- <type 'float'>
+ <class 'float'>
>>> c['bye']['one']
1
>>> c['bye']['two']
1
>>> type(c['bye']['lots']['odd'])
- <type 'float'>
+ <class 'float'>
>>> type(c['bye']['lots']['whoops'])
- <type 'float'>
+ <class 'float'>
>>> spec = ['___many___ = integer()']
@@ -1859,9 +1856,9 @@ def _test_many_check():
>>> c.validate(v)
1
>>> type(c['a'])
- <type 'int'>
+ <class 'int'>
>>> type(c['b'])
- <type 'int'>
+ <class 'int'>
>>> spec = '''
@@ -1884,13 +1881,13 @@ def _test_many_check():
>>> c.validate(v)
1
>>> type(c['hi']['bye']['odd'])
- <type 'float'>
+ <class 'float'>
>>> type(c['hi']['bye']['whoops'])
- <type 'float'>
+ <class 'float'>
>>> type(c['bye']['lots']['odd'])
- <type 'float'>
+ <class 'float'>
>>> type(c['bye']['lots']['whoops'])
- <type 'float'>
+ <class 'float'>
>>> s = ['[dog]', '[[cow]]', 'something = boolean', '[[__many__]]',
... 'fish = integer']
@@ -1944,7 +1941,7 @@ def _unexpected_validation_errors():
>>> check = flatten_errors(ini, res)
>>> for entry in check:
... isinstance(entry[2], ValidateError)
- ... print str(entry[2])
+ ... print(str(entry[2]))
True
Section 'cow' was provided as a single value
@@ -1962,7 +1959,7 @@ def _unexpected_validation_errors():
>>> check = flatten_errors(ini, res)
>>> for entry in check:
... isinstance(entry[2], ValidateError)
- ... print str(entry[2])
+ ... print(str(entry[2]))
True
Value 'something' was provided as a section
@@ -2064,45 +2061,45 @@ def _test_reset_and_clear_more():
def _test_invalid_lists():
"""
>>> v = ['string = val, val2, , val3']
- >>> c = ConfigObj(v)
+ >>> c = ConfigObj(v) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- ParseError: Parse error in value at line 1.
+ configobj.ParseError: Parse error in value at line 1.
>>> v = ['string = val, val2,, val3']
- >>> c = ConfigObj(v)
+ >>> c = ConfigObj(v) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- ParseError: Parse error in value at line 1.
+ configobj.ParseError: Parse error in value at line 1.
>>> v = ['string = val, val2,,']
- >>> c = ConfigObj(v)
+ >>> c = ConfigObj(v) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- ParseError: Parse error in value at line 1.
+ configobj.ParseError: Parse error in value at line 1.
>>> v = ['string = val, ,']
- >>> c = ConfigObj(v)
+ >>> c = ConfigObj(v) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- ParseError: Parse error in value at line 1.
+ configobj.ParseError: Parse error in value at line 1.
>>> v = ['string = val, , ']
- >>> c = ConfigObj(v)
+ >>> c = ConfigObj(v) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- ParseError: Parse error in value at line 1.
+ configobj.ParseError: Parse error in value at line 1.
>>> v = ['string = ,,']
- >>> c = ConfigObj(v)
+ >>> c = ConfigObj(v) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- ParseError: Parse error in value at line 1.
+ configobj.ParseError: Parse error in value at line 1.
>>> v = ['string = ,, ']
- >>> c = ConfigObj(v)
+ >>> c = ConfigObj(v) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- ParseError: Parse error in value at line 1.
+ configobj.ParseError: Parse error in value at line 1.
>>> v = ['string = ,foo']
- >>> c = ConfigObj(v)
+ >>> c = ConfigObj(v) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- ParseError: Parse error in value at line 1.
+ configobj.ParseError: Parse error in value at line 1.
>>> v = ['string = foo, ']
>>> c = ConfigObj(v)
>>> c['string']
['foo']
>>> v = ['string = foo, "']
- >>> c = ConfigObj(v)
+ >>> c = ConfigObj(v) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
- ParseError: Parse error in value at line 1.
+ configobj.ParseError: Parse error in value at line 1.
"""
def _test_validation_with_preserve_errors():
@@ -2156,7 +2153,7 @@ if __name__ == '__main__':
[[[lev3c]]] # comment 22
key1 = val # comment 23"""
#
- testconfig2 = """\
+ testconfig2 = b"""\
key1 = 'val1'
key2 = "val2"
key3 = val3
@@ -2173,7 +2170,7 @@ if __name__ == '__main__':
fish = 3
"""
#
- testconfig6 = '''
+ testconfig6 = b'''
name1 = """ a single line value """ # comment
name2 = \''' another single line value \''' # comment
name3 = """ a single line value """
@@ -2201,14 +2198,14 @@ if __name__ == '__main__':
# does a string.expandtabs() on all of them, sigh
oneTabCfg = ['[sect]', '\t[[sect]]', '\t\tfoo = bar']
twoTabsCfg = ['[sect]', '\t\t[[sect]]', '\t\t\t\tfoo = bar']
- tabsAndSpacesCfg = ['[sect]', '\t \t [[sect]]', '\t \t \t \t foo = bar']
+ tabsAndSpacesCfg = [b'[sect]', b'\t \t [[sect]]', b'\t \t \t \t foo = bar']
#
import doctest
m = sys.modules.get('__main__')
globs = m.__dict__.copy()
a = ConfigObj(testconfig1.split('\n'), raise_errors=True)
- b = ConfigObj(testconfig2.split('\n'), raise_errors=True)
- i = ConfigObj(testconfig6.split('\n'), raise_errors=True)
+ b = ConfigObj(testconfig2.split(b'\n'), raise_errors=True)
+ i = ConfigObj(testconfig6.split(b'\n'), raise_errors=True)
globs.update({'INTP_VER': INTP_VER, 'a': a, 'b': b, 'i': i,
'oneTabCfg': oneTabCfg, 'twoTabsCfg': twoTabsCfg,
'tabsAndSpacesCfg': tabsAndSpacesCfg})
@@ -2218,4 +2215,4 @@ if __name__ == '__main__':
doctest.testmod(configobj, globs=globs)
-# Man alive I prefer unittest ;-) \ No newline at end of file
+# Man alive I prefer unittest ;-)
diff --git a/validate.py b/validate.py
index 73dbdb8..a39e103 100644
--- a/validate.py
+++ b/validate.py
@@ -284,7 +284,7 @@ def dottedQuadToNum(ip):
except socket.error:
# bug in inet_aton, corrected in Python 2.4
if ip.strip() == '255.255.255.255':
- return 0xFFFFFFFFL
+ return 0xFFFFFFFF
else:
raise ValueError('Not a good dotted-quad IP: %s' % ip)
return
@@ -316,11 +316,11 @@ def numToDottedQuad(num):
import socket, struct
# no need to intercept here, 4294967295L is fine
- if num > 4294967295L or num < 0:
+ if num > 4294967295 or num < 0:
raise ValueError('Not a good numeric IP: %s' % num)
try:
return socket.inet_ntoa(
- struct.pack('!L', long(num)))
+ struct.pack('!L', int(num)))
except (socket.error, struct.error, OverflowError):
raise ValueError('Not a good numeric IP: %s' % num)
@@ -615,7 +615,7 @@ class Validator(object):
fun_kwargs = dict(fun_kwargs)
else:
fun_name, fun_args, fun_kwargs, default = self._parse_check(check)
- fun_kwargs = dict([(str(key), value) for (key, value) in fun_kwargs.items()])
+ fun_kwargs = dict([(str(key), value) for (key, value) in list(fun_kwargs.items())])
self._cache[check] = fun_name, list(fun_args), dict(fun_kwargs), default
return fun_name, fun_args, fun_kwargs, default
@@ -736,10 +736,10 @@ 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, basestring)):
+ elif isinstance(val, (int, float, str)):
try:
out_params.append(fun(val))
- except ValueError, e:
+ except ValueError as e:
raise VdtParamError(name, val)
else:
raise VdtParamError(name, val)
@@ -793,9 +793,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, basestring)):
+ if not isinstance(value, (int, str)):
raise VdtTypeError(value)
- if isinstance(value, basestring):
+ if isinstance(value, str):
# if it's a string - does it represent an integer ?
try:
value = int(value)
@@ -845,7 +845,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, basestring)):
+ if not isinstance(value, (int, float, str)):
raise VdtTypeError(value)
if not isinstance(value, float):
# if it's a string - does it represent a float ?
@@ -910,7 +910,7 @@ def is_boolean(value):
VdtTypeError: the value "up" is of the wrong type.
"""
- if isinstance(value, basestring):
+ if isinstance(value, str):
try:
return bool_dict[value.lower()]
except KeyError:
@@ -953,7 +953,7 @@ def is_ip_addr(value):
Traceback (most recent call last):
VdtTypeError: the value "0" is of the wrong type.
"""
- if not isinstance(value, basestring):
+ if not isinstance(value, str):
raise VdtTypeError(value)
value = value.strip()
try:
@@ -995,7 +995,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, basestring):
+ if isinstance(value, str):
raise VdtTypeError(value)
try:
num_members = len(value)
@@ -1064,7 +1064,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, basestring):
+ if not isinstance(value, str):
raise VdtTypeError(value)
(min_len, max_len) = _is_num_param(('min', 'max'), (min, max))
try:
@@ -1170,7 +1170,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, basestring):
+ if isinstance(value, str):
raise VdtTypeError(value)
return [is_string(mem) for mem in is_list(value, min, max)]
@@ -1292,7 +1292,7 @@ def is_mixed_list(value, *args):
raise VdtValueTooLongError(value)
try:
return [fun_dict[arg](val) for arg, val in zip(args, value)]
- except KeyError, e:
+ except KeyError as e:
raise VdtParamError('mixed_list', e)
@@ -1309,7 +1309,7 @@ def is_option(value, *options):
Traceback (most recent call last):
VdtTypeError: the value "0" is of the wrong type.
"""
- if not isinstance(value, basestring):
+ if not isinstance(value, str):
raise VdtTypeError(value)
if not value in options:
raise VdtValueError(value)