Config: move to cffi

This halves the amount of code we have to take into account for dealing
with the config.

There is a slight change in the API. Config.get_multivar() returns an
iterator instead of a list, which lets us reuse code from the general
iterator and is closer to libgit2's API.
This commit is contained in:
Carlos Martín Nieto 2014-04-20 18:18:25 +02:00
parent 3cc129dd62
commit c41c1a1c97
11 changed files with 339 additions and 653 deletions

@ -38,6 +38,7 @@ from .version import __version__
from .settings import Settings
from .credentials import *
from .remote import Remote, get_credentials
from .config import Config
from .errors import check_error
from .ffi import ffi, C, to_str

259
pygit2/config.py Normal file

@ -0,0 +1,259 @@
# -*- coding: utf-8 -*-
#
# Copyright 2010-2014 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
# as published by the Free Software Foundation.
#
# In addition to the permissions in the GNU General Public License,
# the authors give you unlimited permission to link the compiled
# version of this file into combinations with other programs,
# and to distribute those combinations without any restriction
# coming from the use of this file. (The General Public License
# restrictions do apply in other respects; for example, they cover
# modification of the file, and distribution when not linked into
# a combined executable.)
#
# This file is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
# Import from the future
from __future__ import absolute_import, unicode_literals
from _pygit2 import Oid
from .ffi import ffi, C, to_str, is_string
from .errors import check_error, GitError
from .refspec import Refspec
def assert_string(v, desc):
if not is_string(v):
raise TypeError("%s must be a string" % desc)
class ConfigIterator(object):
def __init__(self, config, ptr):
self._iter = ptr
self._config = config
def __del__(self):
C.git_config_iterator_free(self._iter)
def __iter__(self):
return self
def _next_entry(self):
centry = ffi.new('git_config_entry **')
err = C.git_config_next(centry, self._iter)
check_error(err)
return centry[0]
def next(self):
return self.__next__()
def __next__(self):
entry = self._next_entry()
name = ffi.string(entry.name).decode('utf-8')
value = ffi.string(entry.value).decode('utf-8')
return name, value
class ConfigMultivarIterator(ConfigIterator):
def __next__(self):
entry = self._next_entry()
return ffi.string(entry.value).decode('utf-8')
class Config(object):
"""Git configuration management"""
def __init__(self, path=None):
cconfig = ffi.new('git_config **')
if not path:
err = C.git_config_new(cconfig)
else:
assert_string(path, "path")
err = C.git_config_open_ondisk(cconfig, to_str(path))
check_error(err, True)
self._config = cconfig[0]
@classmethod
def from_c(cls, repo, ptr):
config = cls.__new__(cls)
config._repo = repo
config._config = ptr
return config
def __del__(self):
C.git_config_free(self._config)
def _get(self, key):
assert_string(key, "key")
cstr = ffi.new('char **')
err = C.git_config_get_string(cstr, self._config, to_str(key))
return err, cstr
def _get_string(self, key):
err, cstr = self._get(key)
if err == C.GIT_ENOTFOUND:
raise KeyError(key)
check_error(err)
return cstr[0]
def __contains__(self, key):
err, cstr = self._get(key)
if err == C.GIT_ENOTFOUND:
return False
check_error(err)
return True
def __getitem__(self, key):
val = self._get_string(key)
return ffi.string(val).decode()
def __setitem__(self, key, value):
assert_string(key, "key")
err = 0
if isinstance(value, bool):
err = C.git_config_set_bool(self._config, to_str(key), value)
elif isinstance(value, int):
err = C.git_config_set_int64(self._config, to_str(key), value)
else:
err = C.git_config_set_string(self._config, to_str(key), to_str(value))
check_error(err)
def __delitem__(self, key):
assert_string(key, "key")
err = C.git_config_delete_entry(self._config, to_str(key))
check_error(err)
def __iter__(self):
citer = ffi.new('git_config_iterator **')
err = C.git_config_iterator_new(citer, self._config)
check_error(err)
return ConfigIterator(self, citer[0])
def get_multivar(self, name, regex=None):
"""get_multivar(name[, regex]) -> [str, ...]
Get each value of a multivar ''name'' as a list. The optional ''regex''
parameter is expected to be a regular expression to filter the variables
we're interested in."""
assert_string(name, "name")
citer = ffi.new('git_config_iterator **')
err = C.git_config_multivar_iterator_new(citer, self._config, to_str(name), to_str(regex))
check_error(err)
return ConfigMultivarIterator(self, citer[0])
def set_multivar(self, name, regex, value):
"""set_multivar(name, regex, value)
Set a multivar ''name'' to ''value''. ''regexp'' is a regular expression
to indicate which values to replace"""
assert_string(name, "name")
assert_string(regex, "regex")
assert_string(value, "value")
err = C.git_config_set_multivar(self._config, to_str(name), to_str(regex), to_str(value))
check_error(err)
def get_bool(self, key):
"""get_bool(key) -> Bool
Look up *key* and parse its value as a boolean as per the git-config rules
Truthy values are: 'true', 1, 'on' or 'yes'. Falsy values are: 'false',
0, 'off' and 'no'"""
val = self._get_string(key)
res = ffi.new('int *')
err = C.git_config_parse_bool(res, val)
check_error(err)
return res[0] != 0
def get_int(self, key):
"""get_int(key) -> int
Look up *key* and parse its value as an integer as per the git-config rules.
A value can have a suffix 'k', 'm' or 'g' which stand for 'kilo', 'mega' and
'giga' respectively"""
val = self._get_string(key)
res = ffi.new('int64_t *')
err = C.git_config_parse_int64(res, val)
check_error(err)
return res[0]
def add_file(self, path, level=0, force=0):
"""add_file(path, level=0, force=0)
Add a config file instance to an existing config."""
err = C.git_config_add_file_ondisk(self._config, to_str(path), level, force)
check_error(err)
#
# Static methods to get specialized version of the config
#
@staticmethod
def _from_found_config(fn):
buf = ffi.new('char []', C.GIT_PATH_MAX)
err = fn(buf, C.GIT_PATH_MAX)
check_error(err, True)
return Config(ffi.string(buf).decode())
@staticmethod
def get_system_config():
"""get_system_config() -> Config
Return an object representing the system configuration file."""
return Config._from_found_config(C.git_config_find_system)
@staticmethod
def get_global_config():
"""get_global_config() -> Config
Return an object representing the global configuration file."""
return Config._from_found_config(C.git_config_find_global)
@staticmethod
def get_xdg_config():
"""get_xdg_config() -> Config
Return an object representing the global configuration file."""
return Config._from_found_config(C.git_config_find_xdg)

