Commits

Daniel Neuhäuser committed 53a7a2d

Added :func:`ini.load` and :func:`ini.dump` as well as tests for both functions.

  • Participants
  • Parent commits 2983ffa

Comments (0)

Files changed (2)

 """
     ini
     ~~~
+    This module provides simple and easy loading and dumping of configuration
+    files in ini format.
 
     :copyright: 2010 by the INI Team, see AUTHORS for details.
     :license: MIT, see LICENSE for details.
     def __repr__(self):
         return "{0}({1!r})".format(self.__class__.__name__, self.items())
 
-__all__ = ["OrderedDict"]
+def load(ini_file, section_seperator="."):
+    """
+    Parses the given `ini_file` which has to be an iterable over the lines of
+    the ini formatted file or string and returns a nested :class:`OrderedDict`.
+
+    If you have nested sections this function will parse and nest them
+    appropriately. Just specify the character you use with the
+    `section_seperator` keyword argument.
+    """
+    rv = OrderedDict()
+    for line in ini_file:
+        line = line.strip()
+        if not line or line.startswith("#"):
+            continue
+        elif line[0] == "[" and line[-1] == "]":
+            sections = line[1:-1].split(section_seperator)
+            container = rv
+            for section in sections:
+                container = container.setdefault(section, OrderedDict())
+        else:
+            key, value = line.split("=")
+            container[key.strip()] = value.strip()
+    return rv
+
+def _encode_flat(d, seperator):
+    rv = OrderedDict()
+    for key, value in d.iteritems():
+        if isinstance(value, dict):
+            for k, v in _encode_flat(value, seperator).iteritems():
+                rv[key + seperator + k] = v
+        else:
+            rv[key] = value
+    return rv
+
+def dump(d, ini_file, section_seperator="."):
+    """
+    Dumps the arbitary deeply nested dictionary `d` to the given `ini_file`
+    in ini format.
+    
+    Use the `section_seperator` keyword argument to specify which character
+    should be used to join nested sections.
+    """
+    flat_d = _encode_flat(d, section_seperator)
+    dumpable = OrderedDict()
+    for key, value in flat_d.iteritems():
+        section, key = key.rsplit(section_seperator, 1)
+        dumpable.setdefault(section, OrderedDict())[key] = value
+    for section, items in dumpable.iteritems():
+        ini_file.write("[{0}]\n".format(section))
+        for key, value in items.iteritems():
+            ini_file.write("{0} = {1}\n".format(key, value))
+
+__all__ = ["OrderedDict", "load", "dump"]
     :copyright: 2010 by the INI Team, see AUTHORS for details.
     :license: MIT, see LICENSE for details.
 """
-from ini import OrderedDict
+from StringIO import StringIO
+
+from ini import OrderedDict, load, dump
 
 def make_test_data():
     return zip(range(10), range(10))
         assert "some key" not in d
         assert d.setdefault("some key", "some value") == "some value"
         assert d["some key"] == "some value"
+
+class TestLoad(object):
+    def test_flat_file(self):
+        ini_file = StringIO(
+        "[spam]\n"
+        "foo = bar\n"
+        "spam = eggs\n"
+        "[eggs]\n"
+        "monty = python\n"
+        )
+        assert load(ini_file) == {
+            "spam": {
+                "foo": "bar",
+                "spam": "eggs",
+            },
+            "eggs": {
+                "monty": "python",
+            },
+        }
+
+    def test_nested_file(self):
+        ini_file = StringIO(
+            "[spam]\n"
+            "foo = bar\n"
+            "[spam.eggs]\n"
+            "monty = python\n"
+            "[foo]\n"
+            "bar = baz\n"
+        )
+        assert load(ini_file) == {
+            "spam": {
+                "foo": "bar",
+                "eggs": {
+                    "monty": "python",
+                },
+            },
+            "foo": {
+                "bar": "baz",
+            },
+        }
+
+class TestDump(object):
+    def test_flat(self):
+        data = {
+            "foo": {
+                "bar": "baz",
+            },
+            "bar": {
+                "spam": "eggs",
+            },
+        }
+        expected = (
+            "[foo]\n"
+            "bar = baz\n"
+            "[bar]\n"
+            "spam = eggs\n"
+        )
+        test_file = StringIO()
+        dump(data, test_file)
+        assert test_file.getvalue() == expected
+
+    def test_nested(self):
+        data = {
+            "foo": {
+                "bar": {
+                    "spam": "eggs",
+                },
+            },
+            "bar": {
+                "monty": "python",
+            },
+        }
+        expected = (
+            "[foo.bar]\n"
+            "spam = eggs\n"
+            "[bar]\n"
+            "monty = python\n"
+        )
+        test_file = StringIO()
+        dump(data, test_file)
+        assert test_file.getvalue() == expected