Skip to content

Commit 73e9e58

Browse files
committed
Config: make bool and int parsing explicit via functions
Passing a tuple to the mapping interface isn't the best of interfaces, as the key is only the string. Instead, expose `Config.get_bool()` and `Config.get_int()` methods to parse the values as per the git-config rules before returning the appropriate type to the user. The mapping interface itself returns a string.
1 parent 687dc53 commit 73e9e58

3 files changed

Lines changed: 88 additions & 71 deletions

File tree

docs/config.rst

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,9 @@ The Config type
2424

2525
The :class:`Config` Mapping interface.
2626

27-
Parsing the values
28-
===================
27+
When using the mapping interface, the value is returned as a
28+
string. In order to apply the git-config parsing rules, you can use
29+
:method:`Config.get_bool` or :method:`Config.get_int`.
2930

30-
Instead of a string, a tuple of `(str,type)` can be used to look up a
31-
key and parse it through the Git rules. E.g.
32-
33-
config['core.bare',bool]
34-
35-
will return True if 'core.bare' is truthy.
36-
37-
Truty values are: 'true', 1, 'on' or 'yes'. Falsy values are: 'false',
38-
0, 'off' and 'no'.
39-
40-
Available types are `bool` and `int`. Not specifying a type returns a
41-
string.
31+
.. automethod:: pygit2.Config.get_bool
32+
.. automethod:: pygit2.Config.get_int

src/config.c

Lines changed: 77 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -168,70 +168,94 @@ Config_contains(Config *self, PyObject *py_key) {
168168
return 1;
169169
}
170170

