summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLudovico Cavedon <cavedon@debian.org>2010-06-12 16:34:16 -0700
committerLudovico Cavedon <cavedon@debian.org>2010-06-12 16:34:16 -0700
commitb58ade97bde0f26271ebbccf6959bfc5a30a88d2 (patch)
tree3d296c03823ea4806febf3d0d9becb8a90e57671
parent60b2e5ebf0b074436aaec91409d9863d015b9a73 (diff)
Imported Upstream version 0.4
-rw-r--r--Changelog16
-rw-r--r--PKG-INFO2
-rw-r--r--iniparse/__init__.py3
-rw-r--r--iniparse/compat.py44
-rw-r--r--iniparse/config.py43
-rw-r--r--iniparse/ini.py49
-rw-r--r--iniparse/utils.py47
-rw-r--r--python-iniparse.spec4
-rw-r--r--setup.py2
-rw-r--r--tests/__init__.py2
-rw-r--r--tests/test_fuzz.py4
-rw-r--r--tests/test_ini.py6
-rw-r--r--tests/test_misc.py16
-rw-r--r--tests/test_multiprocessing.py33
-rw-r--r--tests/test_tidy.py3
15 files changed, 175 insertions, 99 deletions
diff --git a/Changelog b/Changelog
index 1b33c82..0bb1522 100644
--- a/Changelog
+++ b/Changelog
@@ -1,3 +1,19 @@
+2010-06-12
+* released 0.4
+
+2010-05-08
+* reorganize code to remove circular imports
+* auto-create config settings when they are accessed via square bracket
+ syntax (for example, cfg[x][y]). Previously this raised KeyError.
+
+2010-05-07
+* Ensure that __special__ method lookups don't cause config attributes
+ to be added.
+
+2010-05-06
+* Fix problems with pickling INIConfig objects. This also fixes
+ multiprocessing problems.
+
2010-04-17
* released 0.3.2
diff --git a/PKG-INFO b/PKG-INFO
index f3ffe4b..31c4ad2 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.0
Name: iniparse
-Version: 0.3.2
+Version: 0.4
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 1a267e6..8de756f 100644
--- a/iniparse/__init__.py
+++ b/iniparse/__init__.py
@@ -3,9 +3,10 @@
# Copyright (c) 2007 Tim Lauridsen <tla@rasmil.dk>
# All Rights Reserved. See LICENSE-PSF & LICENSE for details.
-from ini import INIConfig, tidy, change_comment_syntax
+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, \
diff --git a/iniparse/compat.py b/iniparse/compat.py
index 3de6148..db89ed8 100644
--- a/iniparse/compat.py
+++ b/iniparse/compat.py
@@ -68,17 +68,13 @@ class RawConfigParser(object):
The DEFAULT section is not acknowledged.
"""
- try:
- self.data[section]
- return True
- except KeyError:
- return False
+ return (section in self.data)
def options(self, section):
"""Return a list of option names for the given section name."""
- try:
+ if section in self.data:
return list(self.data[section])
- except KeyError:
+ else:
raise NoSectionError(section)
def read(self, filenames):
@@ -119,19 +115,20 @@ class RawConfigParser(object):
raise NoSectionError(section)
if vars is not None and option in vars:
value = vars[option]
- try:
- sec = self.data[section]
+
+ sec = self.data[section]
+ if option in sec:
return sec._compat_get(option)
- except KeyError:
+ else:
raise NoOptionError(option, section)
def items(self, section):
- try:
+ if section in self.data:
ans = []
for opt in self.data[section]:
ans.append((opt, self.get(section, opt)))
return ans
- except KeyError:
+ else:
raise NoSectionError(section)
def getint(self, section, option):
@@ -151,21 +148,17 @@ class RawConfigParser(object):
def has_option(self, section, option):
"""Check for the existence of a given option in a given section."""
- try:
+ if section in self.data:
sec = self.data[section]
- except KeyError:
+ else:
raise NoSectionError(section)
- try:
- sec[option]
- return True
- except KeyError:
- return False
+ return (option in sec)
def set(self, section, option, value):
"""Set an option."""
- try:
+ if section in self.data:
self.data[section][option] = value
- except KeyError:
+ else:
raise NoSectionError(section)
def write(self, fp):
@@ -174,15 +167,14 @@ class RawConfigParser(object):
def remove_option(self, section, option):
"""Remove an option."""
- try:
+ if section in self.data:
sec = self.data[section]
- except KeyError:
+ else:
raise NoSectionError(section)
- try:
- sec[option]
+ if option in sec:
del sec[option]
return 1
- except KeyError:
+ else:
return 0
def remove_section(self, section):
diff --git a/iniparse/config.py b/iniparse/config.py
index 508dac2..5cfa2ea 100644
--- a/iniparse/config.py
+++ b/iniparse/config.py
@@ -17,7 +17,7 @@ class ConfigNamespace(object):
# Methods that must be implemented by subclasses
- def __getitem__(self, key):
+ def _getitem(self, key):
return NotImplementedError(key)
def __setitem__(self, key, value):
@@ -32,7 +32,15 @@ class ConfigNamespace(object):
def _new_namespace(self, name):
raise NotImplementedError(name)
- # Machinery for converting dotted access into contained access
+ def __contains__(self, key):
+ try:
+ self._getitem(key)
+ except KeyError:
+ return False
+ return True
+
+ # Machinery for converting dotted access into container access,
+ # and automatically creating new sections/namespaces.
#
# To distinguish between accesses of class members and namespace
# keys, we first call object.__getattribute__(). If that succeeds,
@@ -43,10 +51,18 @@ class ConfigNamespace(object):
# not just in the __init__() function. See BasicNamespace for
# an example.
+ def __getitem__(self, key):
+ try:
+ return self._getitem(key)
+ except KeyError:
+ return Undefined(key, self)
+
def __getattr__(self, name):
try:
- return self.__getitem__(name)
+ return self._getitem(name)
except KeyError:
+ if name.startswith('__') and name.endswith('__'):
+ raise AttributeError
return Undefined(name, self)
def __setattr__(self, name, value):
@@ -63,9 +79,10 @@ class ConfigNamespace(object):
except AttributeError:
self.__delitem__(name)
- def __getstate__(self):
- return self.__dict__
-
+ # During unpickling, Python checks if the class has a __setstate__
+ # method. But, the data dicts have not been initialised yet, which
+ # leads to _getitem and hence __getattr__ raising an exception. So
+ # we explicitly impement default __setstate__ behavior.
def __setstate__(self, state):
self.__dict__.update(state)
@@ -85,6 +102,10 @@ class Undefined(object):
obj = self.namespace._new_namespace(self.name)
obj[name] = value
+ def __setitem__(self, name, value):
+ obj = self.namespace._new_namespace(self.name)
+ obj[name] = value
+
# ---- Basic implementation of a ConfigNamespace
@@ -164,7 +185,7 @@ class BasicConfig(ConfigNamespace):
def __init__(self):
self._data = {}
- def __getitem__(self, key):
+ def _getitem(self, key):
return self._data[key]
def __setitem__(self, key, value):
@@ -215,11 +236,11 @@ class BasicConfig(ConfigNamespace):
name_components = name.split('.')
ns = self
for n in name_components[:-1]:
- try:
+ if n in ns:
ns = ns[n]
if not isinstance(ns, ConfigNamespace):
raise TypeError('value-namespace conflict', n)
- except KeyError:
+ else:
ns = ns._new_namespace(n)
ns[name_components[-1]] = value
@@ -259,11 +280,11 @@ def update_config(target, source):
for name in source:
value = source[name]
if isinstance(value, ConfigNamespace):
- try:
+ if name in target:
myns = target[name]
if not isinstance(myns, ConfigNamespace):
raise TypeError('value-namespace conflict')
- except KeyError:
+ else:
myns = target._new_namespace(name)
update_config(myns, value)
else:
diff --git a/iniparse/ini.py b/iniparse/ini.py
index f0e7ec2..408354d 100644
--- a/iniparse/ini.py
+++ b/iniparse/ini.py
@@ -45,7 +45,6 @@ import re
from ConfigParser import DEFAULTSECT, ParsingError, MissingSectionHeaderError
import config
-import compat
class LineType(object):
line = None
@@ -352,7 +351,7 @@ class INISection(config.ConfigNamespace):
value = re.sub('\n+', '\n', value)
return value
- def __getitem__(self, key):
+ def _getitem(self, key):
if key == '__name__':
return self._lines[-1].name
if self._optionxform: key = self._optionxform(key)
@@ -474,7 +473,7 @@ class INIConfig(config.ConfigNamespace):
_optionxform = _make_xform_property('_optionxform', 'optionxform')
_sectionxform = _make_xform_property('_sectionxform', 'optionxform')
- def __getitem__(self, key):
+ def _getitem(self, key):
if key == DEFAULTSECT:
return self._defaults
if self._sectionxform: key = self._sectionxform(key)
@@ -642,47 +641,3 @@ class INIConfig(config.ConfigNamespace):
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/iniparse/utils.py b/iniparse/utils.py
new file mode 100644
index 0000000..829fc28
--- /dev/null
+++ b/iniparse/utils.py
@@ -0,0 +1,47 @@
+import compat
+from ini import LineContainer, EmptyLine
+
+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 b9dda79..4ba6bef 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.2
+Version: 0.4
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 Jun 12 2010 Paramjit Oberoi <param@cs.wisc.edu> - 0.4-1
+- Release 0.4
* 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
diff --git a/setup.py b/setup.py
index c2c477d..736cfa1 100644
--- a/setup.py
+++ b/setup.py
@@ -2,7 +2,7 @@
from distutils.core import setup
-VERSION = '0.3.2'
+VERSION = '0.4'
setup(name ='iniparse',
version = VERSION,
diff --git a/tests/__init__.py b/tests/__init__.py
index 1269812..f1fa321 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -6,6 +6,7 @@ import test_fuzz
import test_compat
import test_unicode
import test_tidy
+import test_multiprocessing
from iniparse import config
from iniparse import ini
@@ -20,4 +21,5 @@ class suite(unittest.TestSuite):
test_compat.suite(),
test_unicode.suite(),
test_tidy.suite(),
+ test_multiprocessing.suite(),
])
diff --git a/tests/test_fuzz.py b/tests/test_fuzz.py
index f63a718..5420dcc 100644
--- a/tests/test_fuzz.py
+++ b/tests/test_fuzz.py
@@ -4,7 +4,7 @@ import random
import unittest
import ConfigParser
from StringIO import StringIO
-from iniparse import compat, ini
+from iniparse import compat, ini, tidy
# TODO:
# tabs
@@ -101,7 +101,7 @@ class test_fuzz(unittest.TestCase):
# compare the two configparsers
self.assertEqualConfig(cc_py, cc)
# check that tidy does not change semantics
- ini.tidy(cc)
+ tidy(cc)
cc_tidy = ConfigParser.RawConfigParser()
cc_tidy.readfp(StringIO(str(cc.data)))
self.assertEqualConfig(cc_py, cc_tidy)
diff --git a/tests/test_ini.py b/tests/test_ini.py
index bdb9080..6a76edb 100644
--- a/tests/test_ini.py
+++ b/tests/test_ini.py
@@ -223,8 +223,14 @@ but = also me
p = ini.INIConfig(sio)
p.new1.created = 1
setattr(getattr(p, 'new2'), 'created', 1)
+ p.new3['created'] = 1
+ p['new4'].created = 1
+ p['new5']['created'] = 1
self.assertEqual(p.new1.created, 1)
self.assertEqual(p.new2.created, 1)
+ self.assertEqual(p.new3.created, 1)
+ self.assertEqual(p.new4.created, 1)
+ self.assertEqual(p.new5.created, 1)
def test_order(self):
sio = StringIO(self.s1)
diff --git a/tests/test_misc.py b/tests/test_misc.py
index 482c5c4..31cf4da 100644
--- a/tests/test_misc.py
+++ b/tests/test_misc.py
@@ -385,19 +385,19 @@ class test_pickle(unittest.TestCase):
c = cfg_class()
c.readfp(StringIO(self.s))
self.do_compat_checks(c)
- p = pickle.dumps(c)
- c = None
- c2 = pickle.loads(p)
- self.do_compat_checks(c2)
+ for i in range(0, pickle.HIGHEST_PROTOCOL+1):
+ p = pickle.dumps(c, protocol=i)
+ c2 = pickle.loads(p)
+ self.do_compat_checks(c2)
def test_ini(self):
c = ini.INIConfig()
c._readfp(StringIO(self.s))
self.do_ini_checks(c)
- p = pickle.dumps(c)
- c = None
- c2 = pickle.loads(p)
- self.do_ini_checks(c2)
+ for i in range(0, pickle.HIGHEST_PROTOCOL+1):
+ p = pickle.dumps(c, protocol=i)
+ c2 = pickle.loads(p)
+ self.do_ini_checks(c2)
class test_comment_syntax(unittest.TestCase):
"""Test changing comment syntax with change_comment_syntax"""
diff --git a/tests/test_multiprocessing.py b/tests/test_multiprocessing.py
new file mode 100644
index 0000000..f95a569
--- /dev/null
+++ b/tests/test_multiprocessing.py
@@ -0,0 +1,33 @@
+import unittest
+try:
+ from multiprocessing import Process, Queue, Pipe
+ disabled = False
+except ImportError:
+ disabled = True
+
+from iniparse import compat, ini
+
+class test_ini(unittest.TestCase):
+ """Test sending INIConfig objects."""
+
+ def test_queue(self):
+ def getxy(q, w):
+ cfg = q.get_nowait()
+ w.put(cfg.x.y)
+ cfg = ini.INIConfig()
+ cfg.x.y = '42'
+ q = Queue()
+ w = Queue()
+ q.put(cfg)
+ p = Process(target=getxy, args=(q, w))
+ p.start()
+ self.assertEqual(w.get(timeout=1), '42')
+
+class suite(unittest.TestSuite):
+ def __init__(self):
+ if disabled:
+ unittest.TestSuite.__init__(self, [])
+ else:
+ unittest.TestSuite.__init__(self, [
+ unittest.makeSuite(test_ini, 'test'),
+ ])
diff --git a/tests/test_tidy.py b/tests/test_tidy.py
index 4990457..7304747 100644
--- a/tests/test_tidy.py
+++ b/tests/test_tidy.py
@@ -2,7 +2,8 @@ import unittest
from textwrap import dedent
from StringIO import StringIO
-from iniparse.ini import INIConfig, EmptyLine, tidy
+from iniparse import tidy,INIConfig
+from iniparse.ini import EmptyLine
from iniparse.compat import ConfigParser
class test_tidy(unittest.TestCase):