@ -5,8 +5,11 @@ typedef ... git_push;
typedef ... git_cred;
typedef ... git_diff_file;
typedef ... git_tree;
typedef ... git_config;
typedef ... git_config_iterator;
#define GIT_OID_RAWSZ ...
#define GIT_PATH_MAX ...
typedef struct git_oid {
unsigned char id[20];
@ -212,3 +215,49 @@ int git_clone(git_repository **out,
const char *url,
const char *local_path,
const git_clone_options *options);
typedef enum {
GIT_CONFIG_LEVEL_SYSTEM = 1,
GIT_CONFIG_LEVEL_XDG = 2,
GIT_CONFIG_LEVEL_GLOBAL = 3,
GIT_CONFIG_LEVEL_LOCAL = 4,
GIT_CONFIG_LEVEL_APP = 5,
GIT_CONFIG_HIGHEST_LEVEL = -1,
} git_config_level_t;
typedef struct {
const char *name;
const char *value;
git_config_level_t level;
} git_config_entry;
int git_repository_config(git_config **out, git_repository *repo);
void git_config_free(git_config *cfg);
int git_config_get_string(const char **out, const git_config *cfg, const char *name);
int git_config_set_string(git_config *cfg, const char *name, const char *value);
int git_config_set_bool(git_config *cfg, const char *name, int value);
int git_config_set_int64(git_config *cfg, const char *name, int64_t value);
int git_config_parse_bool(int *out, const char *value);
int git_config_parse_int64(int64_t *out, const char *value);
int git_config_delete_entry(git_config *cfg, const char *name);
int git_config_add_file_ondisk(git_config *cfg,
const char *path,
git_config_level_t level,
int force);
int git_config_iterator_new(git_config_iterator **out, const git_config *cfg);
int git_config_next(git_config_entry **entry, git_config_iterator *iter);
void git_config_iterator_free(git_config_iterator *iter);
int git_config_multivar_iterator_new(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp);
int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value);
int git_config_new(git_config **out);
int git_config_open_ondisk(git_config **out, const char *path);
int git_config_find_system(char *out, size_t length);
int git_config_find_global(char *out, size_t length);
int git_config_find_xdg(char *out, size_t length);

