Introduce the Signature object

This commit is contained in:
J. David Ibáñez 2011-11-23 23:48:41 +01:00
parent 63ab0f3b26
commit 9064b8e038
5 changed files with 225 additions and 92 deletions

6
TODO.txt Normal file

@ -0,0 +1,6 @@
Signature
=========
- Implement equality interface
- Return unicode for the email
- Implement interface to access the name/email as bytes
- In Repository's create_commit/create_tag check signatures encoding is right

252
pygit2.c

@ -104,6 +104,13 @@ typedef struct {
git_reference *reference;
} Reference;
typedef struct {
PyObject_HEAD
Object *obj;
const git_signature *signature;
const char *encoding;
} Signature;
static PyTypeObject RepositoryType;
static PyTypeObject ObjectType;
static PyTypeObject CommitType;
@ -117,6 +124,7 @@ static PyTypeObject IndexIterType;
static PyTypeObject IndexEntryType;
static PyTypeObject WalkerType;
static PyTypeObject ReferenceType;
static PyTypeObject SignatureType;
static PyObject *GitError;
@ -614,47 +622,27 @@ Repository_walk(Repository *self, PyObject *args)
}
static PyObject *
build_person(const git_signature *signature, const char *encoding)
build_signature(Object *obj, const git_signature *signature,
const char *encoding)
{
PyObject *name;
Signature *py_signature;
name = PyUnicode_Decode(signature->name, strlen(signature->name),
encoding, "strict");
return Py_BuildValue("(NsLi)", name, signature->email,
signature->when.time, signature->when.offset);
}
static git_signature *
py_signature_to_git_signature(PyObject *value, const char* encoding)
{
PyObject *py_name;
char *name, *email;
long long time;
int offset;
int err;
git_signature *signature;
if (!PyArg_ParseTuple(value, "OsLi", &py_name, &email, &time, &offset))
return NULL;
name = py_str_to_c_str(py_name, encoding);
err = git_signature_new(&signature, name, email, time, offset);
if (err < 0) {
Error_set(err);
return NULL;
py_signature = PyObject_New(Signature, &SignatureType);
if (py_signature) {
Py_INCREF(obj);
py_signature->obj = obj;
py_signature->signature = signature;
py_signature->encoding = encoding;
}
return signature;
return (PyObject*)py_signature;
}
static PyObject *
Repository_create_commit(Repository *self, PyObject *args)
{
PyObject *py_author, *py_committer;
Signature *py_author, *py_committer;
PyObject *py_oid, *py_message, *py_parents, *py_parent;
PyObject *py_result = NULL;
git_signature *author = NULL, *committer = NULL;
char *message, *update_ref, *encoding = NULL;
git_oid oid;
git_tree *tree = NULL;
@ -665,21 +653,14 @@ Repository_create_commit(Repository *self, PyObject *args)
if (!PyArg_ParseTuple(args, "zO!O!OOO!|s",
&update_ref,
&PyTuple_Type, &py_author,
&PyTuple_Type, &py_committer,
&SignatureType, &py_author,
&SignatureType, &py_committer,
&py_message,
&py_oid,
&PyList_Type, &py_parents,
&encoding))
return NULL;
author = py_signature_to_git_signature(py_author, encoding);
if (author == NULL)
return NULL;
committer = py_signature_to_git_signature(py_committer, encoding);
if (committer == NULL)
goto out;
len = py_str_to_git_oid(py_oid, &oid);
if (len == 0)
goto out;
@ -708,7 +689,8 @@ Repository_create_commit(Repository *self, PyObject *args)
goto out;
}
err = git_commit_create(&oid, self->repo, update_ref, author, committer,
err = git_commit_create(&oid, self->repo, update_ref,
py_author->signature, py_committer->signature,
encoding, message, tree, parent_count,
(const git_commit**)parents);
if (err < 0) {
@ -719,8 +701,6 @@ Repository_create_commit(Repository *self, PyObject *args)
py_result = git_oid_to_python(oid.id);
out:
git_signature_free(author);
git_signature_free(committer);
git_tree_close(tree);
while (i > 0) {
i--;
@ -733,9 +713,9 @@ out:
static PyObject *
Repository_create_tag(Repository *self, PyObject *args)
{
PyObject *py_oid, *py_tagger, *py_result = NULL;
PyObject *py_oid, *py_result = NULL;
Signature *py_tagger;
char *tag_name, *message;
git_signature *tagger = NULL;
git_oid oid;
git_object *target = NULL;
int err, target_type;
@ -746,14 +726,10 @@ Repository_create_tag(Repository *self, PyObject *args)
&tag_name,
&py_oid,
&target_type,
&PyTuple_Type, &py_tagger,
&SignatureType, &py_tagger,
&message))
return NULL;
tagger = py_signature_to_git_signature(py_tagger, NULL);
if (tagger == NULL)
return NULL;
len = py_str_to_git_oid(py_oid, &oid);
if (len == 0)
goto out;
@ -767,13 +743,12 @@ Repository_create_tag(Repository *self, PyObject *args)
goto out;
}
err = git_tag_create(&oid, self->repo, tag_name, target, tagger, message,
0);
err = git_tag_create(&oid, self->repo, tag_name, target,
py_tagger->signature, message, 0);
if (err == 0)
py_result = git_oid_to_python(oid.id);
out:
git_signature_free(tagger);
git_object_close(target);
return py_result;
}
@ -1187,29 +1162,27 @@ Commit_get_commit_time_offset(Commit *commit)
}
static PyObject *
Commit_get_committer(Commit *commit)
Commit_get_committer(Commit *self)
{
const git_signature *signature;
const char *encoding;
signature = git_commit_committer(commit->commit);
encoding = git_commit_message_encoding(commit->commit);
if (encoding == NULL)
encoding = "utf-8";
return build_person(signature, encoding);
signature = git_commit_committer(self->commit);
encoding = git_commit_message_encoding(self->commit);
return build_signature((Object*)self, signature, encoding);
}
static PyObject *
Commit_get_author(Commit *commit)
Commit_get_author(Commit *self)
{
const git_signature *signature;
const char *encoding;
signature = git_commit_author(commit->commit);
encoding = git_commit_message_encoding(commit->commit);
if (encoding == NULL)
encoding = "utf-8";
return build_person(signature, encoding);
signature = git_commit_author(self->commit);
encoding = git_commit_message_encoding(self->commit);
return build_signature((Object*)self, signature, encoding);
}
static PyObject *
@ -1715,12 +1688,13 @@ Tag_get_name(Tag *self)
}
static PyObject *
Tag_get_tagger(Tag *tag)
Tag_get_tagger(Tag *self)
{
const git_signature *signature = git_tag_tagger(tag->tag);
const git_signature *signature = git_tag_tagger(self->tag);
if (!signature)
Py_RETURN_NONE;
return build_person(signature, "utf-8");
return build_signature((Object*)self, signature, "utf-8");
}
static PyObject *
@ -2624,6 +2598,142 @@ static PyTypeObject ReferenceType = {
0, /* tp_new */
};
static int
Signature_init(Signature *self, PyObject *args, PyObject *kwds)
{
PyObject *py_name;
char *name, *email, *encoding = NULL;
long long time;
int offset;
int err;
git_signature *signature;
if (kwds) {
PyErr_SetString(PyExc_TypeError,
"Signature takes no keyword arguments");
return -1;
}
if (!PyArg_ParseTuple(args, "OsLi|s",
&py_name, &email, &time, &offset, &encoding))
return -1;
name = py_str_to_c_str(py_name, encoding);
err = git_signature_new(&signature, name, email, time, offset);
if (err < 0) {
Error_set(err);
return -1;
}
self->obj = NULL;
self->signature = signature;
if (encoding) {
self->encoding = strdup(encoding);
if (self->encoding == NULL) {
PyErr_NoMemory();
return -1;
}
}
return 0;
}
static void
Signature_dealloc(Signature *self)
{
if (self->obj)
Py_DECREF(self->obj);
else {
git_signature_free((git_signature*)self->signature);
free((void*)self->encoding);
}
Py_TYPE(self)->tp_free((PyObject*)self);
}
static PyObject *
Signature_get_name(Signature *self)
{
const char *encoding;
git_object *object;
encoding = self->encoding;
if (encoding == NULL)
encoding = "utf-8";
return PyUnicode_Decode(self->signature->name,
strlen(self->signature->name),
encoding, "strict");
}
static PyObject *
Signature_get_email(Signature *self)
{
return PyString_FromString(self->signature->email);
}
static PyObject *
Signature_get_time(Signature *self)
{
return PyInt_FromLong(self->signature->when.time);
}
static PyObject *
Signature_get_offset(Signature *self)
{
return PyInt_FromLong(self->signature->when.offset);
}
static PyGetSetDef Signature_getseters[] = {
{"name", (getter)Signature_get_name, NULL, "Name", NULL},
{"email", (getter)Signature_get_email, NULL, "Email", NULL},
{"time", (getter)Signature_get_time, NULL, "Time", NULL},
{"offset", (getter)Signature_get_offset, NULL, "Offset", NULL},
{NULL}
};
static PyTypeObject SignatureType = {
PyVarObject_HEAD_INIT(NULL, 0)
"pygit2.Signature", /* tp_name */
sizeof(Signature), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)Signature_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, /* tp_flags */
"Signature", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
Signature_getseters, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)Signature_init, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};
static PyObject *
init_repository(PyObject *self, PyObject *args)
{
@ -2704,6 +2814,9 @@ moduleinit(PyObject* m)
ReferenceType.tp_new = PyType_GenericNew;
if (PyType_Ready(&ReferenceType) < 0)
return NULL;
SignatureType.tp_new = PyType_GenericNew;
if (PyType_Ready(&SignatureType) < 0)
return NULL;
Py_INCREF(GitError);
PyModule_AddObject(m, "GitError", GitError);
@ -2738,6 +2851,9 @@ moduleinit(PyObject* m)
Py_INCREF(&ReferenceType);
PyModule_AddObject(m, "Reference", (PyObject *)&ReferenceType);
Py_INCREF(&SignatureType);
PyModule_AddObject(m, "Signature", (PyObject *)&SignatureType);
PyModule_AddIntConstant(m, "GIT_OBJ_ANY", GIT_OBJ_ANY);
PyModule_AddIntConstant(m, "GIT_OBJ_COMMIT", GIT_OBJ_COMMIT);
PyModule_AddIntConstant(m, "GIT_OBJ_TREE", GIT_OBJ_TREE);

@ -31,7 +31,7 @@ from __future__ import absolute_import
from __future__ import unicode_literals
import unittest
from pygit2 import GIT_OBJ_COMMIT
from pygit2 import GIT_OBJ_COMMIT, Signature
from . import utils
@ -55,20 +55,22 @@ class CommitTest(utils.BareRepoTestCase):
commit.message)
commit_time = 1288481576
self.assertEqual(commit_time, commit.commit_time)
self.assertEqual(
('Dave Borowitz', 'dborowitz@google.com', commit_time, -420),
commit.committer)
self.assertEqual(
('Dave Borowitz', 'dborowitz@google.com', 1288477363, -420),
commit.author)
self.assertEqualSignature(
commit.committer,
Signature('Dave Borowitz', 'dborowitz@google.com',
commit_time, -420))
self.assertEqualSignature(
commit.author,
Signature('Dave Borowitz', 'dborowitz@google.com', 1288477363,
-420))
self.assertEqual(
'967fce8df97cc71722d3c2a5930ef3e6f1d27b12', commit.tree.hex)
def test_new_commit(self):
repo = self.repo
message = 'New commit.\n\nMessage with non-ascii chars: ééé.\n'
committer = ('John Doe', 'jdoe@example.com', 12346, 0)
author = ('J. David Ibáñez', 'jdavid@example.com', 12345, 0)
committer = Signature('John Doe', 'jdoe@example.com', 12346, 0)
author = Signature('J. David Ibáñez', 'jdavid@example.com', 12345, 0)
tree = '967fce8df97cc71722d3c2a5930ef3e6f1d27b12'
tree_prefix = tree[:5]
too_short_prefix = tree[:3]
@ -87,31 +89,34 @@ class CommitTest(utils.BareRepoTestCase):
self.assertEqual(None, commit.message_encoding)
self.assertEqual(message, commit.message)
self.assertEqual(12346, commit.commit_time)
self.assertEqual(committer, commit.committer)
self.assertEqual(author, commit.author)
self.assertEqualSignature(committer, commit.committer)
self.assertEqualSignature(author, commit.author)
self.assertEqual(tree, commit.tree.hex)
self.assertEqual(1, len(commit.parents))
self.assertEqual(COMMIT_SHA, commit.parents[0].hex)
def test_new_commit_encoding(self):
repo = self.repo
encoding = 'iso-8859-1'
message = 'New commit.\n\nMessage with non-ascii chars: ééé.\n'
committer = ('John Doe', 'jdoe@example.com', 12346, 0)
author = ('J. David Ibáñez', 'jdavid@example.com', 12345, 0)
committer = Signature('John Doe', 'jdoe@example.com', 12346, 0,
encoding)
author = Signature('J. David Ibáñez', 'jdavid@example.com', 12345, 0,
encoding)
tree = '967fce8df97cc71722d3c2a5930ef3e6f1d27b12'
tree_prefix = tree[:5]
parents = [COMMIT_SHA[:5]]
sha = repo.create_commit(None, author, committer, message,
tree_prefix, parents, 'iso-8859-1')
tree_prefix, parents, encoding)
commit = repo[sha]
self.assertEqual(GIT_OBJ_COMMIT, commit.type)
self.assertEqual('iso-8859-1', commit.message_encoding)
self.assertEqual(message, commit.message)
self.assertEqual(12346, commit.commit_time)
self.assertEqual(committer, commit.committer)
self.assertEqual(author, commit.author)
self.assertEqualSignature(committer, commit.committer)
self.assertEqualSignature(author, commit.author)
self.assertEqual(tree, commit.tree.hex)
self.assertEqual(1, len(commit.parents))
self.assertEqual(COMMIT_SHA, commit.parents[0].hex)

