From e126b09380c44d87967a284bca3427450990bd2d Mon Sep 17 00:00:00 2001
From: Martin Lenders <mlenders@elegosoft.com>
Date: Fri, 8 Jun 2012 17:11:10 +0200
Subject: [PATCH] Implement dictionary-like behaviour for Config

---
 include/pygit2/config.h       |   2 +
 src/pygit2/config.c           | 105 +++++++++++++++++++++++++++++++++-
 test/data/testrepo.git/config |   1 +
 test/data/testrepo.tar        | Bin 61440 -> 61440 bytes
 test/test_config.py           |  62 ++++++++++++++++++++
 5 files changed, 168 insertions(+), 2 deletions(-)

diff --git a/include/pygit2/config.h b/include/pygit2/config.h
index d281902..9d6017f 100644
--- a/include/pygit2/config.h
+++ b/include/pygit2/config.h
@@ -8,5 +8,7 @@
 PyObject* Config_get_global_config(void);
 PyObject* Config_get_system_config(void);
 PyObject* Config_add_file(Config *self, PyObject *args);
+PyObject* Config_getitem(Config *self, PyObject *key);
+int Config_setitem(Config *self, PyObject *key, PyObject *value);
 
 #endif
diff --git a/src/pygit2/config.c b/src/pygit2/config.c
index d05f1a7..b18977a 100644
--- a/src/pygit2/config.c
+++ b/src/pygit2/config.c
@@ -102,6 +102,90 @@ Config_get_system_config(void)
     return Config_open(path);
 }
 