@ -33,7 +33,7 @@ from .ffi import ffi, C
from _pygit2 import GitError
def check_error(err):
def check_error(err, io=False):
if err >= 0:
return
@ -45,7 +45,10 @@ def check_error(err):
if err in [C.GIT_EEXISTS, C.GIT_EINVALIDSPEC, C.GIT_EEXISTS, C.GIT_EAMBIGUOUS]:
raise ValueError(message)
elif err == C.GIT_ENOTFOUND:
raise KeyError(message)
if io:
raise IOError(message)
else:
raise KeyError(message)
elif err == C.GIT_EINVALIDSPEC:
raise ValueError(message)
elif err == C.GIT_ITEROVER:

@ -38,6 +38,7 @@ from _pygit2 import Reference, Tree, Commit, Blob
from .ffi import ffi, C, to_str
from .errors import check_error
from .remote import Remote
from .config import Config
class Repository(_Repository):
@ -110,6 +111,23 @@ class Repository(_Repository):
C.git_strarray_free(names)
#
# Configuration
#
@property
def config(self):
"""The configuration file for this repository
If a the configuration hasn't been set yet, the default config for
repository will be returned, including global and system configurations
(if they are available)."""
cconfig = ffi.new('git_config **')
err = C.git_repository_config(cconfig, self._repo)
check_error(err)
return Config.from_c(self, cconfig[0])
#
# References
#

