From fef9b4532a1a6a03aab82711f52ebde3f2d131d7 Mon Sep 17 00:00:00 2001 From: Andrew Shadura Date: Fri, 21 Aug 2015 21:06:36 +0200 Subject: Add Python 3 support. --- cfgparser.1 | 2 + debian/changelog | 8 + debian/control | 41 +- .../patches/python-iniparse-python3-compat.patch | 505 +++++++++++++++++++++ debian/patches/series | 1 + debian/rules | 12 +- iniparse/__init__.py | 20 +- iniparse/compat.py | 30 +- iniparse/config.py | 16 +- iniparse/configparser.py | 7 + iniparse/ini.py | 20 +- iniparse/utils.py | 4 +- tests/__init__.py | 14 +- tests/test_compat.py | 23 +- tests/test_fuzz.py | 18 +- tests/test_ini.py | 8 +- tests/test_misc.py | 4 +- tests/test_tidy.py | 2 +- tests/test_unicode.py | 10 +- 19 files changed, 662 insertions(+), 83 deletions(-) create mode 100644 cfgparser.1 create mode 100644 debian/patches/python-iniparse-python3-compat.patch create mode 100644 debian/patches/series create mode 100644 iniparse/configparser.py diff --git a/cfgparser.1 b/cfgparser.1 new file mode 100644 index 0000000..d347746 --- /dev/null +++ b/cfgparser.1 @@ -0,0 +1,2 @@ +[Foo Bar] +foo = newbar diff --git a/debian/changelog b/debian/changelog index 3c43ae3..14de39e 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +python-iniparse (0.4-2.2) unstable; urgency=medium + + * Non-maintainer upload. + * Add Python 3 support. + * Use pybuild. + + -- Andrew Shadura Fri, 21 Aug 2015 21:06:04 +0200 + python-iniparse (0.4-2.1) unstable; urgency=low * Non-maintainer upload. diff --git a/debian/control b/debian/control index 5377fff..52ddb33 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,14 @@ Source: python-iniparse Section: python Priority: extra Maintainer: Ludovico Cavedon -Build-Depends: debhelper (>= 8), python +Build-Depends: debhelper (>= 9), + dh-python, + python-all, + python-setuptools, + python-six, + python3-all, + python3-setuptools, + python3-six Standards-Version: 3.9.1 Homepage: http://code.google.com/p/iniparse/ Vcs-Git: git://git.debian.org/git/collab-maint/python-iniparse.git @@ -11,10 +18,10 @@ X-Python-Version: >= 2.4 Package: python-iniparse Architecture: all -Depends: ${python:Depends}, ${misc:Depends} +Depends: ${misc:Depends}, ${python:Depends}, python-six Provides: ${python:Provides} Breaks: ${python:Breaks} -Description: Module to access and modify configuration data in INI files +Description: access and modify configuration data in INI files (Python 2) iniparse is a INI parser for Python which is: . * Compatible with ConfigParser: Backward compatible implementations of @@ -34,3 +41,31 @@ Description: Module to access and modify configuration data in INI files completely rearranged whenever a program changes it. iniparse also allows making the order of entries in a config file significant, which is desirable in applications like image galleries. + . + This is a Python 2 version of the package + +Package: python3-iniparse +Architecture: all +Depends: ${misc:Depends}, ${python3:Depends}, python3-six +Description: access and modify configuration data in INI files (Python 3) + iniparse is a INI parser for Python which is: + . + * Compatible with ConfigParser: Backward compatible implementations of + ConfigParser, RawConfigParser, and SafeConfigParser are included that are + API-compatible with the Python standard library. They pass all the unit + tests in Python-2.4.4. + . + * Preserves structure of INI files: Order of sections & options, indentation, + comments, and blank lines are preserved as far as possible when data is + updated. + . + * More convenient: Values can be accessed using dotted notation + (cfg.user.name), or using container syntax (cfg['user']['name']). + . + It is very useful for config files that are updated both by users and by + programs, since it is very disorienting for a user to have her config file + completely rearranged whenever a program changes it. iniparse also allows + making the order of entries in a config file significant, which is desirable + in applications like image galleries. + . + This is a Python 3 version of the package diff --git a/debian/patches/python-iniparse-python3-compat.patch b/debian/patches/python-iniparse-python3-compat.patch new file mode 100644 index 0000000..843a027 --- /dev/null +++ b/debian/patches/python-iniparse-python3-compat.patch @@ -0,0 +1,505 @@ +From: Slavek Kabrda +Subject: Python 3 support using python-six +Origin: https://code.google.com/p/iniparse/issues/detail?id=22 +Bug-RedHat: https://bugzilla.redhat.com/show_bug.cgi?id=1010302 + +--- a/iniparse/compat.py ++++ b/iniparse/compat.py +@@ -12,19 +12,21 @@ + """ + + import re +-from ConfigParser import DuplicateSectionError, \ +- NoSectionError, NoOptionError, \ +- InterpolationMissingOptionError, \ +- InterpolationDepthError, \ +- InterpolationSyntaxError, \ +- DEFAULTSECT, MAX_INTERPOLATION_DEPTH ++from .configparser import DuplicateSectionError, \ ++ NoSectionError, NoOptionError, \ ++ InterpolationMissingOptionError, \ ++ InterpolationDepthError, \ ++ InterpolationSyntaxError, \ ++ DEFAULTSECT, MAX_INTERPOLATION_DEPTH + + # These are imported only for compatiability. + # The code below does not reference them directly. +-from ConfigParser import Error, InterpolationError, \ +- MissingSectionHeaderError, ParsingError ++from .configparser import Error, InterpolationError, \ ++ MissingSectionHeaderError, ParsingError + +-import ini ++import six ++ ++from . import ini + + class RawConfigParser(object): + def __init__(self, defaults=None, dict_type=dict): +@@ -56,7 +58,7 @@ + # The default section is the only one that gets the case-insensitive + # treatment - so it is special-cased here. + if section.lower() == "default": +- raise ValueError, 'Invalid section name: %s' % section ++ raise ValueError('Invalid section name: %s' % section) + + if self.has_section(section): + raise DuplicateSectionError(section) +@@ -88,7 +90,7 @@ + filename may also be given. + """ + files_read = [] +- if isinstance(filenames, basestring): ++ if isinstance(filenames, six.string_types): + filenames = [filenames] + for filename in filenames: + try: +@@ -143,7 +145,7 @@ + def getboolean(self, section, option): + v = self.get(section, option) + if v.lower() not in self._boolean_states: +- raise ValueError, 'Not a boolean: %s' % v ++ raise ValueError('Not a boolean: %s' % v) + return self._boolean_states[v.lower()] + + def has_option(self, section, option): +@@ -234,7 +236,7 @@ + if "%(" in value: + try: + value = value % vars +- except KeyError, e: ++ except KeyError as e: + raise InterpolationMissingOptionError( + option, section, rawval, e.args[0]) + else: +@@ -283,7 +285,7 @@ + _badpercent_re = re.compile(r"%[^%]|%$") + + def set(self, section, option, value): +- if not isinstance(value, basestring): ++ if not isinstance(value, six.string_types): + raise TypeError("option values must be strings") + # check for bad percent signs: + # first, replace all "good" interpolations +--- /dev/null ++++ b/iniparse/configparser.py +@@ -0,0 +1,7 @@ ++try: ++ from ConfigParser import * ++ # not all objects get imported with __all__ ++ from ConfigParser import Error, InterpolationMissingOptionError ++except ImportError: ++ from configparser import * ++ from configparser import Error, InterpolationMissingOptionError +--- a/iniparse/config.py ++++ b/iniparse/config.py +@@ -143,7 +143,7 @@ + + >>> n.aaa = 42 + >>> del n.x +- >>> print n ++ >>> print(n) + aaa = 42 + name.first = paramjit + name.last = oberoi +@@ -152,7 +152,7 @@ + + >>> isinstance(n.name, ConfigNamespace) + True +- >>> print n.name ++ >>> print(n.name) + first = paramjit + last = oberoi + >>> sorted(list(n.name)) +@@ -160,7 +160,7 @@ + + Finally, values can be read from a file as follows: + +- >>> from StringIO import StringIO ++ >>> from six import StringIO + >>> sio = StringIO(''' + ... # comment + ... ui.height = 100 +@@ -171,7 +171,7 @@ + ... ''') + >>> n = BasicConfig() + >>> n._readfp(sio) +- >>> print n ++ >>> print(n) + complexity = medium + data.secret.password = goodness=gracious me + have_python +@@ -199,7 +199,7 @@ + + def __str__(self, prefix=''): + lines = [] +- keys = self._data.keys() ++ keys = list(self._data.keys()) + keys.sort() + for name in keys: + value = self._data[name] +@@ -258,7 +258,7 @@ + >>> n.ui.display_clock = True + >>> n.ui.display_qlength = True + >>> n.ui.width = 150 +- >>> print n ++ >>> print(n) + playlist.expand_playlist = True + ui.display_clock = True + ui.display_qlength = True +@@ -267,7 +267,7 @@ + >>> from iniparse import ini + >>> i = ini.INIConfig() + >>> update_config(i, n) +- >>> print i ++ >>> print(i) + [playlist] + expand_playlist = True + +@@ -277,7 +277,7 @@ + width = 150 + + """ +- for name in source: ++ for name in sorted(source): + value = source[name] + if isinstance(value, ConfigNamespace): + if name in target: +--- a/iniparse/ini.py ++++ b/iniparse/ini.py +@@ -7,7 +7,7 @@ + + Example: + +- >>> from StringIO import StringIO ++ >>> from six import StringIO + >>> sio = StringIO('''# configure foo-application + ... [foo] + ... bar1 = qualia +@@ -16,14 +16,14 @@ + ... special = 1''') + + >>> cfg = INIConfig(sio) +- >>> print cfg.foo.bar1 ++ >>> print(cfg.foo.bar1) + qualia +- >>> print cfg['foo-ext'].special ++ >>> print(cfg['foo-ext'].special) + 1 + >>> cfg.foo.newopt = 'hi!' + >>> cfg.baz.enabled = 0 + +- >>> print cfg ++ >>> print(cfg) + # configure foo-application + [foo] + bar1 = qualia +@@ -42,9 +42,11 @@ + # Backward-compatiable with ConfigParser + + import re +-from ConfigParser import DEFAULTSECT, ParsingError, MissingSectionHeaderError ++from .configparser import DEFAULTSECT, ParsingError, MissingSectionHeaderError + +-import config ++import six ++ ++from . import config + + class LineType(object): + line = None +@@ -278,6 +280,8 @@ + value = property(get_value, set_value) + + def __str__(self): ++ for c in self.contents: ++ pass#print(c.__str__()) + s = [x.__str__() for x in self.contents] + return '\n'.join(s) + +@@ -465,7 +469,7 @@ + self._sections = {} + if defaults is None: defaults = {} + self._defaults = INISection(LineContainer(), optionxformsource=self) +- for name, value in defaults.iteritems(): ++ for name, value in defaults.items(): + self._defaults[name] = value + if fp is not None: + self._readfp(fp) +@@ -551,7 +555,7 @@ + + for line in readline_iterator(fp): + # Check for BOM on first line +- if linecount == 0 and isinstance(line, unicode): ++ if linecount == 0 and isinstance(line, six.text_type): + if line[0] == u'\ufeff': + line = line[1:] + self._bom = True +--- a/iniparse/__init__.py ++++ b/iniparse/__init__.py +@@ -3,17 +3,17 @@ + # Copyright (c) 2007 Tim Lauridsen + # All Rights Reserved. See LICENSE-PSF & LICENSE for details. + +-from ini import INIConfig, change_comment_syntax +-from config import BasicConfig, ConfigNamespace +-from compat import RawConfigParser, ConfigParser, SafeConfigParser +-from utils import tidy ++from .ini import INIConfig, change_comment_syntax ++from .config import BasicConfig, ConfigNamespace ++from .compat import RawConfigParser, ConfigParser, SafeConfigParser ++from .utils import tidy + +-from ConfigParser import DuplicateSectionError, \ +- NoSectionError, NoOptionError, \ +- InterpolationMissingOptionError, \ +- InterpolationDepthError, \ +- InterpolationSyntaxError, \ +- DEFAULTSECT, MAX_INTERPOLATION_DEPTH ++from .configparser import DuplicateSectionError, \ ++ NoSectionError, NoOptionError, \ ++ InterpolationMissingOptionError, \ ++ InterpolationDepthError, \ ++ InterpolationSyntaxError, \ ++ DEFAULTSECT, MAX_INTERPOLATION_DEPTH + + __all__ = [ + 'BasicConfig', 'ConfigNamespace', +--- a/iniparse/utils.py ++++ b/iniparse/utils.py +@@ -1,5 +1,5 @@ +-import compat +-from ini import LineContainer, EmptyLine ++from . import compat ++from .ini import LineContainer, EmptyLine + + def tidy(cfg): + """Clean up blank lines. +--- a/tests/__init__.py ++++ b/tests/__init__.py +@@ -1,12 +1,12 @@ + import unittest, doctest + +-import test_ini +-import test_misc +-import test_fuzz +-import test_compat +-import test_unicode +-import test_tidy +-import test_multiprocessing ++from . import test_ini ++from . import test_misc ++from . import test_fuzz ++from . import test_compat ++from . import test_unicode ++from . import test_tidy ++from . import test_multiprocessing + from iniparse import config + from iniparse import ini + +--- a/tests/test_compat.py ++++ b/tests/test_compat.py +@@ -1,9 +1,16 @@ + from iniparse import compat as ConfigParser +-import StringIO ++from six import StringIO ++try: ++ import UserDict ++except ImportError: ++ import collections as UserDict + import unittest +-import UserDict + +-from test import test_support ++import sys ++if sys.version_info[0] < 3: ++ from test import test_support ++else: ++ from test import support as test_support + + class SortedDict(UserDict.UserDict): + def items(self): +@@ -35,7 +42,7 @@ + + def fromstring(self, string, defaults=None): + cf = self.newconfig(defaults) +- sio = StringIO.StringIO(string) ++ sio = StringIO(string) + cf.readfp(sio) + return cf + +@@ -161,7 +168,7 @@ + "No Section!\n") + + def parse_error(self, exc, src): +- sio = StringIO.StringIO(src) ++ sio = StringIO(src) + self.assertRaises(exc, self.cf.readfp, sio) + + def test_query_errors(self): +@@ -181,7 +188,7 @@ + def get_error(self, exc, section, option): + try: + self.cf.get(section, option) +- except exc, e: ++ except exc as e: + return e + else: + self.fail("expected exception type %s.%s" +@@ -227,7 +234,7 @@ + "foo: another very\n" + " long line" + ) +- output = StringIO.StringIO() ++ output = StringIO() + cf.write(output) + self.assertEqual( + output.getvalue(), +@@ -465,7 +472,7 @@ + "o1=4\n" + "[a]\n" + "k=v\n") +- output = StringIO.StringIO() ++ output = StringIO() + self.cf.write(output) + self.assertEquals(output.getvalue(), + "[a]\n" +--- a/tests/test_fuzz.py ++++ b/tests/test_fuzz.py +@@ -1,9 +1,10 @@ + import re + import os + import random ++import sys + import unittest +-import ConfigParser +-from StringIO import StringIO ++from six import StringIO ++from six.moves import configparser + from iniparse import compat, ini, tidy + + # TODO: +@@ -96,24 +97,25 @@ + s = '\n'.join(good_lines) + cc = compat.RawConfigParser() + cc.readfp(StringIO(s)) +- cc_py = ConfigParser.RawConfigParser() ++ cc_py = configparser.RawConfigParser() + cc_py.readfp(StringIO(s)) + # compare the two configparsers + self.assertEqualConfig(cc_py, cc) + # check that tidy does not change semantics + tidy(cc) +- cc_tidy = ConfigParser.RawConfigParser() ++ cc_tidy = configparser.RawConfigParser() + cc_tidy.readfp(StringIO(str(cc.data))) + self.assertEqualConfig(cc_py, cc_tidy) + except AssertionError: + fname = 'fuzz-test-iter-%d.ini' % fuzz_iter +- print 'Fuzz test failed at iteration', fuzz_iter +- print 'Writing out failing INI file as', fname ++ print('Fuzz test failed at iteration', fuzz_iter) ++ print('Writing out failing INI file as', fname) + f = open(fname, 'w') + f.write(s) + f.close() + raise + ++ @unittest.skipIf(sys.version_info[0] > 2, 'http://code.google.com/p/iniparse/issues/detail?id=22#c9') + def assertEqualConfig(self, c1, c2): + self.assertEqualSorted(c1.sections(), c2.sections()) + self.assertEqualSorted(c1.defaults().items(), c2.defaults().items()) +@@ -123,9 +125,7 @@ + self.assertEqual(c1.get(sec, opt), c2.get(sec, opt)) + + def assertEqualSorted(self, l1, l2): +- l1.sort() +- l2.sort() +- self.assertEqual(l1, l2) ++ self.assertEqual(sorted(l1), sorted(l2)) + + class suite(unittest.TestSuite): + def __init__(self): +--- a/tests/test_ini.py ++++ b/tests/test_ini.py +@@ -1,5 +1,5 @@ + import unittest +-from StringIO import StringIO ++from six import StringIO + + from iniparse import ini + from iniparse import compat +@@ -196,13 +196,13 @@ + self.assertEqual(p._data.find('section2').find('just').value, 'kidding') + + itr = p._data.finditer('section1') +- v = itr.next() ++ v = next(itr) + self.assertEqual(v.find('help').value, 'yourself') + self.assertEqual(v.find('but').value, 'also me') +- v = itr.next() ++ v = next(itr) + self.assertEqual(v.find('help').value, 'me') + self.assertEqual(v.find('I\'m').value, 'desperate') +- self.assertRaises(StopIteration, itr.next) ++ self.assertRaises(StopIteration, next, itr) + + self.assertRaises(KeyError, p._data.find, 'section') + self.assertRaises(KeyError, p._data.find('section2').find, 'ahem') +--- a/tests/test_misc.py ++++ b/tests/test_misc.py +@@ -1,9 +1,9 @@ + import re + import unittest + import pickle +-import ConfigParser ++from six.moves import configparser ++from six import StringIO + from textwrap import dedent +-from StringIO import StringIO + from iniparse import compat, ini + + class CaseSensitiveConfigParser(compat.ConfigParser): +--- a/tests/test_tidy.py ++++ b/tests/test_tidy.py +@@ -1,6 +1,6 @@ + import unittest + from textwrap import dedent +-from StringIO import StringIO ++from six import StringIO + + from iniparse import tidy,INIConfig + from iniparse.ini import EmptyLine +--- a/tests/test_unicode.py ++++ b/tests/test_unicode.py +@@ -1,5 +1,5 @@ + import unittest +-from StringIO import StringIO ++import six + from iniparse import compat, ini + + class test_unicode(unittest.TestCase): +@@ -17,14 +17,14 @@ + """ + + def basic_tests(self, s, strable): +- f = StringIO(s) ++ f = six.StringIO(s) + i = ini.INIConfig(f) +- self.assertEqual(unicode(i), s) +- self.assertEqual(type(i.foo.bar), unicode) ++ self.assertEqual(six.text_type(i), s) ++ self.assertEqual(type(i.foo.bar), six.text_type) + if strable: + self.assertEqual(str(i), str(s)) + else: +- self.assertRaises(UnicodeEncodeError, lambda: str(i)) ++ self.assertRaises(UnicodeEncodeError, lambda: six.text_type(i).encode('ascii')) + return i + + def test_ascii(self): +--- /dev/null ++++ b/cfgparser.1 +@@ -0,0 +1,2 @@ ++[Foo Bar] ++foo = newbar diff --git a/debian/patches/series b/debian/patches/series new file mode 100644 index 0000000..e647cce --- /dev/null +++ b/debian/patches/series @@ -0,0 +1 @@ +python-iniparse-python3-compat.patch diff --git a/debian/rules b/debian/rules index a5cc653..e37b8f1 100755 --- a/debian/rules +++ b/debian/rules @@ -1,8 +1,16 @@ #!/usr/bin/make -f +export PYBUILD_NAME=iniparse + %: - dh $@ --with python2 --buildsystem=python_distutils + dh $@ --with python2,python3 --buildsystem=pybuild override_dh_auto_install: dh_auto_install - rm -fr debian/python-iniparse/usr/share/doc/iniparse-* + rm -fr debian/python*-iniparse/usr/share/doc/iniparse-* + +override_dh_auto_test: + python2 ./runtests.py + python3 ./runtests.py + +.PHONY: override_dh_auto_install override_dh_auto_test diff --git a/iniparse/__init__.py b/iniparse/__init__.py index 8de756f..7193f92 100644 --- a/iniparse/__init__.py +++ b/iniparse/__init__.py @@ -3,17 +3,17 @@ # Copyright (c) 2007 Tim Lauridsen # All Rights Reserved. See LICENSE-PSF & LICENSE for details. -from ini import INIConfig, change_comment_syntax -from config import BasicConfig, ConfigNamespace -from compat import RawConfigParser, ConfigParser, SafeConfigParser -from utils import tidy +from .ini import INIConfig, change_comment_syntax +from .config import BasicConfig, ConfigNamespace +from .compat import RawConfigParser, ConfigParser, SafeConfigParser +from .utils import tidy -from ConfigParser import DuplicateSectionError, \ - NoSectionError, NoOptionError, \ - InterpolationMissingOptionError, \ - InterpolationDepthError, \ - InterpolationSyntaxError, \ - DEFAULTSECT, MAX_INTERPOLATION_DEPTH +from .configparser import DuplicateSectionError, \ + NoSectionError, NoOptionError, \ + InterpolationMissingOptionError, \ + InterpolationDepthError, \ + InterpolationSyntaxError, \ + DEFAULTSECT, MAX_INTERPOLATION_DEPTH __all__ = [ 'BasicConfig', 'ConfigNamespace', diff --git a/iniparse/compat.py b/iniparse/compat.py index db89ed8..f95c25c 100644 --- a/iniparse/compat.py +++ b/iniparse/compat.py @@ -12,19 +12,21 @@ The underlying INIConfig object can be accessed as cfg.data """ import re -from ConfigParser import DuplicateSectionError, \ - NoSectionError, NoOptionError, \ - InterpolationMissingOptionError, \ - InterpolationDepthError, \ - InterpolationSyntaxError, \ - DEFAULTSECT, MAX_INTERPOLATION_DEPTH +from .configparser import DuplicateSectionError, \ + NoSectionError, NoOptionError, \ + InterpolationMissingOptionError, \ + InterpolationDepthError, \ + InterpolationSyntaxError, \ + DEFAULTSECT, MAX_INTERPOLATION_DEPTH # These are imported only for compatiability. # The code below does not reference them directly. -from ConfigParser import Error, InterpolationError, \ - MissingSectionHeaderError, ParsingError +from .configparser import Error, InterpolationError, \ + MissingSectionHeaderError, ParsingError -import ini +import six + +from . import ini class RawConfigParser(object): def __init__(self, defaults=None, dict_type=dict): @@ -56,7 +58,7 @@ class RawConfigParser(object): # The default section is the only one that gets the case-insensitive # treatment - so it is special-cased here. if section.lower() == "default": - raise ValueError, 'Invalid section name: %s' % section + raise ValueError('Invalid section name: %s' % section) if self.has_section(section): raise DuplicateSectionError(section) @@ -88,7 +90,7 @@ class RawConfigParser(object): filename may also be given. """ files_read = [] - if isinstance(filenames, basestring): + if isinstance(filenames, six.string_types): filenames = [filenames] for filename in filenames: try: @@ -143,7 +145,7 @@ class RawConfigParser(object): def getboolean(self, section, option): v = self.get(section, option) if v.lower() not in self._boolean_states: - raise ValueError, 'Not a boolean: %s' % v + raise ValueError('Not a boolean: %s' % v) return self._boolean_states[v.lower()] def has_option(self, section, option): @@ -234,7 +236,7 @@ class ConfigParser(RawConfigParser): if "%(" in value: try: value = value % vars - except KeyError, e: + except KeyError as e: raise InterpolationMissingOptionError( option, section, rawval, e.args[0]) else: @@ -283,7 +285,7 @@ class SafeConfigParser(ConfigParser): _badpercent_re = re.compile(r"%[^%]|%$") def set(self, section, option, value): - if not isinstance(value, basestring): + if not isinstance(value, six.string_types): raise TypeError("option values must be strings") # check for bad percent signs: # first, replace all "good" interpolations diff --git a/iniparse/config.py b/iniparse/config.py index 5cfa2ea..3b28549 100644 --- a/iniparse/config.py +++ b/iniparse/config.py @@ -143,7 +143,7 @@ class BasicConfig(ConfigNamespace): >>> n.aaa = 42 >>> del n.x - >>> print n + >>> print(n) aaa = 42 name.first = paramjit name.last = oberoi @@ -152,7 +152,7 @@ class BasicConfig(ConfigNamespace): >>> isinstance(n.name, ConfigNamespace) True - >>> print n.name + >>> print(n.name) first = paramjit last = oberoi >>> sorted(list(n.name)) @@ -160,7 +160,7 @@ class BasicConfig(ConfigNamespace): Finally, values can be read from a file as follows: - >>> from StringIO import StringIO + >>> from six import StringIO >>> sio = StringIO(''' ... # comment ... ui.height = 100 @@ -171,7 +171,7 @@ class BasicConfig(ConfigNamespace): ... ''') >>> n = BasicConfig() >>> n._readfp(sio) - >>> print n + >>> print(n) complexity = medium data.secret.password = goodness=gracious me have_python @@ -199,7 +199,7 @@ class BasicConfig(ConfigNamespace): def __str__(self, prefix=''): lines = [] - keys = self._data.keys() + keys = list(self._data.keys()) keys.sort() for name in keys: value = self._data[name] @@ -258,7 +258,7 @@ def update_config(target, source): >>> n.ui.display_clock = True >>> n.ui.display_qlength = True >>> n.ui.width = 150 - >>> print n + >>> print(n) playlist.expand_playlist = True ui.display_clock = True ui.display_qlength = True @@ -267,7 +267,7 @@ def update_config(target, source): >>> from iniparse import ini >>> i = ini.INIConfig() >>> update_config(i, n) - >>> print i + >>> print(i) [playlist] expand_playlist = True @@ -277,7 +277,7 @@ def update_config(target, source): width = 150 """ - for name in source: + for name in sorted(source): value = source[name] if isinstance(value, ConfigNamespace): if name in target: diff --git a/iniparse/configparser.py b/iniparse/configparser.py new file mode 100644 index 0000000..c543d50 --- /dev/null +++ b/iniparse/configparser.py @@ -0,0 +1,7 @@ +try: + from ConfigParser import * + # not all objects get imported with __all__ + from ConfigParser import Error, InterpolationMissingOptionError +except ImportError: + from configparser import * + from configparser import Error, InterpolationMissingOptionError diff --git a/iniparse/ini.py b/iniparse/ini.py index 408354d..052d9e9 100644 --- a/iniparse/ini.py +++ b/iniparse/ini.py @@ -7,7 +7,7 @@ Example: - >>> from StringIO import StringIO + >>> from six import StringIO >>> sio = StringIO('''# configure foo-application ... [foo] ... bar1 = qualia @@ -16,14 +16,14 @@ Example: ... special = 1''') >>> cfg = INIConfig(sio) - >>> print cfg.foo.bar1 + >>> print(cfg.foo.bar1) qualia - >>> print cfg['foo-ext'].special + >>> print(cfg['foo-ext'].special) 1 >>> cfg.foo.newopt = 'hi!' >>> cfg.baz.enabled = 0 - >>> print cfg + >>> print(cfg) # configure foo-application [foo] bar1 = qualia @@ -42,9 +42,11 @@ Example: # Backward-compatiable with ConfigParser import re -from ConfigParser import DEFAULTSECT, ParsingError, MissingSectionHeaderError +from .configparser import DEFAULTSECT, ParsingError, MissingSectionHeaderError -import config +import six + +from . import config class LineType(object): line = None @@ -278,6 +280,8 @@ class LineContainer(object): value = property(get_value, set_value) def __str__(self): + for c in self.contents: + pass#print(c.__str__()) s = [x.__str__() for x in self.contents] return '\n'.join(s) @@ -465,7 +469,7 @@ class INIConfig(config.ConfigNamespace): self._sections = {} if defaults is None: defaults = {} self._defaults = INISection(LineContainer(), optionxformsource=self) - for name, value in defaults.iteritems(): + for name, value in defaults.items(): self._defaults[name] = value if fp is not None: self._readfp(fp) @@ -551,7 +555,7 @@ class INIConfig(config.ConfigNamespace): for line in readline_iterator(fp): # Check for BOM on first line - if linecount == 0 and isinstance(line, unicode): + if linecount == 0 and isinstance(line, six.text_type): if line[0] == u'\ufeff': line = line[1:] self._bom = True diff --git a/iniparse/utils.py b/iniparse/utils.py index 829fc28..f8b773a 100644 --- a/iniparse/utils.py +++ b/iniparse/utils.py @@ -1,5 +1,5 @@ -import compat -from ini import LineContainer, EmptyLine +from . import compat +from .ini import LineContainer, EmptyLine def tidy(cfg): """Clean up blank lines. diff --git a/tests/__init__.py b/tests/__init__.py index f1fa321..88689fb 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,12 +1,12 @@ import unittest, doctest -import test_ini -import test_misc -import test_fuzz -import test_compat -import test_unicode -import test_tidy -import test_multiprocessing +from . import test_ini +from . import test_misc +from . import test_fuzz +from . import test_compat +from . import test_unicode +from . import test_tidy +from . import test_multiprocessing from iniparse import config from iniparse import ini diff --git a/tests/test_compat.py b/tests/test_compat.py index b8da3d5..b6dfb5c 100644 --- a/tests/test_compat.py +++ b/tests/test_compat.py @@ -1,9 +1,16 @@ from iniparse import compat as ConfigParser -import StringIO +from six import StringIO +try: + import UserDict +except ImportError: + import collections as UserDict import unittest -import UserDict -from test import test_support +import sys +if sys.version_info[0] < 3: + from test import test_support +else: + from test import support as test_support class SortedDict(UserDict.UserDict): def items(self): @@ -35,7 +42,7 @@ class TestCaseBase(unittest.TestCase): def fromstring(self, string, defaults=None): cf = self.newconfig(defaults) - sio = StringIO.StringIO(string) + sio = StringIO(string) cf.readfp(sio) return cf @@ -161,7 +168,7 @@ class TestCaseBase(unittest.TestCase): "No Section!\n") def parse_error(self, exc, src): - sio = StringIO.StringIO(src) + sio = StringIO(src) self.assertRaises(exc, self.cf.readfp, sio) def test_query_errors(self): @@ -181,7 +188,7 @@ class TestCaseBase(unittest.TestCase): def get_error(self, exc, section, option): try: self.cf.get(section, option) - except exc, e: + except exc as e: return e else: self.fail("expected exception type %s.%s" @@ -227,7 +234,7 @@ class TestCaseBase(unittest.TestCase): "foo: another very\n" " long line" ) - output = StringIO.StringIO() + output = StringIO() cf.write(output) self.assertEqual( output.getvalue(), @@ -465,7 +472,7 @@ class SortedTestCase(RawConfigParserTestCase): "o1=4\n" "[a]\n" "k=v\n") - output = StringIO.StringIO() + output = StringIO() self.cf.write(output) self.assertEquals(output.getvalue(), "[a]\n" diff --git a/tests/test_fuzz.py b/tests/test_fuzz.py index 5420dcc..b219500 100644 --- a/tests/test_fuzz.py +++ b/tests/test_fuzz.py @@ -1,9 +1,10 @@ import re import os import random +import sys import unittest -import ConfigParser -from StringIO import StringIO +from six import StringIO +from six.moves import configparser from iniparse import compat, ini, tidy # TODO: @@ -96,24 +97,25 @@ class test_fuzz(unittest.TestCase): s = '\n'.join(good_lines) cc = compat.RawConfigParser() cc.readfp(StringIO(s)) - cc_py = ConfigParser.RawConfigParser() + cc_py = configparser.RawConfigParser() cc_py.readfp(StringIO(s)) # compare the two configparsers self.assertEqualConfig(cc_py, cc) # check that tidy does not change semantics tidy(cc) - cc_tidy = ConfigParser.RawConfigParser() + cc_tidy = configparser.RawConfigParser() cc_tidy.readfp(StringIO(str(cc.data))) self.assertEqualConfig(cc_py, cc_tidy) except AssertionError: fname = 'fuzz-test-iter-%d.ini' % fuzz_iter - print 'Fuzz test failed at iteration', fuzz_iter - print 'Writing out failing INI file as', fname + print('Fuzz test failed at iteration', fuzz_iter) + print('Writing out failing INI file as', fname) f = open(fname, 'w') f.write(s) f.close() raise + @unittest.skipIf(sys.version_info[0] > 2, 'http://code.google.com/p/iniparse/issues/detail?id=22#c9') def assertEqualConfig(self, c1, c2): self.assertEqualSorted(c1.sections(), c2.sections()) self.assertEqualSorted(c1.defaults().items(), c2.defaults().items()) @@ -123,9 +125,7 @@ class test_fuzz(unittest.TestCase): self.assertEqual(c1.get(sec, opt), c2.get(sec, opt)) def assertEqualSorted(self, l1, l2): - l1.sort() - l2.sort() - self.assertEqual(l1, l2) + self.assertEqual(sorted(l1), sorted(l2)) class suite(unittest.TestSuite): def __init__(self): diff --git a/tests/test_ini.py b/tests/test_ini.py index 6a76edb..07d4f4e 100644 --- a/tests/test_ini.py +++ b/tests/test_ini.py @@ -1,5 +1,5 @@ import unittest -from StringIO import StringIO +from six import StringIO from iniparse import ini from iniparse import compat @@ -196,13 +196,13 @@ but = also me self.assertEqual(p._data.find('section2').find('just').value, 'kidding') itr = p._data.finditer('section1') - v = itr.next() + v = next(itr) self.assertEqual(v.find('help').value, 'yourself') self.assertEqual(v.find('but').value, 'also me') - v = itr.next() + v = next(itr) self.assertEqual(v.find('help').value, 'me') self.assertEqual(v.find('I\'m').value, 'desperate') - self.assertRaises(StopIteration, itr.next) + self.assertRaises(StopIteration, next, itr) self.assertRaises(KeyError, p._data.find, 'section') self.assertRaises(KeyError, p._data.find('section2').find, 'ahem') diff --git a/tests/test_misc.py b/tests/test_misc.py index 31cf4da..96ef035 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1,9 +1,9 @@ import re import unittest import pickle -import ConfigParser +from six.moves import configparser +from six import StringIO from textwrap import dedent -from StringIO import StringIO from iniparse import compat, ini class CaseSensitiveConfigParser(compat.ConfigParser): diff --git a/tests/test_tidy.py b/tests/test_tidy.py index 7304747..26b6cde 100644 --- a/tests/test_tidy.py +++ b/tests/test_tidy.py @@ -1,6 +1,6 @@ import unittest from textwrap import dedent -from StringIO import StringIO +from six import StringIO from iniparse import tidy,INIConfig from iniparse.ini import EmptyLine diff --git a/tests/test_unicode.py b/tests/test_unicode.py index a56fcab..14d4fbd 100644 --- a/tests/test_unicode.py +++ b/tests/test_unicode.py @@ -1,5 +1,5 @@ import unittest -from StringIO import StringIO +import six from iniparse import compat, ini class test_unicode(unittest.TestCase): @@ -17,14 +17,14 @@ baz = Marc-Andr\202 """ def basic_tests(self, s, strable): - f = StringIO(s) + f = six.StringIO(s) i = ini.INIConfig(f) - self.assertEqual(unicode(i), s) - self.assertEqual(type(i.foo.bar), unicode) + self.assertEqual(six.text_type(i), s) + self.assertEqual(type(i.foo.bar), six.text_type) if strable: self.assertEqual(str(i), str(s)) else: - self.assertRaises(UnicodeEncodeError, lambda: str(i)) + self.assertRaises(UnicodeEncodeError, lambda: six.text_type(i).encode('ascii')) return i def test_ascii(self): -- cgit v1.2.3