config: make type conversion explicit

The type of a config value depends on the tool that interprets
it. Parsing eagerly can lead to a situation where we return a bool
instead of a string or a number.

Let the user specify the type themselves by passing in a (str, type)
tuple into the mapping interface.
This commit is contained in:
Carlos Martín Nieto
2014-03-27 16:53:16 +01:00
parent 607d4810f6
commit 687dc5388e
4 changed files with 62 additions and 17 deletions

View File

@@ -23,3 +23,19 @@ The Config type
set multiple times in the configuration files. set multiple times in the configuration files.
The :class:`Config` Mapping interface. The :class:`Config` Mapping interface.
Parsing the values
===================
Instead of a string, a tuple of `(str,type)` can be used to look up a
key and parse it through the Git rules. E.g.
config['core.bare',bool]
will return True if 'core.bare' is truthy.
Truty values are: 'true', 1, 'on' or 'yes'. Falsy values are: 'false',
0, 'off' and 'no'.
Available types are `bool` and `int`. Not specifying a type returns a
string.

View File

@@ -170,29 +170,56 @@ Config_contains(Config *self, PyObject *py_key) {
PyObject * PyObject *
Config_getitem(Config *self, PyObject *py_key) Config_getitem(Config *self, PyObject *py_input_key)
{ {
int64_t value_int; int err;
int err, value_bool;
const char *value_str; const char *value_str;
const char *key; const char *key;
PyObject* py_value, *tmp; PyObject *py_key, *py_value, *tkey, *tmp_type = NULL;
PyTypeObject *py_type = NULL;
key = py_str_borrow_c_str(&tmp, py_key, NULL); if (PyTuple_Check(py_input_key) && PyTuple_Size(py_input_key) == 2) {
py_key = PyTuple_GetItem(py_input_key, 0);
tmp_type = PyTuple_GetItem(py_input_key, 1);
} else {
py_key = py_input_key;
}
/* If passed a tuple, make sure the second item is a type */
if (tmp_type) {
if (!PyType_Check(tmp_type))
return NULL;
else
py_type = (PyTypeObject *) tmp_type;
}
key = py_str_borrow_c_str(&tkey, py_key, NULL);
if (key == NULL) if (key == NULL)
return NULL; return NULL;
err = git_config_get_string(&value_str, self->config, key); err = git_config_get_string(&value_str, self->config, key);
Py_CLEAR(tmp); Py_CLEAR(tkey);
if (err < 0) if (err < 0)
goto cleanup; goto cleanup;
if (git_config_parse_int64(&value_int, value_str) == 0) /* If the user specified a type, let's parse it */
py_value = PyLong_FromLongLong(value_int); if (py_type) {
else if(git_config_parse_bool(&value_bool, value_str) == 0) if (py_type == &PyBool_Type) {
py_value = PyBool_FromLong(value_bool); int value;
else if ((err = git_config_parse_bool(&value, value_str)) < 0)
goto cleanup;
py_value = PyBool_FromLong(value);
} else if (py_type == &PyInteger_Type) {
int64_t value;
if ((err = git_config_parse_int64(&value, value_str)) < 0)
goto cleanup;
py_value = PyLong_FromLongLong(value);
}
} else {
py_value = to_unicode(value_str, NULL, NULL); py_value = to_unicode(value_str, NULL, NULL);
}
cleanup: cleanup:
if (err < 0) { if (err < 0) {

View File

@@ -47,6 +47,7 @@
#undef PyLong_Check #undef PyLong_Check
#define PyLong_Check PyInt_Check #define PyLong_Check PyInt_Check
#define PyLong_FromLong PyInt_FromLong #define PyLong_FromLong PyInt_FromLong
#define PyInteger_Type PyInt_Type
#define PyBytes_AS_STRING PyString_AS_STRING #define PyBytes_AS_STRING PyString_AS_STRING
#define PyBytes_AsString PyString_AsString #define PyBytes_AsString PyString_AsString
#define PyBytes_AsStringAndSize PyString_AsStringAndSize #define PyBytes_AsStringAndSize PyString_AsStringAndSize
@@ -57,6 +58,7 @@
#define to_path(x) to_bytes(x) #define to_path(x) to_bytes(x)
#define to_encoding(x) to_bytes(x) #define to_encoding(x) to_bytes(x)
#else #else
#define PyInteger_Type PyLong_Type
#define to_path(x) to_unicode(x, Py_FileSystemDefaultEncoding, "strict") #define to_path(x) to_unicode(x, Py_FileSystemDefaultEncoding, "strict")
#define to_encoding(x) PyUnicode_DecodeASCII(x, strlen(x), "strict") #define to_encoding(x) PyUnicode_DecodeASCII(x, strlen(x), "strict")
#endif #endif

View File

@@ -74,7 +74,7 @@ class ConfigTest(utils.RepoTestCase):
config_read = Config(CONFIG_FILENAME) config_read = Config(CONFIG_FILENAME)
self.assertTrue('core.bare' in config_read) self.assertTrue('core.bare' in config_read)
self.assertFalse(config_read['core.bare']) self.assertFalse(config_read['core.bare',bool])
self.assertTrue('core.editor' in config_read) self.assertTrue('core.editor' in config_read)
self.assertEqual(config_read['core.editor'], 'ed') self.assertEqual(config_read['core.editor'], 'ed')
@@ -88,9 +88,9 @@ class ConfigTest(utils.RepoTestCase):
config.add_file(CONFIG_FILENAME, 0) config.add_file(CONFIG_FILENAME, 0)
self.assertTrue('this.that' in config) self.assertTrue('this.that' in config)
self.assertTrue(config['this.that']) self.assertTrue(config['this.that',bool])
self.assertTrue('something.other.here' in config) self.assertTrue('something.other.here' in config)
self.assertFalse(config['something.other.here']) self.assertFalse(config['something.other.here',bool])
def test_read(self): def test_read(self):
config = self.repo.config config = self.repo.config
@@ -103,11 +103,11 @@ class ConfigTest(utils.RepoTestCase):
lambda: config['abc.def']) lambda: config['abc.def'])
self.assertTrue('core.bare' in config) self.assertTrue('core.bare' in config)
self.assertFalse(config['core.bare']) self.assertFalse(config['core.bare',bool])
self.assertTrue('core.editor' in config) self.assertTrue('core.editor' in config)
self.assertEqual(config['core.editor'], 'ed') self.assertEqual(config['core.editor'], 'ed')
self.assertTrue('core.repositoryformatversion' in config) self.assertTrue('core.repositoryformatversion' in config)
self.assertEqual(config['core.repositoryformatversion'], 0) self.assertEqual(config['core.repositoryformatversion',int], 0)
new_file = open(CONFIG_FILENAME, "w") new_file = open(CONFIG_FILENAME, "w")
new_file.write("[this]\n\tthat = foobar\n\tthat = foobeer\n") new_file.write("[this]\n\tthat = foobar\n\tthat = foobeer\n")
@@ -129,7 +129,7 @@ class ConfigTest(utils.RepoTestCase):
self.assertFalse('core.dummy1' in config) self.assertFalse('core.dummy1' in config)
config['core.dummy1'] = 42 config['core.dummy1'] = 42
self.assertTrue('core.dummy1' in config) self.assertTrue('core.dummy1' in config)
self.assertEqual(config['core.dummy1'], 42) self.assertEqual(config['core.dummy1',int], 42)
self.assertFalse('core.dummy2' in config) self.assertFalse('core.dummy2' in config)
config['core.dummy2'] = 'foobar' config['core.dummy2'] = 'foobar'