@ -1,540 +0,0 @@
/*
* Copyright 2010-2014 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
* as published by the Free Software Foundation.
*
* In addition to the permissions in the GNU General Public License,
* the authors give you unlimited permission to link the compiled
* version of this file into combinations with other programs,
* and to distribute those combinations without any restriction
* coming from the use of this file. (The General Public License
* restrictions do apply in other respects; for example, they cover
* modification of the file, and distribution when not linked into
* a combined executable.)
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "error.h"
#include "types.h"
#include "utils.h"
#include "config.h"
extern PyTypeObject ConfigType;
extern PyTypeObject ConfigIterType;
PyObject *
wrap_config(char *c_path) {
int err;
PyObject *py_path;
Config *py_config;
py_path = Py_BuildValue("(s)", c_path);
py_config = PyObject_New(Config, &ConfigType);
err = Config_init(py_config, py_path, NULL);
if (err < 0)
return NULL;
return (PyObject*) py_config;
}
int
Config_init(Config *self, PyObject *args, PyObject *kwds)
{
char *path = NULL;
int err;
if (kwds && PyDict_Size(kwds) > 0) {
PyErr_SetString(PyExc_TypeError,
"Config takes no keyword arguments");
return -1;
}
if (!PyArg_ParseTuple(args, "|s", &path))
return -1;
if (path == NULL)
err = git_config_new(&self->config);
else
err = git_config_open_ondisk(&self->config, path);
if (err < 0) {
git_config_free(self->config);
if (err == GIT_ENOTFOUND)
Error_set_exc(PyExc_IOError);
else
Error_set(err);
return -1;
}
return 0;
}
void
Config_dealloc(Config *self)
{
git_config_free(self->config);
Py_TYPE(self)->tp_free(self);
}
PyDoc_STRVAR(Config_get_global_config__doc__,
"get_global_config() -> Config\n"
"\n"
"Return an object representing the global configuration file.");
PyObject *
Config_get_global_config(void)
{
char path[GIT_PATH_MAX];
int err;
err = git_config_find_global(path, GIT_PATH_MAX);
if (err < 0) {
if (err == GIT_ENOTFOUND) {
PyErr_SetString(PyExc_IOError, "Global config file not found.");
return NULL;
}
return Error_set(err);
}
return wrap_config(path);
}
PyDoc_STRVAR(Config_get_system_config__doc__,
"get_system_config() -> Config\n"
"\n"
"Return an object representing the system configuration file.");
PyObject *
Config_get_system_config(void)
{
char path[GIT_PATH_MAX];
int err;
err = git_config_find_system(path, GIT_PATH_MAX);
if (err < 0) {
if (err == GIT_ENOTFOUND) {
PyErr_SetString(PyExc_IOError, "System config file not found.");
return NULL;
}
return Error_set(err);
}
return wrap_config(path);
}
int
Config_contains(Config *self, PyObject *py_key) {
int err;
const char *c_value, *c_key;
PyObject *tkey;
c_key = py_str_borrow_c_str(&tkey, py_key, NULL);
if (c_key == NULL)
return -1;
err = git_config_get_string(&c_value, self->config, c_key);
Py_DECREF(tkey);
if (err < 0) {
if (err == GIT_ENOTFOUND)
return 0;
Error_set(err);
return -1;
}
return 1;
}
/* Get the C string value given a python string as key */
static int
get_string(const char **key_out, Config *self, PyObject *py_key)
{
PyObject *tkey;
const char *key;
int err;
key = py_str_borrow_c_str(&tkey, py_key, NULL);
if (key == NULL)
return -1;
err = git_config_get_string(key_out, self->config, key);
Py_CLEAR(tkey);
if (err == GIT_ENOTFOUND) {
PyErr_SetObject(PyExc_KeyError, py_key);
return -1;
}
if (err < 0) {
Error_set(err);
return -1;
}
return 0;
}
PyObject *
Config_getitem(Config *self, PyObject *py_key)
{
int err;
const char *value_str;
err = get_string(&value_str, self, py_key);
if (err < 0)
return NULL;
return to_unicode(value_str, NULL, NULL);
}
PyDoc_STRVAR(Config_get_bool__doc__,
"get_bool(key) -> Bool\n"
"\n"
"Look up *key* and parse its value as a boolean as per the git-config rules\n"
"\n"
"Truthy values are: 'true', 1, 'on' or 'yes'. Falsy values are: 'false',\n"
"0, 'off' and 'no'");
PyObject *
Config_get_bool(Config *self, PyObject *key)
{
int err, value;
const char *value_str;
err = get_string(&value_str, self, key);
if (err < 0)
return NULL;
if ((err = git_config_parse_bool(&value, value_str)) < 0)
return NULL;
return PyBool_FromLong(value);
}
PyDoc_STRVAR(Config_get_int__doc__,
"get_int(key) -> int\n"
"\n"
"Look up *key* and parse its value as an integer as per the git-config rules\n"
"\n"
"A value can have a suffix 'k', 'm' or 'g' which stand for 'kilo', 'mega' and\n"
"'giga' respectively");
PyObject *
Config_get_int(Config *self, PyObject *key)
{
int err;
int64_t value;
const char *value_str;
err = get_string(&value_str, self, key);
if (err < 0)
return NULL;
if ((err = git_config_parse_int64(&value, value_str)) < 0)
return NULL;
return PyLong_FromLongLong(value);
}
int
Config_setitem(Config *self, PyObject *py_key, PyObject *py_value)
{
int err;
const char *key, *value;
PyObject *tkey, *tvalue;
key = py_str_borrow_c_str(&tkey, py_key, NULL);
if (key == NULL)
return -1;
if (py_value == NULL)
err = git_config_delete_entry(self->config, key);
else if (PyBool_Check(py_value)) {
err = git_config_set_bool(self->config, key,
(int)PyObject_IsTrue(py_value));
} else if (PyLong_Check(py_value)) {
err = git_config_set_int64(self->config, key,
(int64_t)PyLong_AsLong(py_value));
} else {
value = py_str_borrow_c_str(&tvalue, py_value, NULL);
err = git_config_set_string(self->config, key, value);
Py_DECREF(tvalue);
}
Py_DECREF(tkey);
if (err < 0) {
Error_set(err);
return -1;
}
return 0;
}
PyDoc_STRVAR(Config_add_file__doc__,
"add_file(path, level=0, force=0)\n"
"\n"
"Add a config file instance to an existing config.");
PyObject *
Config_add_file(Config *self, PyObject *args, PyObject *kwds)
{
char *keywords[] = {"path", "level", "force", NULL};
int err;
char *path;
unsigned int level = 0;
int force = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|Ii", keywords,
&path, &level, &force))
return NULL;
err = git_config_add_file_ondisk(self->config, path, level, force);
if (err < 0)
return Error_set_str(err, path);
Py_RETURN_NONE;
}
PyDoc_STRVAR(Config_get_multivar__doc__,
"get_multivar(name[, regex]) -> [str, ...]\n"
"\n"
"Get each value of a multivar ''name'' as a list. The optional ''regex''\n"
"parameter is expected to be a regular expression to filter the variables\n"
"we're interested in.");
PyObject *
Config_get_multivar(Config *self, PyObject *args)
{
int err;
PyObject *list;
const char *name = NULL;
const char *regex = NULL;
git_config_iterator *iter;
git_config_entry *entry;
if (!PyArg_ParseTuple(args, "s|s", &name, &regex))
return NULL;
list = PyList_New(0);
err = git_config_multivar_iterator_new(&iter, self->config, name, regex);
if (err < 0)
return Error_set(err);
while ((err = git_config_next(&entry, iter)) == 0) {
PyObject *item;
item = to_unicode(entry->value, NULL, NULL);
if (item == NULL) {
git_config_iterator_free(iter);
return NULL;
}
PyList_Append(list, item);
Py_CLEAR(item);
}
git_config_iterator_free(iter);
if (err == GIT_ITEROVER)
err = 0;
if (err < 0)
return Error_set(err);
return list;
}
PyDoc_STRVAR(Config_set_multivar__doc__,
"set_multivar(name, regex, value)\n"
"\n"
"Set a multivar ''name'' to ''value''. ''regexp'' is a regular expression\n"
"to indicate which values to replace");
PyObject *
Config_set_multivar(Config *self, PyObject *args)
{
int err;
const char *name = NULL;
const char *regex = NULL;
const char *value = NULL;
if (!PyArg_ParseTuple(args, "sss", &name, &regex, &value))
return NULL;
err = git_config_set_multivar(self->config, name, regex, value);
if (err < 0) {
if (err == GIT_ENOTFOUND)
Error_set(err);
else
PyErr_SetNone(PyExc_TypeError);
return NULL;
}
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, add_file, METH_VARARGS | METH_KEYWORDS),
METHOD(Config, get_multivar, METH_VARARGS),
METHOD(Config, set_multivar, METH_VARARGS),
METHOD(Config, get_bool, METH_O),
METHOD(Config, get_int, METH_O),
{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 */
};
PyDoc_STRVAR(Config__doc__, "Configuration management.");
PyTypeObject ConfigType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_pygit2.Config", /* tp_name */
sizeof(Config), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)Config_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
&Config_as_sequence, /* tp_as_sequence */
&Config_as_mapping, /* 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, /* tp_flags */
Config__doc__, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
(getiterfunc)Config_iter, /* tp_iter */
0, /* tp_iternext */
Config_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)Config_init, /* tp_init */
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 */
};

