diff --git a/docs/config.rst b/docs/config.rst index e69fbca..ef3a1a5 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -10,9 +10,17 @@ The Config type .. automethod:: pygit2.Config.get_system_config .. automethod:: pygit2.Config.get_global_config -.. automethod:: pygit2.Config.foreach .. automethod:: pygit2.Config.add_file .. automethod:: pygit2.Config.get_multivar .. automethod:: pygit2.Config.set_multivar The :class:`Config` Mapping interface. + +Iterator +========= + +The :class:`Config` class has an iterator which can be used to loop +through all the entries in the configuration. Each element is a tuple +containing the name and the value of each configuration variable. Be +aware that this may return multiple versions of each entry if they are +set multiple times in the configuration files. diff --git a/src/config.c b/src/config.c index 873ba73..f767558 100644 --- a/src/config.c +++ b/src/config.c @@ -33,6 +33,7 @@ #include "config.h" extern PyTypeObject ConfigType; +extern PyTypeObject ConfigIterType; PyObject * @@ -239,70 +240,6 @@ Config_setitem(Config *self, PyObject *py_key, PyObject *py_value) return 0; } -int -Config_foreach_callback_wrapper(const git_config_entry *entry, void *c_payload) -{ - PyObject *args = (PyObject *)c_payload; - PyObject *py_callback = NULL; - PyObject *py_payload = NULL; - PyObject *py_result = NULL; - int c_result; - - if (!PyArg_ParseTuple(args, "O|O", &py_callback, &py_payload)) - return -1; - - if (py_payload) - args = Py_BuildValue("ssO", entry->name, entry->value, py_payload); - else - args = Py_BuildValue("ss", entry->name, entry->value); - if (!args) - return -1; - - if (!(py_result = PyObject_CallObject(py_callback, args))) - return -1; - - if ((c_result = PyLong_AsLong(py_result)) == -1) - return -1; - - Py_CLEAR(args); - - return c_result; -} - - -PyDoc_STRVAR(Config_foreach__doc__, - "foreach(callback[, payload]) -> int\n" - "\n" - "Perform an operation on each config variable.\n" - "\n" - "The callback must be of type Callable and receives the normalized name\n" - "and value of each variable in the config backend, and an optional payload\n" - "passed to this method. As soon as one of the callbacks returns an integer\n" - "other than 0, this function returns that value."); - -PyObject * -Config_foreach(Config *self, PyObject *args) -{ - int ret; - PyObject *py_callback; - PyObject *py_payload = NULL; - - if (!PyArg_ParseTuple(args, "O|O", &py_callback, &py_payload)) - return NULL; - - if (!PyCallable_Check(py_callback)) { - PyErr_SetString(PyExc_TypeError, - "Argument 'callback' is not callable"); - return NULL; - } - - ret = git_config_foreach(self->config, Config_foreach_callback_wrapper, - (void *)args); - - return PyLong_FromLong((long)ret); -} - - PyDoc_STRVAR(Config_add_file__doc__, "add_file(path, level=0, force=0)\n" "\n" @@ -407,10 +344,28 @@ Config_set_multivar(Config *self, PyObject *args) Py_RETURN_NONE; } +PyObject * +Config_iter(Config *self) +{ + ConfigIter *iter; + int err; + + iter = PyObject_New(ConfigIter, &ConfigIterType); + if (!iter) + return NULL; + + if ((err = git_config_iterator_new(&iter->iter, self->config)) < 0) + return Error_set(err); + + Py_INCREF(self); + iter->owner = self; + + return (PyObject*)iter; +} + PyMethodDef Config_methods[] = { METHOD(Config, get_system_config, METH_NOARGS | METH_STATIC), METHOD(Config, get_global_config, METH_NOARGS | METH_STATIC), - METHOD(Config, foreach, METH_VARARGS), METHOD(Config, add_file, METH_VARARGS | METH_KEYWORDS), METHOD(Config, get_multivar, METH_VARARGS), METHOD(Config, set_multivar, METH_VARARGS), @@ -463,7 +418,7 @@ PyTypeObject ConfigType = { 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ + (getiterfunc)Config_iter, /* tp_iter */ 0, /* tp_iternext */ Config_methods, /* tp_methods */ 0, /* tp_members */ @@ -477,3 +432,56 @@ PyTypeObject ConfigType = { 0, /* tp_alloc */ 0, /* tp_new */ }; + +void +ConfigIter_dealloc(ConfigIter *self) +{ + Py_CLEAR(self->owner); + git_config_iterator_free(self->iter); + PyObject_Del(self); +} + +PyObject * +ConfigIter_iternext(ConfigIter *self) +{ + int err; + git_config_entry *entry; + + if ((err = git_config_next(&entry, self->iter)) < 0) + return Error_set(err); + + return Py_BuildValue("ss", entry->name, entry->value); +} + +PyDoc_STRVAR(ConfigIter__doc__, "Configuration iterator."); + +PyTypeObject ConfigIterType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_pygit2.ConfigIter", /* tp_name */ + sizeof(ConfigIter), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)ConfigIter_dealloc , /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + ConfigIter__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)ConfigIter_iternext, /* tp_iternext */ + +}; diff --git a/src/pygit2.c b/src/pygit2.c index e640ea8..97eb421 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -56,6 +56,7 @@ extern PyTypeObject IndexEntryType; extern PyTypeObject IndexIterType; extern PyTypeObject WalkerType; extern PyTypeObject ConfigType; +extern PyTypeObject ConfigIterType; extern PyTypeObject ReferenceType; extern PyTypeObject RefLogIterType; extern PyTypeObject RefLogEntryType; @@ -412,7 +413,9 @@ moduleinit(PyObject* m) /* Config */ INIT_TYPE(ConfigType, NULL, PyType_GenericNew) + INIT_TYPE(ConfigIterType, NULL, PyType_GenericNew) ADD_TYPE(m, Config) + ADD_TYPE(m, ConfigIter) /* Remotes */ INIT_TYPE(RemoteType, NULL, NULL) diff --git a/src/types.h b/src/types.h index c864959..94ce790 100644 --- a/src/types.h +++ b/src/types.h @@ -77,6 +77,11 @@ typedef struct { git_config* config; } Config; +typedef struct { + PyObject_HEAD + Config *owner; + git_config_iterator *iter; +} ConfigIter; /* git_note */ typedef struct { diff --git a/test/test_config.py b/test/test_config.py index 8740a64..4a22f40 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -36,13 +36,6 @@ from . import utils CONFIG_FILENAME = "test_config" - -def foreach_test_wrapper(key, name, lst): - lst[key] = name - return 0 -foreach_test_wrapper.__test__ = False - - class ConfigTest(utils.RepoTestCase): def tearDown(self): @@ -175,13 +168,15 @@ class ConfigTest(utils.RepoTestCase): for i in l: self.assertEqual(i, 'foo-123456') - def test_foreach(self): + def test_iterator(self): config = self.repo.config lst = {} - config.foreach(foreach_test_wrapper, lst) + + for name, value in config: + lst[name] = value + self.assertTrue('core.bare' in lst) self.assertTrue(lst['core.bare']) - if __name__ == '__main__': unittest.main()