Hello community, here is the log from the commit of package python-configobj for openSUSE:Factory checked in at 2014-07-24 06:58:50 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-configobj (Old) and /work/SRC/openSUSE:Factory/.python-configobj.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-configobj" Changes: -------- --- /work/SRC/openSUSE:Factory/python-configobj/python-configobj.changes 2012-05-31 17:09:21.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.python-configobj.new/python-configobj.changes 2014-07-24 06:58:53.000000000 +0200 @@ -1,0 +2,21 @@ +Wed Jul 23 13:04:31 UTC 2014 - fcaste...@suse.com + +- Update to version 5.0.5: + * BUGFIX: error in writing out config files to disk with non-ascii + characters + * BUGFIX: correcting that the code path fixed in 5.0.3 didn’t cover + reading in config files + * BUGFIX: not handling unicode encoding well, especially with respect to + writing out files + * Specific error message for installing version this version on Python + versions older than 2.5 + * Documentation corrections + * BUGFIX: Fixed regression on python 2.x where passing an encoding parameter + did not convert a bytestring config file (which is the most common) to + unicode. Added unit tests for this and related cases + * BUGFIX: A particular error message would fail to display with a type error + on python 2.6 only + * Python 3 single-source compatibility at the cost of a more restrictive set + of versions: 2.6, 2.7, 3.2, 3.3 (otherwise unchanged) + +------------------------------------------------------------------- Old: ---- configobj-4.7.2.tar.gz New: ---- configobj-5.0.5.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-configobj.spec ++++++ --- /var/tmp/diff_new_pack.dAqhm0/_old 2014-07-24 06:58:54.000000000 +0200 +++ /var/tmp/diff_new_pack.dAqhm0/_new 2014-07-24 06:58:54.000000000 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-configobj # -# Copyright (c) 2012 SUSE LINUX Products GmbH, Nuernberg, Germany. +# Copyright (c) 2014 SUSE LINUX Products GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -15,12 +15,12 @@ # Please submit bugfixes or comments via http://bugs.opensuse.org/ # + %define modname configobj Name: python-%{modname} -Version: 4.7.2 +Version: 5.0.5 Release: 0 -# FIXME: Change python-cofigobj-docs Obsoletes to < with next version update. Obsoletes introduced with version 4.7.2. -Url: http://www.voidspace.org.uk/python/configobj.html +Url: https://github.com/DiffSK/configobj Summary: Config file reading, writing and validation License: BSD-3-Clause Group: Development/Languages/Python @@ -29,7 +29,7 @@ BuildRequires: python-devel # There are no real docs! Provides: python-%{modname}-docs = %{version} -Obsoletes: python-%{modname}-docs <= %{version} +Obsoletes: python-%{modname}-docs < %{version} %if 0%{?suse_version} <= 1110 %{!?python_sitelib: %global python_sitelib %(python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")} %else @@ -69,6 +69,7 @@ %defattr(-,root,root,-) %{python_sitelib}/%{modname}.py* %{python_sitelib}/validate.py* +%{python_sitelib}/_version.py* %{python_sitelib}/%{modname}-%{version}-py%{py_ver}.egg-info %changelog ++++++ configobj-4.7.2.tar.gz -> configobj-5.0.5.tar.gz ++++++ Files old/configobj-4.7.2/._configobj.py and new/configobj-5.0.5/._configobj.py differ Files old/configobj-4.7.2/._setup.py and new/configobj-5.0.5/._setup.py differ Files old/configobj-4.7.2/._validate.py and new/configobj-5.0.5/._validate.py differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/configobj-4.7.2/PKG-INFO new/configobj-5.0.5/PKG-INFO --- old/configobj-4.7.2/PKG-INFO 2010-03-02 00:18:57.000000000 +0100 +++ new/configobj-5.0.5/PKG-INFO 2014-04-28 13:18:13.000000000 +0200 @@ -1,12 +1,11 @@ -Metadata-Version: 1.0 +Metadata-Version: 1.1 Name: configobj -Version: 4.7.2 +Version: 5.0.5 Summary: Config file reading, writing and validation. -Home-page: http://www.voidspace.org.uk/python/configobj.html -Author: Michael Foord & Nicola Larosa -Author-email: fuzzy...@voidspace.org.uk +Home-page: https://github.com/DiffSK/configobj +Author: Rob Dennis, Eli Courtwright (Michael Foord & Nicola Larosa original maintainers) +Author-email: rdennis+config...@gmail.com, e...@courtwright.org, fuzzy...@voidspace.co.uk, n...@teknico.net License: UNKNOWN -Download-URL: http://www.voidspace.org.uk/downloads/configobj-4.7.2.zip Description: **ConfigObj** is a simple but powerful config file reader and writer: an *ini file round tripper*. Its main feature is that it is very easy to use, with a straightforward programmer's interface and a simple syntax for config files. @@ -19,14 +18,25 @@ * String interpolation (substitution) * Integrated with a powerful validation system - - including automatic type checking/conversion - - and allowing default values - - repeated sections + - including automatic type checking/conversion + - and allowing default values + - repeated sections * All comments in the file are preserved * The order of keys/sections is preserved * Powerful ``unrepr`` mode for storing/retrieving Python data-types + | Release 5.0.5 corrects a unicode-bug that still existed in writing files + | Release 5.0.4 corrects a unicode-bug that still existed in reading files after + | fixing lists of string in 5.0.3 + | Release 5.0.3 corrects errors related to the incorrectly handling unicode + | encoding and writing out files + | Release 5.0.2 adds a specific error message when trying to install on + | Python versions older than 2.5 + | Release 5.0.1 fixes a regression with unicode conversion not happening + | in certain cases PY2 + | Release 5.0.0 updates the supported Python versions to 2.6, 2.7, 3.2, 3.3 + | and is otherwise unchanged | Release 4.7.2 fixes several bugs in 4.7.1 | Release 4.7.1 fixes a bug with the deprecated options keyword in | 4.7.0. @@ -38,10 +48,12 @@ Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2.3 -Classifier: Programming Language :: Python :: 2.4 -Classifier: Programming Language :: Python :: 2.5 +Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.2 +Classifier: Programming Language :: Python :: 3.3 Classifier: Operating System :: OS Independent Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: Software Development :: Libraries :: Python Modules diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/configobj-4.7.2/_version.py new/configobj-5.0.5/_version.py --- old/configobj-4.7.2/_version.py 1970-01-01 01:00:00.000000000 +0100 +++ new/configobj-5.0.5/_version.py 2014-04-26 19:06:01.000000000 +0200 @@ -0,0 +1 @@ +__version__ = '5.0.5' \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/configobj-4.7.2/configobj.py new/configobj-5.0.5/configobj.py --- old/configobj-4.7.2/configobj.py 2010-02-27 22:36:16.000000000 +0100 +++ new/configobj-5.0.5/configobj.py 2014-04-26 19:06:01.000000000 +0200 @@ -1,22 +1,17 @@ # configobj.py # A config file reader/writer that supports nested sections in config files. -# Copyright (C) 2005-2010 Michael Foord, Nicola Larosa -# E-mail: fuzzyman AT voidspace DOT org DOT uk -# nico AT tekNico DOT net - -# ConfigObj 4 -# http://www.voidspace.org.uk/python/configobj.html - -# 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. +# Copyright (C) 2005-2014: +# (name) : (email) +# Michael Foord: fuzzyman AT voidspace DOT org DOT uk +# Nicola Larosa: nico AT tekNico DOT net +# Rob Dennis: rdennis AT gmail DOT com +# Eli Courtwright: eli AT courtwright DOT org -from __future__ import generators +# This software is licensed under the terms of the BSD license. +# http://opensource.org/licenses/BSD-3-Clause + +# ConfigObj 5 - main repository for documentation and issue tracking: +# https://github.com/DiffSK/configobj import os import re @@ -24,6 +19,8 @@ from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE +import six +from _version import __version__ # imported lazily to avoid startup performance hit if it isn't used compiler = None @@ -83,20 +80,7 @@ # Sentinel for use in getattr calls to replace hasattr MISSING = object() -__version__ = '4.7.2' - -try: - any -except NameError: - def any(iterable): - for entry in iterable: - if entry: - return True - return False - - __all__ = ( - '__version__', 'DEFAULT_INDENT_TYPE', 'DEFAULT_INTERPOLATION', 'ConfigObjError', @@ -137,6 +121,8 @@ 'write_empty_values': False, } +# this could be replaced if six is used for compatibility, or there are no +# more assertions about items being a string def getObj(s): @@ -155,13 +141,12 @@ class Builder(object): def build(self, o): - m = getattr(self, 'build_' + o.__class__.__name__, None) if m is None: raise UnknownType(o.__class__.__name__) 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 +155,7 @@ 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 +173,7 @@ 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,8 +199,10 @@ def unrepr(s): if not s: return s - return _builder.build(getObj(s)) - + + # this is supposed to be safe + import ast + return ast.literal_eval(s) class ConfigObjError(SyntaxError): @@ -518,7 +505,7 @@ 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 +553,11 @@ """Fetch the item and do string interpolation.""" val = dict.__getitem__(self, key) if self.main.interpolation: - if isinstance(val, basestring): + if isinstance(val, six.string_types): return self._interpolate(key, val) if isinstance(val, list): def _check(entry): - if isinstance(entry, basestring): + if isinstance(entry, six.string_types): return self._interpolate(key, entry) return entry new = [_check(entry) for entry in val] @@ -593,7 +580,7 @@ ``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, six.string_types): raise ValueError('The key "%s" is not a string.' % key) # add the comment @@ -627,11 +614,11 @@ if key not in self: self.scalars.append(key) if not self.main.stringify: - if isinstance(value, basestring): + if isinstance(value, six.string_types): pass elif isinstance(value, (list, tuple)): for entry in value: - if not isinstance(entry, basestring): + if not isinstance(entry, six.string_types): raise TypeError('Value is not a string "%s".' % entry) else: raise TypeError('Value is not a string "%s".' % value) @@ -721,7 +708,7 @@ 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 +723,7 @@ 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 +735,7 @@ 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 +801,7 @@ >>> 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 +959,7 @@ return False else: try: - if not isinstance(val, basestring): + if not isinstance(val, six.string_types): # TODO: Why do we raise a KeyError here? raise KeyError() else: @@ -1013,15 +1000,15 @@ >>> 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 +1211,7 @@ 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,12 +1230,11 @@ def _load(self, infile, configspec): - if isinstance(infile, basestring): + if isinstance(infile, six.string_types): self.filename = infile if os.path.isfile(infile): - h = open(infile, 'rb') - infile = h.read() or [] - h.close() + with open(infile, 'rb') as h: + content = h.readlines() or [] elif self.file_error: # raise an error if the file doesn't exist raise IOError('Config file not found: "%s".' % self.filename) @@ -1257,13 +1243,12 @@ if self.create_empty: # this is a good test that the filename specified # isn't impossible - like on a non-existent device - h = open(infile, 'w') - h.write('') - h.close() - infile = [] + with open(infile, 'w') as h: + h.write('') + content = [] elif isinstance(infile, (list, tuple)): - infile = list(infile) + content = list(infile) elif isinstance(infile, dict): # initialise self @@ -1291,21 +1276,21 @@ elif getattr(infile, 'read', MISSING) is not MISSING: # This supports file like objects - infile = infile.read() or [] + content = infile.read() or [] # needs splitting into lines - but needs doing *after* decoding # 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: + + if content: # don't do it for the empty ConfigObj - infile = self._handle_bom(infile) + content = self._handle_bom(content) # infile is now *always* a list # # 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')): + for line in content: + if (not line) or (line[-1] not in ('\r', '\n')): continue for end in ('\r\n', '\n', '\r'): if line.endswith(end): @@ -1313,9 +1298,10 @@ break break - infile = [line.rstrip('\r\n') for line in infile] + assert all(isinstance(line, six.string_types) for line in content), repr(content) + content = [line.rstrip('\r\n') for line in content] - self._parse(infile) + self._parse(content) # if we had any errors, now is the time to raise them if self._errors: info = "at line %s." % self._errors[0].line_number @@ -1404,6 +1390,7 @@ ``infile`` must always be returned as a list of lines, but may be passed in as a single string. """ + if ((self.encoding is not None) and (self.encoding.lower() not in BOM_LIST)): # No need to check for a BOM @@ -1415,6 +1402,13 @@ line = infile[0] else: line = infile + + if isinstance(line, six.text_type): + # it's already decoded and there's no need to do anything + # else, just use the _decode utility method to handle + # listifying appropriately + return self._decode(infile, self.encoding) + if self.encoding is not None: # encoding explicitly supplied # And it could have an associated BOM @@ -1423,7 +1417,7 @@ 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 +1447,9 @@ 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, six.binary_type) or not line.startswith(BOM): + # didn't specify a BOM, or it's not a bytestring continue else: # BOM discovered @@ -1468,25 +1463,32 @@ infile[0] = newline else: infile = newline - # UTF8 - don't decode - if isinstance(infile, basestring): + # UTF-8 + if isinstance(infile, six.text_type): return infile.splitlines(True) + elif isinstance(infile, six.binary_type): + return infile.decode('utf-8').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 + + if six.PY2 and isinstance(line, str): + # don't actually do any decoding, since we're on python 2 and + # returning a bytestring is fine + return self._decode(infile, None) + # No BOM discovered and no encoding specified, default to UTF-8 + if isinstance(infile, six.binary_type): + 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, six.binary_type) and self.encoding: + return aString.decode(self.encoding) else: return aString @@ -1497,34 +1499,42 @@ if is a string, it also needs converting to a list. """ - if isinstance(infile, basestring): - # can't be unicode + if isinstance(infile, six.string_types): + return infile.splitlines(True) + if isinstance(infile, six.binary_type): # NOTE: Could raise a ``UnicodeDecodeError`` - return infile.decode(encoding).splitlines(True) - for i, line in enumerate(infile): - if not isinstance(line, unicode): - # 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`` - infile[i] = line.decode(encoding) + if encoding: + return infile.decode(encoding).splitlines(True) + else: + return infile.splitlines(True) + + if encoding: + for i, line in enumerate(infile): + if isinstance(line, six.binary_type): + # 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`` + infile[i] = line.decode(encoding) return infile 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, six.binary_type) 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, six.string_types): + # intentially 'str' because it's just whatever the "normal" + # string type is for the python version we're dealing with return str(value) else: return value @@ -1615,10 +1625,8 @@ # 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 ({0!r}) (matched as neither section nor keyword) at line "%s".'.format(line), ParseError, infile, cur_index) else: # is a keyword value @@ -1633,7 +1641,7 @@ 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 +1649,11 @@ 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 +1662,11 @@ 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,8 +1785,10 @@ 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, six.string_types): if self.stringify: + # intentially 'str' because it's just whatever the "normal" + # string type is for the python version we're dealing with value = str(value) else: raise TypeError('Value "%s" is not a string.' % value) @@ -1929,11 +1939,11 @@ 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 @@ -2049,7 +2059,7 @@ this_entry = section[entry] comment = self._handle_comment(section.inline_comments[entry]) - if isinstance(this_entry, dict): + if isinstance(this_entry, Section): # a section out.append(self._write_marker( indent_string, @@ -2097,21 +2107,25 @@ # Windows specific hack to avoid writing '\r\r\n' newline = '\n' output = self._a_to_u(newline).join(out) - if self.encoding: - output = output.encode(self.encoding) - if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)): - # Add the UTF8 BOM - output = BOM_UTF8 + output - if not output.endswith(newline): output += newline - if outfile is not None: - outfile.write(output) + + if isinstance(output, six.binary_type): + output_bytes = output else: - h = open(self.filename, 'wb') - h.write(output) - h.close() + output_bytes = output.encode(self.encoding or + self.default_encoding or + 'ascii') + + if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)): + # Add the UTF8 BOM + output_bytes = BOM_UTF8 + output_bytes + if outfile is not None: + outfile.write(output_bytes) + else: + with open(self.filename, 'wb') as h: + h.write(output_bytes) def validate(self, validator, preserve_errors=False, copy=False, section=None): @@ -2189,7 +2203,7 @@ 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 +2352,7 @@ 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, six.string_types): raise ReloadError() filename = self.filename @@ -2416,13 +2430,13 @@ 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 +2450,7 @@ if levels: levels.pop() # - return results + return sorted(results) def get_extra_values(conf, _prepend=()): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/configobj-4.7.2/setup.py new/configobj-5.0.5/setup.py --- old/configobj-4.7.2/setup.py 2010-03-02 00:18:36.000000000 +0100 +++ new/configobj-5.0.5/setup.py 2014-04-26 19:06:01.000000000 +0200 @@ -1,26 +1,35 @@ # setup.py # Install script for ConfigObj -# Copyright (C) 2005-2010 Michael Foord, Mark Andrews, Nicola Larosa -# E-mail: fuzzyman AT voidspace DOT org DOT uk -# mark AT la-la DOT com -# nico AT tekNico DOT net +# Copyright (C) 2005-2014: +# (name) : (email) +# Michael Foord: fuzzyman AT voidspace DOT org DOT uk +# Mark Andrews: mark AT la-la DOT com +# Nicola Larosa: nico AT tekNico DOT net +# Rob Dennis: rdennis AT gmail DOT com +# Eli Courtwright: eli AT courtwright DOT org # This software is licensed under the terms of the BSD license. -# http://www.voidspace.org.uk/python/license.shtml - +# http://opensource.org/licenses/BSD-3-Clause +import os import sys from distutils.core import setup -from configobj import __version__ as VERSION +# a simple import wouldn't work if we moved towards a package with __init__ +from _version import __version__ -NAME = 'configobj' +if sys.version_info < (2, 6): + print('for python versions < 2.6 use configobj ' + 'version 4.7.2') + sys.exit(1) -MODULES = 'configobj', 'validate' +__here__ = os.path.abspath(os.path.dirname(__file__)) -DESCRIPTION = 'Config file reading, writing and validation.' +VERSION = __version__ +NAME = 'configobj' +MODULES = 'configobj', 'validate', '_version' -URL = 'http://www.voidspace.org.uk/python/configobj.html' +DESCRIPTION = 'Config file reading, writing and validation.' -DOWNLOAD_URL = "http://www.voidspace.org.uk/downloads/configobj-%s.zip" % VERSION +URL = 'https://github.com/DiffSK/configobj' LONG_DESCRIPTION = """**ConfigObj** is a simple but powerful config file reader and writer: an *ini file round tripper*. Its main feature is that it is very easy to use, with a @@ -42,6 +51,17 @@ * The order of keys/sections is preserved * Powerful ``unrepr`` mode for storing/retrieving Python data-types +| Release 5.0.5 corrects a unicode-bug that still existed in writing files +| Release 5.0.4 corrects a unicode-bug that still existed in reading files after +| fixing lists of string in 5.0.3 +| Release 5.0.3 corrects errors related to the incorrectly handling unicode +| encoding and writing out files +| Release 5.0.2 adds a specific error message when trying to install on +| Python versions older than 2.5 +| Release 5.0.1 fixes a regression with unicode conversion not happening +| in certain cases PY2 +| Release 5.0.0 updates the supported Python versions to 2.6, 2.7, 3.2, 3.3 +| and is otherwise unchanged | Release 4.7.2 fixes several bugs in 4.7.1 | Release 4.7.1 fixes a bug with the deprecated options keyword in | 4.7.0. @@ -53,27 +73,29 @@ 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Programming Language :: Python', - 'Programming Language :: Python :: 2.3', - 'Programming Language :: Python :: 2.4', - 'Programming Language :: Python :: 2.5', + 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.2', + 'Programming Language :: Python :: 3.3', 'Operating System :: OS Independent', 'Topic :: Software Development :: Libraries', 'Topic :: Software Development :: Libraries :: Python Modules', ] -AUTHOR = 'Michael Foord & Nicola Larosa' +AUTHOR = 'Rob Dennis, Eli Courtwright (Michael Foord & Nicola Larosa original maintainers)' -AUTHOR_EMAIL = 'fuzzy...@voidspace.org.uk' +AUTHOR_EMAIL = 'rdennis+config...@gmail.com, e...@courtwright.org, fuzzy...@voidspace.co.uk, n...@teknico.net' KEYWORDS = "config, ini, dictionary, application, admin, sysadmin, configuration, validation".split(', ') setup(name=NAME, version=VERSION, + install_requires=['six'], description=DESCRIPTION, long_description=LONG_DESCRIPTION, - download_url=DOWNLOAD_URL, author=AUTHOR, author_email=AUTHOR_EMAIL, url=URL, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/configobj-4.7.2/validate.py new/configobj-5.0.5/validate.py --- old/configobj-4.7.2/validate.py 2010-03-02 00:04:02.000000000 +0100 +++ new/configobj-5.0.5/validate.py 2014-02-20 03:41:51.000000000 +0100 @@ -1,20 +1,18 @@ # validate.py # A Validator object -# Copyright (C) 2005-2010 Michael Foord, Mark Andrews, Nicola Larosa -# E-mail: fuzzyman AT voidspace DOT org DOT uk -# mark AT la-la DOT com -# nico AT tekNico DOT net +# Copyright (C) 2005-2014: +# (name) : (email) +# Michael Foord: fuzzyman AT voidspace DOT org DOT uk +# Mark Andrews: mark AT la-la DOT com +# Nicola Larosa: nico AT tekNico DOT net +# Rob Dennis: rdennis AT gmail DOT com +# Eli Courtwright: eli AT courtwright DOT org # This software is licensed under the terms of the BSD license. -# http://www.voidspace.org.uk/python/license.shtml -# Basically you're free to copy, modify, distribute and relicense it, -# So long as you keep a copy of the license with it. - -# 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. +# http://opensource.org/licenses/BSD-3-Clause + +# ConfigObj 5 - main repository for documentation and issue tracking: +# https://github.com/DiffSK/configobj """ The Validator object is used to check that supplied values @@ -165,7 +163,24 @@ import re +import sys +from pprint import pprint +#TODO - #21 - six is part of the repo now, but we didn't switch over to it here +# this could be replaced if six is used for compatibility, or there are no +# more assertions about items being a string +if sys.version_info < (3,): + string_type = basestring +else: + string_type = str + # so tests that care about unicode on 2.x can specify unicode, and the same + # tests when run on 3.x won't complain about a undefined name "unicode" + # since all strings are unicode on 3.x we just want to pass it through + # unchanged + unicode = lambda x: x + # in python 3, all ints are equivalent to python 2 longs, and they'll + # never show "L" in the repr + long = int _list_arg = re.compile(r''' (?: @@ -269,7 +284,7 @@ >>> int(dottedQuadToNum('1.2.3.4')) 16909060 >>> dottedQuadToNum('255.255.255.255') - 4294967295L + 4294967295 >>> dottedQuadToNum('255.255.255.256') Traceback (most recent call last): ValueError: Not a good dotted-quad IP: 255.255.255.256 @@ -282,41 +297,54 @@ return struct.unpack('!L', socket.inet_aton(ip.strip()))[0] except socket.error: - # bug in inet_aton, corrected in Python 2.4 - if ip.strip() == '255.255.255.255': - return 0xFFFFFFFFL - else: - raise ValueError('Not a good dotted-quad IP: %s' % ip) + raise ValueError('Not a good dotted-quad IP: %s' % ip) return def numToDottedQuad(num): """ - Convert long int to dotted quad string + Convert int or long int to dotted quad string - >>> numToDottedQuad(-1L) + >>> numToDottedQuad(long(-1)) Traceback (most recent call last): ValueError: Not a good numeric IP: -1 - >>> numToDottedQuad(1L) + >>> numToDottedQuad(long(1)) '0.0.0.1' - >>> numToDottedQuad(16777218L) + >>> numToDottedQuad(long(16777218)) '1.0.0.2' - >>> numToDottedQuad(16908291L) + >>> numToDottedQuad(long(16908291)) '1.2.0.3' - >>> numToDottedQuad(16909060L) + >>> numToDottedQuad(long(16909060)) '1.2.3.4' - >>> numToDottedQuad(4294967295L) + >>> numToDottedQuad(long(4294967295)) '255.255.255.255' - >>> numToDottedQuad(4294967296L) + >>> numToDottedQuad(long(4294967296)) Traceback (most recent call last): ValueError: Not a good numeric IP: 4294967296 + >>> numToDottedQuad(-1) + Traceback (most recent call last): + ValueError: Not a good numeric IP: -1 + >>> numToDottedQuad(1) + '0.0.0.1' + >>> numToDottedQuad(16777218) + '1.0.0.2' + >>> numToDottedQuad(16908291) + '1.2.0.3' + >>> numToDottedQuad(16909060) + '1.2.3.4' + >>> numToDottedQuad(4294967295) + '255.255.255.255' + >>> numToDottedQuad(4294967296) + Traceback (most recent call last): + ValueError: Not a good numeric IP: 4294967296 + """ # import here to avoid it when ip_addr values are not used import socket, struct # no need to intercept here, 4294967295L is fine - if num > 4294967295L or num < 0: + if num > long(4294967295) or num < 0: raise ValueError('Not a good numeric IP: %s' % num) try: return socket.inet_ntoa( @@ -464,9 +492,9 @@ ... # check that value is of the correct type. ... # possible valid inputs are integers or strings ... # that represent integers - ... if not isinstance(value, (int, long, basestring)): + ... if not isinstance(value, (int, long, string_type)): ... raise VdtTypeError(value) - ... elif isinstance(value, basestring): + ... elif isinstance(value, string_type): ... # if we are given a string ... # attempt to convert to an integer ... try: @@ -615,7 +643,7 @@ 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 +764,10 @@ 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, long, float, string_type)): try: out_params.append(fun(val)) - except ValueError, e: + except ValueError as e: raise VdtParamError(name, val) else: raise VdtParamError(name, val) @@ -793,9 +821,9 @@ 0 """ (min_val, max_val) = _is_num_param(('min', 'max'), (min, max)) - if not isinstance(value, (int, long, basestring)): + if not isinstance(value, (int, long, string_type)): raise VdtTypeError(value) - if isinstance(value, basestring): + if isinstance(value, string_type): # if it's a string - does it represent an integer ? try: value = int(value) @@ -845,7 +873,7 @@ """ (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, long, float, string_type)): raise VdtTypeError(value) if not isinstance(value, float): # if it's a string - does it represent a float ? @@ -910,7 +938,7 @@ VdtTypeError: the value "up" is of the wrong type. """ - if isinstance(value, basestring): + if isinstance(value, string_type): try: return bool_dict[value.lower()] except KeyError: @@ -953,7 +981,7 @@ Traceback (most recent call last): VdtTypeError: the value "0" is of the wrong type. """ - if not isinstance(value, basestring): + if not isinstance(value, string_type): raise VdtTypeError(value) value = value.strip() try: @@ -995,7 +1023,7 @@ 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, string_type): raise VdtTypeError(value) try: num_members = len(value) @@ -1064,7 +1092,7 @@ Traceback (most recent call last): VdtValueTooLongError: the value "1234" is too long. """ - if not isinstance(value, basestring): + if not isinstance(value, string_type): raise VdtTypeError(value) (min_len, max_len) = _is_num_param(('min', 'max'), (min, max)) try: @@ -1170,7 +1198,7 @@ Traceback (most recent call last): VdtTypeError: the value "hello" is of the wrong type. """ - if isinstance(value, basestring): + if isinstance(value, string_type): raise VdtTypeError(value) return [is_string(mem) for mem in is_list(value, min, max)] @@ -1266,21 +1294,10 @@ >>> vtor.check(mix_str, 0) Traceback (most recent call last): VdtTypeError: the value "0" is of the wrong type. - - This test requires an elaborate setup, because of a change in error string - output from the interpreter between Python 2.2 and 2.3 . - - >>> res_seq = ( - ... 'passed an incorrect value "', - ... 'yoda', - ... '" for parameter "mixed_list".', - ... ) - >>> res_str = "'".join(res_seq) - >>> try: - ... vtor.check('mixed_list("yoda")', ('a')) - ... except VdtParamError, err: - ... str(err) == res_str - 1 + + >>> vtor.check('mixed_list("yoda")', ('a')) + Traceback (most recent call last): + VdtParamError: passed an incorrect value "KeyError('yoda',)" for parameter "'mixed_list'" """ try: length = len(value) @@ -1292,7 +1309,7 @@ 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 +1326,7 @@ Traceback (most recent call last): VdtTypeError: the value "0" is of the wrong type. """ - if not isinstance(value, basestring): + if not isinstance(value, string_type): raise VdtTypeError(value) if not value in options: raise VdtValueError(value) @@ -1338,20 +1355,20 @@ ... ] >>> v = Validator({'test': _test}) >>> for entry in checks: - ... print v.check(('test(%s)' % entry), 3) - (3, ('3', '6'), {'test': ['a', 'b', 'c'], 'max': '3', 'min': '1'}) + ... pprint(v.check(('test(%s)' % entry), 3)) + (3, ('3', '6'), {'max': '3', 'min': '1', 'test': ['a', 'b', 'c']}) (3, ('3',), {}) (3, ('3', '6'), {}) (3, ('3',), {}) - (3, (), {'test': 'a b c', 'min': '1'}) - (3, (), {'test': 'a, b, c', 'min': '5'}) - (3, (), {'test': 'a, b, c', 'max': '3', 'min': '1'}) - (3, (), {'test': '-99', 'min': '-100'}) + (3, (), {'min': '1', 'test': 'a b c'}) + (3, (), {'min': '5', 'test': 'a, b, c'}) + (3, (), {'max': '3', 'min': '1', 'test': 'a, b, c'}) + (3, (), {'min': '-100', 'test': '-99'}) (3, (), {'max': '3', 'min': '1'}) (3, ('3', '6'), {'test': '36'}) (3, ('3', '6'), {'test': 'a, b, c'}) - (3, ('3',), {'test': ['a', 'b', 'c'], 'max': '3'}) - (3, ('3',), {'test': ["'a'", 'b', 'x=(c)'], 'max': '3'}) + (3, ('3',), {'max': '3', 'test': ['a', 'b', 'c']}) + (3, ('3',), {'max': '3', 'test': ["'a'", 'b', 'x=(c)']}) (3, (), {'test': 'x=fish(3)'}) >>> v = Validator() @@ -1383,14 +1400,14 @@ Bug test for unicode arguments >>> v = Validator() - >>> v.check(u'string(min=4)', u'test') - u'test' + >>> v.check(unicode('string(min=4)'), unicode('test')) == unicode('test') + True >>> v = Validator() - >>> v.get_default_value(u'string(min=4, default="1234")') - u'1234' - >>> v.check(u'string(min=4, default="1234")', u'test') - u'test' + >>> v.get_default_value(unicode('string(min=4, default="1234")')) == unicode('1234') + True + >>> v.check(unicode('string(min=4, default="1234")'), unicode('test')) == unicode('test') + True >>> v = Validator() >>> default = v.get_default_value('string(default=None)') @@ -1416,7 +1433,8 @@ '' >>> vtor.check('string(default="\n")', '', missing=True) '\n' - >>> print vtor.check('string(default="\n")', '', missing=True), + >>> print(vtor.check('string(default="\n")', '', missing=True)) + <BLANKLINE> <BLANKLINE> >>> vtor.check('string()', '\n') '\n' @@ -1447,4 +1465,8 @@ globs.update({ 'vtor': Validator(), }) - doctest.testmod(m, globs=globs) + + failures, tests = doctest.testmod( + m, globs=globs, + optionflags=doctest.IGNORE_EXCEPTION_DETAIL | doctest.ELLIPSIS) + assert not failures, '{} failures out of {} tests'.format(failures, tests) -- To unsubscribe, e-mail: opensuse-commit+unsubscr...@opensuse.org For additional commands, e-mail: opensuse-commit+h...@opensuse.org