@ -1,45 +0,0 @@
/*
* Copyright 2010-2014 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
* as published by the Free Software Foundation.
*
* In addition to the permissions in the GNU General Public License,
* the authors give you unlimited permission to link the compiled
* version of this file into combinations with other programs,
* and to distribute those combinations without any restriction
* coming from the use of this file. (The General Public License
* restrictions do apply in other respects; for example, they cover
* modification of the file, and distribution when not linked into
* a combined executable.)
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef INCLUDE_pygit2_config_h
#define INCLUDE_pygit2_config_h
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <git2.h>
PyObject* wrap_config(char *c_path);
PyObject* Config_get_global_config(void);
PyObject* Config_get_system_config(void);
PyObject* Config_add_file(Config *self, PyObject *args, PyObject *kwds);
PyObject* Config_getitem(Config *self, PyObject *key);
PyObject* Config_foreach(Config *self, PyObject *args);
PyObject* Config_get_multivar(Config *self, PyObject *args);
PyObject* Config_set_multivar(Config *self, PyObject *args);
int Config_init(Config *self, PyObject *args, PyObject *kwds);
int Config_setitem(Config *self, PyObject *key, PyObject *value);
#endif

@ -66,8 +66,6 @@ extern PyTypeObject IndexType;
extern PyTypeObject IndexEntryType;
extern PyTypeObject IndexIterType;
extern PyTypeObject WalkerType;
extern PyTypeObject ConfigType;
extern PyTypeObject ConfigIterType;
extern PyTypeObject ReferenceType;
extern PyTypeObject RefLogIterType;
extern PyTypeObject RefLogEntryType;
@ -385,11 +383,6 @@ moduleinit(PyObject* m)
ADD_CONSTANT_INT(m, GIT_CONFIG_LEVEL_XDG);
ADD_CONSTANT_INT(m, GIT_CONFIG_LEVEL_SYSTEM);
INIT_TYPE(ConfigType, NULL, PyType_GenericNew)
INIT_TYPE(ConfigIterType, NULL, NULL)
ADD_TYPE(m, Config)
ADD_TYPE(m, ConfigIter)
/* Blame */
INIT_TYPE(BlameType, NULL, NULL)
INIT_TYPE(BlameIterType, NULL, NULL)

