diff options
author | Ludovico Cavedon <ludovico.cavedon@gmail.com> | 2010-04-18 23:36:58 -0700 |
---|---|---|
committer | Ludovico Cavedon <ludovico.cavedon@gmail.com> | 2010-04-18 23:36:58 -0700 |
commit | 60b2e5ebf0b074436aaec91409d9863d015b9a73 (patch) | |
tree | a1bc02ee02e081bf37b30ff43c9628832b9afc52 | |
parent | 8cbb16c4830d341deb19d77d8ff9c10813460e46 (diff) |
Imported Upstream version 0.3.2
-rw-r--r-- | Changelog | 11 | ||||
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | PKG-INFO | 2 | ||||
-rw-r--r-- | iniparse/__init__.py | 15 | ||||
-rw-r--r-- | iniparse/ini.py | 58 | ||||
-rw-r--r-- | python-iniparse.spec | 4 | ||||
-rw-r--r-- | setup.py | 2 | ||||
-rw-r--r-- | tests/__init__.py | 2 | ||||
-rw-r--r-- | tests/test_fuzz.py | 83 | ||||
-rw-r--r-- | tests/test_ini.py | 8 | ||||
-rw-r--r-- | tests/test_misc.py | 51 | ||||
-rw-r--r-- | tests/test_tidy.py | 135 |
12 files changed, 336 insertions, 37 deletions
@@ -1,3 +1,14 @@ +2010-04-17 +* released 0.3.2 + +2010-02-26 +* added tidy() and change_comment_syntax() based on patch + by Paul Lambert. +* added ConfigParser exceptions and constants to the iniparse module + +2009-03-23 +* Added debian package support contributed by Gavin Kinsey + 2009-03-02 * released 0.3.1 @@ -12,7 +12,7 @@ archive: python setup.py sdist -d . @echo "The archive is in ${PKGNAME}-$(SETUPVERSION).tar.gz" -buildrpm: archive +rpmbuild: archive rpmbuild -ta ${PKGNAME}-$(SPECVERSION).tar.gz pychecker: @@ -1,6 +1,6 @@ Metadata-Version: 1.0 Name: iniparse -Version: 0.3.1 +Version: 0.3.2 Summary: Accessing and Modifying INI files Home-page: http://code.google.com/p/iniparse/ Author: Paramjit Oberoi diff --git a/iniparse/__init__.py b/iniparse/__init__.py index 350b7a7..1a267e6 100644 --- a/iniparse/__init__.py +++ b/iniparse/__init__.py @@ -3,11 +3,22 @@ # Copyright (c) 2007 Tim Lauridsen <tla@rasmil.dk> # All Rights Reserved. See LICENSE-PSF & LICENSE for details. -from ini import INIConfig +from ini import INIConfig, tidy, change_comment_syntax from config import BasicConfig, ConfigNamespace from compat import RawConfigParser, ConfigParser, SafeConfigParser +from ConfigParser import DuplicateSectionError, \ + NoSectionError, NoOptionError, \ + InterpolationMissingOptionError, \ + InterpolationDepthError, \ + InterpolationSyntaxError, \ + DEFAULTSECT, MAX_INTERPOLATION_DEPTH + __all__ = [ - 'INIConfig', 'BasicConfig', 'ConfigNamespace', + 'BasicConfig', 'ConfigNamespace', + 'INIConfig', 'tidy', 'change_comment_syntax', 'RawConfigParser', 'ConfigParser', 'SafeConfigParser', + 'DuplicateSectionError', 'NoSectionError', 'NoOptionError', + 'InterpolationMissingOptionError', 'InterpolationDepthError', + 'InterpolationSyntaxError', 'DEFAULTSECT', 'MAX_INTERPOLATION_DEPTH', ] diff --git a/iniparse/ini.py b/iniparse/ini.py index d58c38f..f0e7ec2 100644 --- a/iniparse/ini.py +++ b/iniparse/ini.py @@ -21,6 +21,7 @@ Example: >>> print cfg['foo-ext'].special 1 >>> cfg.foo.newopt = 'hi!' + >>> cfg.baz.enabled = 0 >>> print cfg # configure foo-application @@ -30,6 +31,9 @@ Example: newopt = hi! [foo-ext] special = 1 + <BLANKLINE> + [baz] + enabled = 0 """ @@ -41,6 +45,7 @@ import re from ConfigParser import DEFAULTSECT, ParsingError, MissingSectionHeaderError import config +import compat class LineType(object): line = None @@ -158,6 +163,14 @@ class OptionLine(LineType): parse = classmethod(parse) +def change_comment_syntax(comment_chars='%;#', allow_rem=False): + comment_chars = re.sub(r'([\]\-\^])', r'\\\1', comment_chars) + regex = r'^(?P<csep>[%s]' % comment_chars + if allow_rem: + regex += '|[rR][eE][mM]' + regex += r')(?P<comment>.*)$' + CommentLine.regex = re.compile(regex) + class CommentLine(LineType): regex = re.compile(r'^(?P<csep>[;#]|[rR][eE][mM])' r'(?P<comment>.*)$') @@ -628,3 +641,48 @@ class INIConfig(config.ConfigNamespace): if exc: raise exc + +def tidy(cfg): + """Clean up blank lines. + + This functions makes the configuration look clean and + handwritten - consecutive empty lines and empty lines at + the start of the file are removed, and one is guaranteed + to be at the end of the file. + """ + + if isinstance(cfg, compat.RawConfigParser): + cfg = cfg.data + cont = cfg._data.contents + i = 1 + while i < len(cont): + if isinstance(cont[i], LineContainer): + tidy_section(cont[i]) + i += 1 + elif (isinstance(cont[i-1], EmptyLine) and + isinstance(cont[i], EmptyLine)): + del cont[i] + else: + i += 1 + + # Remove empty first line + if cont and isinstance(cont[0], EmptyLine): + del cont[0] + + # Ensure a last line + if cont and not isinstance(cont[-1], EmptyLine): + cont.append(EmptyLine()) + +def tidy_section(lc): + cont = lc.contents + i = 1 + while i < len(cont): + if (isinstance(cont[i-1], EmptyLine) and + isinstance(cont[i], EmptyLine)): + del cont[i] + else: + i += 1 + + # Remove empty first line + if len(cont) > 1 and isinstance(cont[1], EmptyLine): + del cont[1] diff --git a/python-iniparse.spec b/python-iniparse.spec index 8ebd41f..b9dda79 100644 --- a/python-iniparse.spec +++ b/python-iniparse.spec @@ -1,7 +1,7 @@ %{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} Name: python-iniparse -Version: 0.3.1 +Version: 0.3.2 Release: 1%{?dist} Summary: Python Module for Accessing and Modifying Configuration Data in INI files Group: Development/Libraries @@ -48,6 +48,8 @@ rm -rf $RPM_BUILD_ROOT %changelog +* Sat Apr 17 2010 Paramjit Oberoi <param@cs.wisc.edu> - 0.3.2-1 +- Release 0.3.2 * Mon Mar 2 2009 Paramjit Oberoi <param@cs.wisc.edu> - 0.3.1-1 - Release 0.3.1 * Fri Feb 27 2009 Paramjit Oberoi <param@cs.wisc.edu> - 0.3.0-1 @@ -2,7 +2,7 @@ from distutils.core import setup -VERSION = '0.3.1' +VERSION = '0.3.2' setup(name ='iniparse', version = VERSION, diff --git a/tests/__init__.py b/tests/__init__.py index beea5e3..1269812 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -5,6 +5,7 @@ import test_misc import test_fuzz import test_compat import test_unicode +import test_tidy from iniparse import config from iniparse import ini @@ -18,4 +19,5 @@ class suite(unittest.TestSuite): test_fuzz.suite(), test_compat.suite(), test_unicode.suite(), + test_tidy.suite(), ]) diff --git a/tests/test_fuzz.py b/tests/test_fuzz.py index 773e1fc..f63a718 100644 --- a/tests/test_fuzz.py +++ b/tests/test_fuzz.py @@ -1,8 +1,8 @@ import re +import os import random import unittest import ConfigParser -from textwrap import dedent from StringIO import StringIO from iniparse import compat, ini @@ -69,35 +69,58 @@ def random_ini_file(): class test_fuzz(unittest.TestCase): def test_fuzz(self): random.seed(42) - for i in range(100): - # parse random file with errors disabled - s = random_ini_file() - c = ini.INIConfig(parse_exc=False) - c._readfp(StringIO(s)) - # check that file is preserved, except for - # commenting out erroneous lines - l1 = s.split('\n') - l2 = str(c).split('\n') - self.assertEqual(len(l1), len(l2)) - good_lines = [] - for i in range(len(l1)): - try: - self.assertEqual(l1[i], l2[i]) - good_lines.append(l1[i]) - except AssertionError: - self.assertEqual('#'+l1[i], l2[i]) - # parse the good subset of the file - # using ConfigParser - s = '\n'.join(good_lines) - cc = compat.RawConfigParser() - cc.readfp(StringIO(s)) - cc_py = ConfigParser.RawConfigParser() - cc_py.readfp(StringIO(s)) - # compare the two configparsers - self.assertEqualSorted(cc_py.sections(), cc.sections()) - self.assertEqualSorted(cc_py.defaults().items(), cc.defaults().items()) - for sec in cc_py.sections(): - self.assertEqualSorted(cc_py.items(sec), cc.items(sec)) + try: + num_iter = int(os.environ['INIPARSE_FUZZ_ITERATIONS']) + except (KeyError, ValueError): + num_iter = 100 + for fuzz_iter in range(num_iter): + try: + # parse random file with errors disabled + s = random_ini_file() + c = ini.INIConfig(parse_exc=False) + c._readfp(StringIO(s)) + # check that file is preserved, except for + # commenting out erroneous lines + l1 = s.split('\n') + l2 = str(c).split('\n') + self.assertEqual(len(l1), len(l2)) + good_lines = [] + for i in range(len(l1)): + try: + self.assertEqual(l1[i], l2[i]) + good_lines.append(l1[i]) + except AssertionError: + self.assertEqual('#'+l1[i], l2[i]) + # parse the good subset of the file + # using ConfigParser + s = '\n'.join(good_lines) + cc = compat.RawConfigParser() + cc.readfp(StringIO(s)) + 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 + ini.tidy(cc) + 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 + f = open(fname, 'w') + f.write(s) + f.close() + raise + + def assertEqualConfig(self, c1, c2): + self.assertEqualSorted(c1.sections(), c2.sections()) + self.assertEqualSorted(c1.defaults().items(), c2.defaults().items()) + for sec in c1.sections(): + self.assertEqualSorted(c1.options(sec), c2.options(sec)) + for opt in c1.options(sec): + self.assertEqual(c1.get(sec, opt), c2.get(sec, opt)) def assertEqualSorted(self, l1, l2): l1.sort() diff --git a/tests/test_ini.py b/tests/test_ini.py index fdf9e5b..bdb9080 100644 --- a/tests/test_ini.py +++ b/tests/test_ini.py @@ -218,6 +218,14 @@ but = also me self.assertEqual(p.section1.just.__class__, config.Undefined) self.assertEqual(p.section2.help.__class__, config.Undefined) + def test_newsection(self): + sio = StringIO(self.s1) + p = ini.INIConfig(sio) + p.new1.created = 1 + setattr(getattr(p, 'new2'), 'created', 1) + self.assertEqual(p.new1.created, 1) + self.assertEqual(p.new2.created, 1) + def test_order(self): sio = StringIO(self.s1) p = ini.INIConfig(sio) diff --git a/tests/test_misc.py b/tests/test_misc.py index aa83daf..482c5c4 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1,3 +1,4 @@ +import re import unittest import pickle import ConfigParser @@ -398,6 +399,53 @@ class test_pickle(unittest.TestCase): c2 = pickle.loads(p) self.do_ini_checks(c2) +class test_comment_syntax(unittest.TestCase): + """Test changing comment syntax with change_comment_syntax""" + + def test_regex(self): + # original regular expression + org_regex = re.compile(r'^(?P<csep>[;#]|[rR][eE][mM])(?P<comment>.*)$') + ini.change_comment_syntax(';#', True) + self.assertEqual(ini.CommentLine.regex, org_regex) + + # mercurial-safe comment line regex, as given by Steve Borho & Paul Lambert + # bitbucket.org/tortoisehg/stable/src/tip/tortoisehg/hgtk/thgconfig.py#cl-1084 + # http://groups.google.com/group/iniparse-discuss/msg/b41a54aa185a9b7c + hg_regex = re.compile(r'^(?P<csep>[%;#])(?P<comment>.*)$') + ini.change_comment_syntax('%;#', False) + self.assertEqual(ini.CommentLine.regex, hg_regex) + + # change_comment_syntax() defaults to hg regex + ini.change_comment_syntax() + self.assertEqual(ini.CommentLine.regex, hg_regex) + + # test escaping of special chars in pattern + regex = re.compile(r'^(?P<csep>[;#\-\^[\]])(?P<comment>.*)$') + ini.change_comment_syntax(';#-^[]') + self.assertEqual(ini.CommentLine.regex, regex) + + def test_ignore_includes(self): + ini.change_comment_syntax() + cfg = ini.INIConfig(StringIO(dedent(""" + # This is a mercurial-style config + % include foobar + + [ui] + username = Firstname Lastname <a@b.c> + """))) + self.assertEqual(cfg.ui.username, 'Firstname Lastname <a@b.c>') + self.assertEqual(str(cfg), dedent(""" + # This is a mercurial-style config + % include foobar + + [ui] + username = Firstname Lastname <a@b.c> + """)) + + def tearDown(self): + ini.change_comment_syntax(';#', True) + + class suite(unittest.TestSuite): def __init__(self): unittest.TestSuite.__init__(self, [ @@ -408,4 +456,5 @@ class suite(unittest.TestSuite): unittest.makeSuite(test_custom_dict, 'test'), unittest.makeSuite(test_compat, 'test'), unittest.makeSuite(test_pickle, 'test'), - ])
\ No newline at end of file + unittest.makeSuite(test_comment_syntax, 'test'), + ]) diff --git a/tests/test_tidy.py b/tests/test_tidy.py new file mode 100644 index 0000000..4990457 --- /dev/null +++ b/tests/test_tidy.py @@ -0,0 +1,135 @@ +import unittest +from textwrap import dedent +from StringIO import StringIO + +from iniparse.ini import INIConfig, EmptyLine, tidy +from iniparse.compat import ConfigParser + +class test_tidy(unittest.TestCase): + def setUp(self): + self.cfg = INIConfig() + + def test_empty_file(self): + self.assertEqual(str(self.cfg), '') + tidy(self.cfg) + self.assertEqual(str(self.cfg), '') + + def test_last_line(self): + self.cfg.newsection.newproperty = "Ok" + self.assertEqual(str(self.cfg), dedent("""\ + [newsection] + newproperty = Ok""")) + tidy(self.cfg) + self.assertEqual(str(self.cfg), dedent("""\ + [newsection] + newproperty = Ok + """)) + + def test_first_line(self): + s = dedent("""\ + + [newsection] + newproperty = Ok + """) + self.cfg._readfp(StringIO(s)) + tidy(self.cfg) + self.assertEqual(str(self.cfg), dedent("""\ + [newsection] + newproperty = Ok + """)) + + def test_remove_newlines(self): + s = dedent("""\ + + + [newsection] + newproperty = Ok + + + + + [newsection2] + + newproperty2 = Ok + + + newproperty3 = yup + + + [newsection4] + + + # remove blank lines, but leave continuation lines unharmed + + a = 1 + + b = l1 + l2 + + + # asdf + l5 + + c = 2 + + + """) + self.cfg._readfp(StringIO(s)) + tidy(self.cfg) + self.assertEqual(str(self.cfg), dedent("""\ + [newsection] + newproperty = Ok + + [newsection2] + newproperty2 = Ok + + newproperty3 = yup + + [newsection4] + # remove blank lines, but leave continuation lines unharmed + + a = 1 + + b = l1 + l2 + + + # asdf + l5 + + c = 2 + """)) + + def test_compat(self): + s = dedent(""" + [sec1] + a=1 + + + [sec2] + + b=2 + + c=3 + + + """) + cfg = ConfigParser() + cfg.readfp(StringIO(s)) + tidy(cfg) + self.assertEqual(str(cfg.data), dedent("""\ + [sec1] + a=1 + + [sec2] + b=2 + + c=3 + """)) + + +class suite(unittest.TestSuite): + def __init__(self): + unittest.TestSuite.__init__(self, [ + unittest.makeSuite(test_tidy, 'test'), + ]) |