+int
+Config_contains(Config *self, PyObject *py_key) {
+    int err;
+    const char *c_value;
+    const char *c_key;
+
+    if (!(c_key = py_str_to_c_str(py_key,NULL)))
+        return -1;
+
+    err = git_config_get_string(&c_value, self->config, c_key);
+
+    if (err == GIT_ENOTFOUND)
+        return 0;
+    if (err < 0) {
+        Error_set(err);
+        return -1;
+    }
+
+    return 1;
+}
+
+PyObject *
+Config_getitem(Config *self, PyObject *py_key)
+{
+    int err;
+    int64_t       c_intvalue;
+    int           c_boolvalue;
+    const char   *c_charvalue;
+    const char   *c_key;
+
+    if (!(c_key = py_str_to_c_str(py_key,NULL)))
+        return NULL;
+
+    err = git_config_get_int64(&c_intvalue, self->config, c_key);
+    if (err == GIT_OK) {
+        return PyInt_FromLong((long)c_intvalue);
+    }
+
+    err = git_config_get_bool(&c_boolvalue, self->config, c_key);
+    if (err == GIT_OK) {
+        return PyBool_FromLong((long)c_boolvalue);
+    }
+
+    err = git_config_get_string(&c_charvalue, self->config, c_key);
+    if (err < 0) {
+        if (err == GIT_ENOTFOUND) {
+            PyErr_SetObject(PyExc_KeyError, py_key);
+            return NULL;
+        }
+        return Error_set(err);
+    }
+
+    return PyUnicode_FromString(c_charvalue);
+}
+
+int
+Config_setitem(Config *self, PyObject *py_key, PyObject *py_value)
+{
+    int err;
+    const char *c_key;
+
+    if (!(c_key = py_str_to_c_str(py_key,NULL)))
+        return -1;
+
+    if (!py_value) {
+        err = git_config_delete(self->config, c_key);
+    } else if (PyBool_Check(py_value)) {
+        err = git_config_set_bool(self->config, c_key,
+                (int)PyObject_IsTrue(py_value));
+    } else if (PyInt_Check(py_value)) {
+        err = git_config_set_int64(self->config, c_key,
+                (int64_t)PyInt_AsLong(py_value));
+    } else {
+        py_value = PyObject_Str(py_value);
+        err = git_config_set_string(self->config, c_key,
+                py_str_to_c_str(py_value,NULL));
+    }
+    if (err < 0) {
+        Error_set(err);
+        return -1;
+    }
+    return 0;
+}
+
 PyObject *
 Config_add_file(Config *self, PyObject *args)
 {
@@ -133,6 +217,23 @@ PyMethodDef Config_methods[] = {
     {NULL}
 };
 
+PySequenceMethods Config_as_sequence = {
+    0,                          /* sq_length */
+    0,                          /* sq_concat */
+    0,                          /* sq_repeat */
+    0,                          /* sq_item */
+    0,                          /* sq_slice */
+    0,                          /* sq_ass_item */
+    0,                          /* sq_ass_slice */
+    (objobjproc)Config_contains,/* sq_contains */
+};
+
+PyMappingMethods Config_as_mapping = {
+    0,                               /* mp_length */
+    (binaryfunc)Config_getitem,      /* mp_subscript */
+    (objobjargproc)Config_setitem,   /* mp_ass_subscript */
+};
+
 PyTypeObject ConfigType = {
     PyVarObject_HEAD_INIT(NULL, 0)
     "_pygit2.Config",                          /* tp_name           */
@@ -145,8 +246,8 @@ PyTypeObject ConfigType = {
     0,                                         /* tp_compare        */
     0,                                         /* tp_repr           */
     0,                                         /* tp_as_number      */
-    0,                                         /* tp_as_sequence    */
-    0,                                         /* tp_as_mapping     */
+    &Config_as_sequence,                       /* tp_as_sequence    */
+    &Config_as_mapping,                        /* tp_as_mapping     */
     0,                                         /* tp_hash           */
     0,                                         /* tp_call           */
     0,                                         /* tp_str            */
diff --git a/test/data/testrepo.git/config b/test/data/testrepo.git/config
index 83142d2..6a442c4 100644
--- a/test/data/testrepo.git/config
+++ b/test/data/testrepo.git/config
@@ -2,5 +2,6 @@
 	repositoryformatversion = 0
 	filemode = true
 	bare = true
+	editor = 'ed'
 [gc]
 	auto = no
diff --git a/test/data/testrepo.tar b/test/data/testrepo.tar
index 1b613a8d20cadb866c663c2fb87361d0f0fff861..a064aa21bd9b68483d83340d58b587ada87eccd9 100644
GIT binary patch
delta 219
zcmZp8z})bFc>`a<X1<h9$&(T?8O<hViAYPBnlKm|nwy!J7#bUz8#5Rfniv}!Gbk8L
zHe^oWEiEodEK*Ql$W1IN$(*bhAk7M6IZt+A?w^ztBcK2TsVSKy`9%u0KnB-l#Yy)W
PHycd4Kp(<^V<{s5F>q2}

delta 170
zcmZp8z})bFc>`a<WWFQOlQTtRB#ccM3=K_<%#2LUP0bA%3=B<-Obr<n3?>^gr%YyK
d{6Fbf%w|K5C5(#_QZ_3bdca5zVzLAC0syUjN0k5o

diff --git a/test/test_config.py b/test/test_config.py
index 92b7b60..b9e6aae 100644
--- a/test/test_config.py
+++ b/test/test_config.py
@@ -60,6 +60,17 @@ class ConfigTest(utils.RepoTestCase):
 
         self.assertNotEqual(config_write, None)
 
+        config_write['core.bare'] = False
+        config_write['core.editor'] = 'ed'
+
+        config_read = pygit2.Config(config_filename)
+        self.assertTrue('core.bare' in config_write)
+        self.assertFalse(config_write['core.bare'])
+        self.assertTrue('core.editor' in config_write)
+        self.assertEqual(config_write['core.editor'], 'ed')
+
+        os.remove(config_filename)
+
     def test_add(self):
         config = pygit2.Config.get_global_config()
 
@@ -69,9 +80,60 @@ class ConfigTest(utils.RepoTestCase):
         new_file.close()
 
         config.add_file(config_filename, 0)
+        self.assertTrue('this.that' in config)
+        self.assertTrue(config['this.that'])
+        self.assertTrue('something.other.here' in config)
+        self.assertFalse(config['something.other.here'])
 
         os.remove(config_filename)
 
+    def test_read(self):
+        config = self.repo.config
+
+        self.assertRaises(TypeError, lambda: config[()])
+        self.assertRaises(TypeError, lambda: config[-4])
+        self.assertRaisesWithArg(pygit2.GitError,
+                "Invalid variable name: 'abc'", lambda: config['abc'])
+        self.assertRaisesWithArg(KeyError, 'abc.def', lambda: config['abc.def'])
+
+        self.assertTrue('core.bare' in config)
+        self.assertFalse(config['core.bare'])
+        self.assertTrue('core.editor' in config)
+        self.assertEqual(config['core.editor'], 'ed')
+        self.assertTrue('core.repositoryformatversion' in config)
+        self.assertEqual(config['core.repositoryformatversion'], 0)
+
+        new_file = open(config_filename, "w")
+        new_file.write("[this]\n\tthat = foobar\n\tthat = foobeer\n")
+        new_file.close()
+
+    def test_write(self):
+        config = self.repo.config
+
+        with self.assertRaises(TypeError):
+            config[()] = 'This should not work'
+
+        self.assertFalse('core.dummy1' in config)
+        config['core.dummy1'] = 42
+        self.assertTrue('core.dummy1' in config)
+        self.assertEqual(config['core.dummy1'], 42)
+
+        self.assertFalse('core.dummy2' in config)
+        config['core.dummy2'] = 'foobar'
+        self.assertTrue('core.dummy2' in config)
+        self.assertEqual(config['core.dummy2'], 'foobar')
+
+        self.assertFalse('core.dummy3' in config)
+        config['core.dummy3'] = True
+        self.assertTrue('core.dummy3' in config)
+        self.assertTrue(config['core.dummy3'])
+
+        del config['core.dummy1']
+        self.assertFalse('core.dummy1' in config)
+        del config['core.dummy2']
+        self.assertFalse('core.dummy2' in config)
+        del config['core.dummy3']
+        self.assertFalse('core.dummy3' in config)
 
 
 if __name__ == '__main__':