diff options
-rw-r--r-- | .gitignore | 36 | ||||
-rw-r--r-- | LICENSE | 39 | ||||
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | configobj.py | 128 | ||||
-rw-r--r-- | configobj.wpr | 17 | ||||
-rw-r--r-- | extras/ConfigPersist.py | 242 | ||||
-rw-r--r-- | extras/configpersist.txt | 266 | ||||
-rw-r--r-- | functionaltests/test_configobj.py | 2 | ||||
-rw-r--r-- | test_configobj.py | 281 | ||||
-rw-r--r-- | validate.py | 32 |
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 @@ -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&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) |