summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLudovico Cavedon <ludovico.cavedon@gmail.com>2010-04-18 23:36:58 -0700
committerLudovico Cavedon <ludovico.cavedon@gmail.com>2010-04-18 23:36:58 -0700
commit60b2e5ebf0b074436aaec91409d9863d015b9a73 (patch)
treea1bc02ee02e081bf37b30ff43c9628832b9afc52
parent8cbb16c4830d341deb19d77d8ff9c10813460e46 (diff)
Imported Upstream version 0.3.2
-rw-r--r--Changelog11
-rw-r--r--Makefile2
-rw-r--r--PKG-INFO2
-rw-r--r--iniparse/__init__.py15
-rw-r--r--iniparse/ini.py58
-rw-r--r--python-iniparse.spec4
-rw-r--r--setup.py2
-rw-r--r--tests/__init__.py2
-rw-r--r--tests/test_fuzz.py83
-rw-r--r--tests/test_ini.py8
-rw-r--r--tests/test_misc.py51
-rw-r--r--tests/test_tidy.py135
12 files changed, 336 insertions, 37 deletions
diff --git a/Changelog b/Changelog
index daf1bd1..1b33c82 100644
--- a/Changelog
+++ b/Changelog
@@ -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
diff --git a/Makefile b/Makefile
index 0e7631a..e774245 100644
--- a/Makefile
+++ b/Makefile
@@ -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:
diff --git a/PKG-INFO b/PKG-INFO
index 1d3a599..f3ffe4b 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -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
diff --git a/setup.py b/setup.py
index 6d17ce3..c2c477d 100644
--- a/setup.py
+++ b/setup.py
@@ -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'),
+ ])