@ -513,45 +513,6 @@ Repository_workdir__get__(Repository *self, void *closure)
return to_path(c_path);
}
PyDoc_STRVAR(Repository_config__doc__,
"Get the configuration file for this repository.\n"
"\n"
"If a configuration file has not been set, the default config set for the\n"
"repository will be returned, including global and system configurations\n"
"(if they are available).");
PyObject *
Repository_config__get__(Repository *self)
{
int err;
git_config *config;
Config *py_config;
assert(self->repo);
if (self->config == NULL) {
err = git_repository_config(&config, self->repo);
if (err < 0)
return Error_set(err);
py_config = PyObject_New(Config, &ConfigType);
if (py_config == NULL) {
git_config_free(config);
return NULL;
}
py_config->config = config;
self->config = (PyObject*)py_config;
/* We need 2 refs here. One is returned, one is kept internally. */
Py_INCREF(self->config);
} else {
Py_INCREF(self->config);
}
return self->config;
}
PyDoc_STRVAR(Repository_merge_base__doc__,
"merge_base(oid, oid) -> Oid\n"
"\n"
@ -1601,7 +1562,6 @@ PyGetSetDef Repository_getseters[] = {
GETTER(Repository, head_is_unborn),
GETTER(Repository, is_empty),
GETTER(Repository, is_bare),
GETTER(Repository, config),
GETTER(Repository, workdir),
GETTER(Repository, default_signature),
GETTER(Repository, _pointer),

@ -70,19 +70,6 @@ SIMPLE_TYPE(Tree, git_tree, tree)
SIMPLE_TYPE(Blob, git_blob, blob)
SIMPLE_TYPE(Tag, git_tag, tag)
/* git_config */
typedef struct {
PyObject_HEAD
git_config* config;
} Config;
typedef struct {
PyObject_HEAD
Config *owner;
git_config_iterator *iter;
} ConfigIter;
/* git_note */
typedef struct {
PyObject_HEAD

@ -115,9 +115,10 @@ class ConfigTest(utils.RepoTestCase):
config.add_file(CONFIG_FILENAME, 0)
self.assertTrue('this.that' in config)
self.assertEqual(len(config.get_multivar('this.that')), 2)
l = config.get_multivar('this.that', 'bar')
self.assertEqual(len(l), 1)
self.assertEqual(2, len(list(config.get_multivar('this.that'))))
l = list(config.get_multivar('this.that', 'bar'))
self.assertEqual(1, len(l))
self.assertEqual(l[0], 'foobar')
def test_write(self):
@ -155,16 +156,16 @@ class ConfigTest(utils.RepoTestCase):
config.add_file(CONFIG_FILENAME, 5)
self.assertTrue('this.that' in config)
l = config.get_multivar('this.that', 'foo.*')
self.assertEqual(len(l), 2)
self.assertEqual(2, len(list(l)))
config.set_multivar('this.that', '^.*beer', 'fool')
l = config.get_multivar('this.that', 'fool')
l = list(config.get_multivar('this.that', 'fool'))
self.assertEqual(len(l), 1)
self.assertEqual(l[0], 'fool')
config.set_multivar('this.that', 'foo.*', 'foo-123456')
l = config.get_multivar('this.that', 'foo.*')
self.assertEqual(len(l), 2)
self.assertEqual(2, len(list(l)))
for i in l:
self.assertEqual(i, 'foo-123456')