@ -48,19 +48,18 @@ class TagTest(utils.BareRepoTestCase):
self.assertEqual(pygit2.GIT_OBJ_TAG, tag.type)
self.assertEqual(pygit2.GIT_OBJ_COMMIT, tag.target.type)
self.assertEqual('root', tag.name)
self.assertEqual(
('Dave Borowitz', 'dborowitz@google.com', 1288724692, -420),
tag.tagger)
self.assertEqual('Tagged root commit.\n', tag.message)
commit = tag.target
self.assertEqual('Initial test data commit.\n', commit.message)
self.assertEqual('Initial test data commit.\n', tag.target.message)
self.assertEqualSignature(
tag.tagger,
pygit2.Signature('Dave Borowitz', 'dborowitz@google.com',
1288724692, -420))
def test_new_tag(self):
name = 'thetag'
target = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16'
message = 'Tag a blob.\n'
tagger = ('John Doe', 'jdoe@example.com', 12347, 0)
tagger = pygit2.Signature('John Doe', 'jdoe@example.com', 12347, 0)
target_prefix = target[:5]
too_short_prefix = target[:3]
@ -74,7 +73,7 @@ class TagTest(utils.BareRepoTestCase):
self.assertEqual('3ee44658fd11660e828dfc96b9b5c5f38d5b49bb', tag.hex)
self.assertEqual(name, tag.name)
self.assertEqual(target, tag.target.hex)
self.assertEqual(tagger, tag.tagger)
self.assertEqualSignature(tagger, tag.tagger)
self.assertEqual(message, tag.message)
self.assertEqual(name, self.repo[tag.hex].name)

@ -74,6 +74,13 @@ class NoRepoTestCase(unittest.TestCase):
else:
self.fail('%s(%r) not raised' % (exc_class.__name__, arg))
def assertEqualSignature(self, a, b):
# XXX Remove this once equality test is supported by Signature
self.assertEqual(a.name, b.name)
self.assertEqual(a.email, b.email)
self.assertEqual(a.time, b.time)
self.assertEqual(a.offset, b.offset)
class BareRepoTestCase(NoRepoTestCase):