171-
172-
PyObject *
173-
Config_getitem(Config *self, PyObject *py_input_key)
171+
/* Get the C string value given a python string as key */
172+
static int
173+
get_string(const char **key_out, Config *self, PyObject *py_key)
174174
{
175-
int err;
176-
const char *value_str;
175+
PyObject *tkey;
177176
const char *key;
178-
PyObject *py_key, *py_value, *tkey, *tmp_type = NULL;
179-
PyTypeObject *py_type = NULL;
177+
int err;
180178

181-
if (PyTuple_Check(py_input_key) && PyTuple_Size(py_input_key) == 2) {
182-
py_key = PyTuple_GetItem(py_input_key, 0);
183-
tmp_type = PyTuple_GetItem(py_input_key, 1);
184-
} else {
185-
py_key = py_input_key;
179+
key = py_str_borrow_c_str(&tkey, py_key, NULL);
180+
if (key == NULL)
181+
return -1;
182+
183+
err = git_config_get_string(key_out, self->config, key);
184+
Py_CLEAR(tkey);
185+
186+
if (err == GIT_ENOTFOUND) {
187+
PyErr_SetObject(PyExc_KeyError, py_key);
188+
return -1;
186189
}
187190

188-
/* If passed a tuple, make sure the second item is a type */
189-
if (tmp_type) {
190-
if (!PyType_Check(tmp_type))
191-
return NULL;
192-
else
193-
py_type = (PyTypeObject *) tmp_type;
191+
if (err < 0) {
192+
Error_set(err);
193+
return -1;
194194
}
195195

196-
key = py_str_borrow_c_str(&tkey, py_key, NULL);
197-
if (key == NULL)
196+
return 0;
197+
}
198+
199+
PyObject *
200+
Config_getitem(Config *self, PyObject *py_key)
201+
{
202+
int err;
203+
const char *value_str;
204+
205+
err = get_string(&value_str, self, py_key);
206+
if (err < 0)
198207
return NULL;
199208

200-
err = git_config_get_string(&value_str, self->config, key);
201-
Py_CLEAR(tkey);
209+
return to_unicode(value_str, NULL, NULL);
210+
}
211+
212+
PyDoc_STRVAR(Config_get_bool__doc__,
213+
"get_bool(key) -> Bool\n"
214+
"\n"
215+
"Look up *key* and parse its value as a boolean as per the git-config rules\n"
216+
"\n"
217+
"Truthy values are: 'true', 1, 'on' or 'yes'. Falsy values are: 'false',\n"
218+
"0, 'off' and 'no'");
219+
220+
PyObject *
221+
Config_get_bool(Config *self, PyObject *key)
222+
{
223+
int err, value;
224+
const char *value_str;
225+
226+
err = get_string(&value_str, self, key);
202227
if (err < 0)
203-
goto cleanup;
204-
205-
/* If the user specified a type, let's parse it */
206-
if (py_type) {
207-
if (py_type == &PyBool_Type) {
208-
int value;
209-
if ((err = git_config_parse_bool(&value, value_str)) < 0)
210-
goto cleanup;
211-
212-
py_value = PyBool_FromLong(value);
213-
} else if (py_type == &PyInteger_Type) {
214-
int64_t value;
215-
if ((err = git_config_parse_int64(&value, value_str)) < 0)
216-
goto cleanup;
217-
218-
py_value = PyLong_FromLongLong(value);
219-
}
220-
} else {
221-
py_value = to_unicode(value_str, NULL, NULL);
222-
}
228+
return NULL;
223229

224-
cleanup:
225-
if (err < 0) {
226-
if (err == GIT_ENOTFOUND) {
227-
PyErr_SetObject(PyExc_KeyError, py_key);
228-
return NULL;
229-
}
230+
if ((err = git_config_parse_bool(&value, value_str)) < 0)
231+
return NULL;
230232

231-
return Error_set(err);
232-
}
233+
return PyBool_FromLong(value);
234+
}
235+
236+
PyDoc_STRVAR(Config_get_int__doc__,
237+
"get_int(key) -> int\n"
238+
"\n"
239+
"Look up *key* and parse its value as an integer as per the git-config rules\n"
240+
"\n"
241+
"A value can have a suffix 'k', 'm' or 'g' which stand for 'kilo', 'mega' and\n"
242+
"'giga' respectively");
243+
244+
PyObject *
245+
Config_get_int(Config *self, PyObject *key)
246+
{
247+
int err;
248+
int64_t value;
249+
const char *value_str;
250+
251+
err = get_string(&value_str, self, key);
252+
if (err < 0)
253+
return NULL;
254+
255+
if ((err = git_config_parse_int64(&value, value_str)) < 0)
256+
return NULL;
233257

234-
return py_value;
258+
return PyLong_FromLongLong(value);
235259
}
236260

237261
int
@@ -396,6 +420,8 @@ PyMethodDef Config_methods[] = {
396420
METHOD(Config, add_file, METH_VARARGS | METH_KEYWORDS),
397421
METHOD(Config, get_multivar, METH_VARARGS),
398422
METHOD(Config, set_multivar, METH_VARARGS),
423+
METHOD(Config, get_bool, METH_O),
424+
METHOD(Config, get_int, METH_O),
399425
{NULL}
400426
};
401427

test/test_config.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ def test_new(self):
7474

7575
config_read = Config(CONFIG_FILENAME)
7676
self.assertTrue('core.bare' in config_read)
77-
self.assertFalse(config_read['core.bare',bool])
77+
self.assertFalse(config_read.get_bool('core.bare'))
7878
self.assertTrue('core.editor' in config_read)
7979
self.assertEqual(config_read['core.editor'], 'ed')
8080

@@ -88,9 +88,9 @@ def test_add(self):
8888

8989
config.add_file(CONFIG_FILENAME, 0)
9090
self.assertTrue('this.that' in config)
91-
self.assertTrue(config['this.that',bool])
91+
self.assertTrue(config.get_bool('this.that'))
9292
self.assertTrue('something.other.here' in config)
93-
self.assertFalse(config['something.other.here',bool])
93+
self.assertFalse(config.get_bool('something.other.here'))
9494

9595
def test_read(self):
9696
config = self.repo.config
@@ -103,11 +103,11 @@ def test_read(self):
103103
lambda: config['abc.def'])
104104

105105
self.assertTrue('core.bare' in config)
106-
self.assertFalse(config['core.bare',bool])
106+
self.assertFalse(config.get_bool('core.bare'))
107107
self.assertTrue('core.editor' in config)
108108
self.assertEqual(config['core.editor'], 'ed')
109109
self.assertTrue('core.repositoryformatversion' in config)
110-
self.assertEqual(config['core.repositoryformatversion',int], 0)
110+
self.assertEqual(config.get_int('core.repositoryformatversion'), 0)
111111

112112
new_file = open(CONFIG_FILENAME, "w")
113113
new_file.write("[this]\n\tthat = foobar\n\tthat = foobeer\n")
@@ -129,7 +129,7 @@ def test_write(self):
129129
self.assertFalse('core.dummy1' in config)
130130
config['core.dummy1'] = 42
131131
self.assertTrue('core.dummy1' in config)
132-
self.assertEqual(config['core.dummy1',int], 42)
132+
self.assertEqual(config.get_int('core.dummy1'), 42)
133133

134134
self.assertFalse('core.dummy2' in config)
135135
config['core.dummy2'] = 'foobar'

0 commit comments

Comments
 (0)