From f8544cc5140f12d0f2413ffcd3049839753d5365 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 13 Apr 2013 13:04:52 +0200 Subject: [PATCH 01/12] Add Oid type --- src/oid.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++ src/pygit2.c | 5 +++ src/types.h | 6 +++ test/__init__.py | 6 +-- test/test_oid.py | 70 ++++++++++++++++++++++++++++++++++ 5 files changed, 183 insertions(+), 3 deletions(-) create mode 100644 test/test_oid.py diff --git a/src/oid.c b/src/oid.c index 413d29c..c671aa6 100644 --- a/src/oid.c +++ b/src/oid.c @@ -125,3 +125,102 @@ git_oid_to_py_str(const git_oid *oid) return to_unicode_n(hex, GIT_OID_HEXSZ, "utf-8", "strict"); } + +int +Oid_init(Oid *self, PyObject *args, PyObject *kw) +{ + char *keywords[] = {"raw", "hex", NULL}; + PyObject *raw = NULL, *hex = NULL; + int err; + + if (!PyArg_ParseTupleAndKeywords(args, kw, "|OO", keywords, &raw, &hex)) + return -1; + + /* We expect one or the other, but not both. */ + if (raw == NULL && hex == NULL) { + PyErr_SetString(PyExc_ValueError, "Expected raw or hex."); + return -1; + } + if (raw != NULL && hex != NULL) { + PyErr_SetString(PyExc_ValueError, "Expected raw or hex, not both."); + return -1; + } + + /* Get the oid. */ + if (raw != NULL) + err = py_str_to_git_oid(raw, &self->oid); + else + err = py_str_to_git_oid(hex, &self->oid); + + if (err < 0) + return -1; + + return 0; +} + + +PyDoc_STRVAR(Oid_raw__doc__, "Raw oid."); + +PyObject * +Oid_raw__get__(Oid *self) +{ + return git_oid_to_python(self->oid.id); +} + + +PyDoc_STRVAR(Oid_hex__doc__, "Hex oid."); + +PyObject * +Oid_hex__get__(Oid *self) +{ + return git_oid_to_py_str(&self->oid); +} + +PyGetSetDef Oid_getseters[] = { + GETTER(Oid, raw), + GETTER(Oid, hex), + {NULL}, +}; + +PyDoc_STRVAR(Oid__doc__, "Object id."); + +PyTypeObject OidType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_pygit2.Oid", /* tp_name */ + sizeof(Oid), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* 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 */ + Oid__doc__, /* 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 */ + Oid_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)Oid_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; diff --git a/src/pygit2.c b/src/pygit2.c index f1775fa..6b93630 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -38,6 +38,7 @@ extern PyObject *GitError; extern PyTypeObject RepositoryType; +extern PyTypeObject OidType; extern PyTypeObject ObjectType; extern PyTypeObject CommitType; extern PyTypeObject DiffType; @@ -193,6 +194,10 @@ moduleinit(PyObject* m) INIT_TYPE(RepositoryType, NULL, PyType_GenericNew) ADD_TYPE(m, Repository); + /* Oid */ + INIT_TYPE(OidType, NULL, PyType_GenericNew) + ADD_TYPE(m, Oid); + /* Objects (make them with the Repository.create_XXX methods). */ INIT_TYPE(ObjectType, NULL, NULL) INIT_TYPE(CommitType, &ObjectType, NULL) diff --git a/src/types.h b/src/types.h index 2de5872..eceeeb9 100644 --- a/src/types.h +++ b/src/types.h @@ -46,6 +46,12 @@ typedef struct { } Repository; +typedef struct { + PyObject_HEAD + git_oid oid; +} Oid; + + #define SIMPLE_TYPE(_name, _ptr_type, _ptr_name) \ typedef struct {\ PyObject_HEAD\ diff --git a/test/__init__.py b/test/__init__.py index e81179b..024476b 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -35,9 +35,9 @@ import sys import unittest -names = ['blob', 'commit', 'config', 'diff', 'index', 'refs', 'remote', - 'repository', 'revwalk', 'signature', 'status', 'tag', 'tree', - 'treebuilder', 'note'] +names = ['blob', 'commit', 'config', 'diff', 'index', 'note', 'oid', 'refs', + 'remote', 'repository', 'revwalk', 'signature', 'status', 'tag', + 'tree', 'treebuilder'] def test_suite(): # Sometimes importing pygit2 fails, we try this first to get an diff --git a/test/test_oid.py b/test/test_oid.py new file mode 100644 index 0000000..7b8d8a4 --- /dev/null +++ b/test/test_oid.py @@ -0,0 +1,70 @@ +# -*- coding: UTF-8 -*- +# +# Copyright 2010-2013 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. + +"""Tests for Object ids.""" + +# Import from the future +from __future__ import absolute_import +from __future__ import unicode_literals + +# Import from the Standard Library +from binascii import unhexlify +import unittest + +# Import from pygit2 +from pygit2 import Oid +from . import utils + + +HEX = "15b648aec6ed045b5ca6f57f8b7831a8b4757298" +RAW = unhexlify(HEX.encode('ascii')) + +class OidTest(utils.BareRepoTestCase): + + def test_raw(self): + oid = Oid(raw=RAW) + self.assertEqual(oid.raw, RAW) + self.assertEqual(oid.hex, HEX) + + def test_hex(self): + oid = Oid(hex=HEX) + self.assertEqual(oid.raw, RAW) + self.assertEqual(oid.hex, HEX) + + def test_none(self): + self.assertRaises(ValueError, Oid) + + def test_both(self): + self.assertRaises(ValueError, Oid, raw=RAW, hex=HEX) + + def test_long(self): + self.assertRaises(ValueError, Oid, raw=RAW + b'a') + self.assertRaises(ValueError, Oid, hex=HEX + 'a') + + +if __name__ == '__main__': + unittest.main() From 406c3175728cd931394e7e450b38ec80ace5db55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 14 Apr 2013 12:26:22 +0200 Subject: [PATCH 02/12] Return Oid wherever we returned raw oid (bytes) before Changes: - Return Oid wherever we returned raw oid (bytes) before - Now py_str_to_git_oid accepts Oid objects - Add ability to compare two Oid objects --- src/index.c | 4 ++-- src/object.c | 2 +- src/oid.c | 35 +++++++++++++++++++++++++++++++---- src/oid.h | 6 ++---- src/pygit2.c | 4 ++-- src/reference.c | 2 +- src/repository.c | 12 ++++++------ src/tag.c | 2 +- src/tree.c | 2 +- test/test_blob.py | 12 +++++------- test/test_index.py | 6 ++---- test/test_note.py | 6 +++--- test/test_oid.py | 8 ++++++++ test/test_repository.py | 15 ++++++++++----- test/test_tag.py | 2 +- test/utils.py | 3 +-- 16 files changed, 77 insertions(+), 44 deletions(-) diff --git a/src/index.c b/src/index.c index fc10116..67defb7 100644 --- a/src/index.c +++ b/src/index.c @@ -423,7 +423,7 @@ Index_write_tree(Index *self) if (err < 0) return Error_set(err); - return git_oid_to_python(oid.id); + return git_oid_to_python(&oid); } PyMethodDef Index_methods[] = { @@ -585,7 +585,7 @@ PyDoc_STRVAR(IndexEntry_oid__doc__, "Object id."); PyObject * IndexEntry_oid__get__(IndexEntry *self) { - return git_oid_to_python(self->entry->oid.id); + return git_oid_to_python(&self->entry->oid); } diff --git a/src/object.c b/src/object.c index fc7922f..f5f9c4f 100644 --- a/src/object.c +++ b/src/object.c @@ -60,7 +60,7 @@ Object_oid__get__(Object *self) oid = git_object_id(self->obj); assert(oid); - return git_oid_to_python(oid->id); + return git_oid_to_python(oid); } diff --git a/src/oid.c b/src/oid.c index c671aa6..3bc79a9 100644 --- a/src/oid.c +++ b/src/oid.c @@ -32,6 +32,20 @@ #include "error.h" #include "oid.h" +PyTypeObject OidType; + + +PyObject * +git_oid_to_python(const git_oid *oid) +{ + Oid *py_oid; + + py_oid = PyObject_New(Oid, &OidType); + git_oid_cpy(&(py_oid->oid), oid); + return (PyObject*)py_oid; +} + + int py_str_to_git_oid(PyObject *py_str, git_oid *oid) { @@ -40,7 +54,13 @@ py_str_to_git_oid(PyObject *py_str, git_oid *oid) int err; Py_ssize_t len; - /* Case 1: raw sha */ + /* Case 1: Git Oid */ + if (PyObject_TypeCheck(py_str, (PyTypeObject*)&OidType)) { + git_oid_cpy(oid, &((Oid*)py_str)->oid); + return GIT_OID_RAWSZ; + } + + /* Case 2: raw sha (bytes) */ if (PyBytes_Check(py_str)) { err = PyBytes_AsStringAndSize(py_str, &hex_or_bin, &len); if (err) @@ -53,7 +73,7 @@ py_str_to_git_oid(PyObject *py_str, git_oid *oid) return len * 2; } - /* Case 2: hex sha */ + /* Case 3: hex sha (unicode) */ if (PyUnicode_Check(py_str)) { py_hex = PyUnicode_AsASCIIString(py_str); if (py_hex == NULL) @@ -159,12 +179,19 @@ Oid_init(Oid *self, PyObject *args, PyObject *kw) } +int +Oid_compare(PyObject *o1, PyObject *o2) +{ + return git_oid_cmp(&((Oid*)o1)->oid, &((Oid*)o2)->oid); +} + + PyDoc_STRVAR(Oid_raw__doc__, "Raw oid."); PyObject * Oid_raw__get__(Oid *self) { - return git_oid_to_python(self->oid.id); + return PyBytes_FromStringAndSize((const char*)self->oid.id, GIT_OID_RAWSZ); } @@ -193,7 +220,7 @@ PyTypeObject OidType = { 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - 0, /* tp_compare */ + (cmpfunc)Oid_compare, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ diff --git a/src/oid.h b/src/oid.h index 7dce303..796d087 100644 --- a/src/oid.h +++ b/src/oid.h @@ -34,10 +34,8 @@ int py_str_to_git_oid(PyObject *py_str, git_oid *oid); int py_str_to_git_oid_expand(git_repository *repo, PyObject *py_str, - git_oid *oid); + git_oid *oid); +PyObject* git_oid_to_python(const git_oid *oid); PyObject* git_oid_to_py_str(const git_oid *oid); -#define git_oid_to_python(id) \ - PyBytes_FromStringAndSize((const char*)id, GIT_OID_RAWSZ) - #endif diff --git a/src/pygit2.c b/src/pygit2.c index 6b93630..4393870 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -142,7 +142,7 @@ hashfile(PyObject *self, PyObject *args) if (err < 0) return Error_set(err); - return git_oid_to_python(oid.id); + return git_oid_to_python(&oid); } PyDoc_STRVAR(hash__doc__, @@ -166,7 +166,7 @@ hash(PyObject *self, PyObject *args) return Error_set(err); } - return git_oid_to_python(oid.id); + return git_oid_to_python(&oid); } diff --git a/src/reference.c b/src/reference.c index 1c3ded8..0de1134 100644 --- a/src/reference.c +++ b/src/reference.c @@ -276,7 +276,7 @@ Reference_oid__get__(Reference *self) } /* Convert and return it */ - return git_oid_to_python(oid->id); + return git_oid_to_python(oid); } int diff --git a/src/repository.c b/src/repository.c index d68fcfd..17f5ada 100644 --- a/src/repository.c +++ b/src/repository.c @@ -403,7 +403,7 @@ Repository_write(Repository *self, PyObject *args) stream->write(stream, buffer, buflen); err = stream->finalize_write(&oid, stream); stream->free(stream); - return git_oid_to_python(oid.id); + return git_oid_to_python(&oid); } @@ -578,7 +578,7 @@ Repository_create_blob(Repository *self, PyObject *args) if (err < 0) return Error_set(err); - return git_oid_to_python(oid.id); + return git_oid_to_python(&oid); } @@ -601,7 +601,7 @@ Repository_create_blob_fromfile(Repository *self, PyObject *args) if (err < 0) return Error_set(err); - return git_oid_to_python(oid.id); + return git_oid_to_python(&oid); } @@ -674,7 +674,7 @@ Repository_create_commit(Repository *self, PyObject *args) goto out; } - py_result = git_oid_to_python(oid.id); + py_result = git_oid_to_python(&oid); out: free(message); @@ -722,7 +722,7 @@ Repository_create_tag(Repository *self, PyObject *args) git_object_free(target); if (err < 0) return Error_set_oid(err, &oid, len); - return git_oid_to_python(oid.id); + return git_oid_to_python(&oid); } @@ -1171,7 +1171,7 @@ Repository_create_note(Repository *self, PyObject* args) if (err < 0) return Error_set(err); - return git_oid_to_python(note_id.id); + return git_oid_to_python(¬e_id); } diff --git a/src/tag.c b/src/tag.c index 4edbde1..bc4eaad 100644 --- a/src/tag.c +++ b/src/tag.c @@ -43,7 +43,7 @@ Tag_target__get__(Tag *self) const git_oid *oid; oid = git_tag_target_id(self->tag); - return git_oid_to_python(oid->id); + return git_oid_to_python(oid); } diff --git a/src/tree.c b/src/tree.c index 6959a04..05ff4b6 100644 --- a/src/tree.c +++ b/src/tree.c @@ -74,7 +74,7 @@ TreeEntry_oid__get__(TreeEntry *self) const git_oid *oid; oid = git_tree_entry_id(self->entry); - return git_oid_to_python(oid->id); + return git_oid_to_python(oid); } diff --git a/test/test_blob.py b/test/test_blob.py index 221ddcc..9123cbc 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -49,7 +49,7 @@ class BlobTest(utils.RepoTestCase): def test_read_blob(self): blob = self.repo[BLOB_SHA] self.assertEqual(blob.hex, BLOB_SHA) - sha = utils.oid_to_hex(blob.oid) + sha = blob.oid.hex self.assertEqual(sha, BLOB_SHA) self.assertTrue(isinstance(blob, pygit2.Blob)) self.assertEqual(pygit2.GIT_OBJ_BLOB, blob.type) @@ -66,9 +66,8 @@ class BlobTest(utils.RepoTestCase): self.assertEqual(blob_oid, blob.oid) self.assertEqual( - utils.gen_blob_sha1(BLOB_NEW_CONTENT), - utils.oid_to_hex(blob_oid) - ) + utils.gen_blob_sha1(BLOB_NEW_CONTENT), + blob_oid.hex) self.assertEqual(BLOB_NEW_CONTENT, blob.data) self.assertEqual(len(BLOB_NEW_CONTENT), blob.size) @@ -84,9 +83,8 @@ class BlobTest(utils.RepoTestCase): self.assertEqual(blob_oid, blob.oid) self.assertEqual( - utils.gen_blob_sha1(BLOB_FILE_CONTENT), - utils.oid_to_hex(blob_oid) - ) + utils.gen_blob_sha1(BLOB_FILE_CONTENT), + blob_oid.hex) self.assertEqual(BLOB_FILE_CONTENT, blob.data) self.assertEqual(len(BLOB_FILE_CONTENT), blob.size) diff --git a/test/test_index.py b/test/test_index.py index 20d98e2..b581373 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -98,8 +98,7 @@ class IndexTest(utils.RepoTestCase): self.assertEqual(len(index), 1) # Test read-write returns the same oid oid = index.write_tree() - oid = utils.oid_to_hex(oid) - self.assertEqual(oid, tree_oid) + self.assertEqual(oid.hex, tree_oid) # Test the index is only modified in memory index.read() self.assertEqual(len(index), 2) @@ -107,8 +106,7 @@ class IndexTest(utils.RepoTestCase): def test_write_tree(self): oid = self.repo.index.write_tree() - sha = utils.oid_to_hex(oid) - self.assertEqual(sha, 'fd937514cb799514d4b81bb24c5fcfeb6472b245') + self.assertEqual(oid.hex, 'fd937514cb799514d4b81bb24c5fcfeb6472b245') def test_iter(self): index = self.repo.index diff --git a/test/test_note.py b/test/test_note.py index b0439e6..a493540 100644 --- a/test/test_note.py +++ b/test/test_note.py @@ -38,9 +38,9 @@ NOTE = ('6c8980ba963cad8b25a9bcaf68d4023ee57370d8', 'note message') NOTES = [ ('ab533997b80705767be3dae8cbb06a0740809f79', 'First Note - HEAD\n', - '784855caf26449a1914d2cf62d12b9374d76ae78'), + '784855caf26449a1914d2cf62d12b9374d76ae78'), ('d879714d880671ed84f8aaed8b27fca23ba01f27', 'Second Note - HEAD~1\n', - 'f5e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87') + 'f5e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87') ] class NotesTest(utils.BareRepoTestCase): @@ -49,7 +49,7 @@ class NotesTest(utils.BareRepoTestCase): annotated_id = self.repo.revparse_single('HEAD~3').hex author = committer = Signature('Foo bar', 'foo@bar.com', 12346, 0) note_id = self.repo.create_note(NOTE[1], author, committer, annotated_id) - self.assertEqual(NOTE[0], utils.oid_to_hex(note_id)) + self.assertEqual(NOTE[0], note_id.hex) # check the note blob self.assertEqual(NOTE[1].encode(), self.repo[note_id].data) diff --git a/test/test_oid.py b/test/test_oid.py index 7b8d8a4..26e980c 100644 --- a/test/test_oid.py +++ b/test/test_oid.py @@ -65,6 +65,14 @@ class OidTest(utils.BareRepoTestCase): self.assertRaises(ValueError, Oid, raw=RAW + b'a') self.assertRaises(ValueError, Oid, hex=HEX + 'a') + def test_cmp(self): + oid1 = Oid(raw=RAW) + oid2 = Oid(hex=HEX) + self.assertEqual(oid1, oid2) + + oid2 = Oid(hex="15b648aec6ed045b5ca6f57f8b7831a8b4757299") + self.assertNotEqual(oid1, oid2) + if __name__ == '__main__': unittest.main() diff --git a/test/test_repository.py b/test/test_repository.py index 0e7503d..6de720b 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -27,18 +27,22 @@ """Tests for Repository objects.""" +# Import from the future from __future__ import absolute_import from __future__ import unicode_literals + +# Import from the Standard Library import binascii import unittest import tempfile import os from os.path import join, realpath +# Import from pygit2 from pygit2 import GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT from pygit2 import init_repository, discover_repository, Commit, hashfile +from pygit2 import Oid import pygit2 - from . import utils @@ -85,8 +89,7 @@ class RepositoryTest(utils.BareRepoTestCase): self.assertRaises(ValueError, self.repo.write, GIT_OBJ_ANY, data) oid = self.repo.write(GIT_OBJ_BLOB, data) - self.assertEqual(type(oid), bytes) - self.assertEqual(len(oid), 20) + self.assertEqual(type(oid), Oid) def test_contains(self): self.assertRaises(TypeError, lambda: 123 in self.repo) @@ -222,16 +225,18 @@ class RepositoryTest_II(utils.RepoTestCase): self.repo.checkout(pygit2.GIT_CHECKOUT_FORCE, head=True) self.assertTrue('bye.txt' not in self.repo.status()) + class NewRepositoryTest(utils.NoRepoTestCase): + def test_new_repo(self): repo = init_repository(self._temp_dir, False) oid = repo.write(GIT_OBJ_BLOB, "Test") - self.assertEqual(type(oid), bytes) - self.assertEqual(len(oid), 20) + self.assertEqual(type(oid), Oid) assert os.path.exists(os.path.join(self._temp_dir, '.git')) + class InitRepositoryTest(utils.NoRepoTestCase): # under the assumption that repo.is_bare works diff --git a/test/test_tag.py b/test/test_tag.py index de79e60..12d89f0 100644 --- a/test/test_tag.py +++ b/test/test_tag.py @@ -72,7 +72,7 @@ class TagTest(utils.BareRepoTestCase): self.assertEqual('3ee44658fd11660e828dfc96b9b5c5f38d5b49bb', tag.hex) self.assertEqual(name, tag.name) - self.assertEqual(target, utils.oid_to_hex(tag.target)) + self.assertEqual(target, tag.target.hex) self.assertEqualSignature(tagger, tag.tagger) self.assertEqual(message, tag.message) self.assertEqual(name, self.repo[tag.hex].name) diff --git a/test/utils.py b/test/utils.py index 0cf41d5..27da92a 100644 --- a/test/utils.py +++ b/test/utils.py @@ -47,8 +47,6 @@ def force_rm_handle(remove_path, path, excinfo): ) remove_path(path) -def oid_to_hex(oid): - return b2a_hex(oid).decode('ascii') def gen_blob_sha1(data): # http://stackoverflow.com/questions/552659/assigning-git-sha1s-without-git @@ -58,6 +56,7 @@ def gen_blob_sha1(data): return m.hexdigest() + def rmtree(path): """In Windows a read-only file cannot be removed, and shutil.rmtree fails. So we implement our own version of rmtree to address this issue. From bd54d281570c15cb08d0d23f842a385de8a4c288 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 14 Apr 2013 13:50:09 +0200 Subject: [PATCH 03/12] Fix for Python3, and add Oid cmp tests --- src/oid.c | 31 ++++++++++++++++++++++++++----- test/test_oid.py | 9 +++++++++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/src/oid.c b/src/oid.c index 3bc79a9..ce5ff9d 100644 --- a/src/oid.c +++ b/src/oid.c @@ -179,10 +179,31 @@ Oid_init(Oid *self, PyObject *args, PyObject *kw) } -int -Oid_compare(PyObject *o1, PyObject *o2) +PyObject * +Oid_richcompare(PyObject *o1, PyObject *o2, int op) { - return git_oid_cmp(&((Oid*)o1)->oid, &((Oid*)o2)->oid); + PyObject *res; + + /* Support only equual (and not-equal). */ + if (op != Py_EQ && op != Py_NE) { + PyErr_SetNone(PyExc_TypeError); + return NULL; + } + + /* Comparing to something else than an Oid is not supported. */ + if (!PyObject_TypeCheck(o2, &OidType)) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + + /* Ok go. */ + if (git_oid_cmp(&((Oid*)o1)->oid, &((Oid*)o2)->oid) == 0) + res = (op == Py_EQ) ? Py_True : Py_False; + else + res = (op == Py_EQ) ? Py_False : Py_True; + + Py_INCREF(res); + return res; } @@ -220,7 +241,7 @@ PyTypeObject OidType = { 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - (cmpfunc)Oid_compare, /* tp_compare */ + 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ @@ -235,7 +256,7 @@ PyTypeObject OidType = { Oid__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ - 0, /* tp_richcompare */ + (richcmpfunc)Oid_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ diff --git a/test/test_oid.py b/test/test_oid.py index 26e980c..eb2a3fa 100644 --- a/test/test_oid.py +++ b/test/test_oid.py @@ -67,12 +67,21 @@ class OidTest(utils.BareRepoTestCase): def test_cmp(self): oid1 = Oid(raw=RAW) + + # Equal oid2 = Oid(hex=HEX) self.assertEqual(oid1, oid2) + # Not equal oid2 = Oid(hex="15b648aec6ed045b5ca6f57f8b7831a8b4757299") self.assertNotEqual(oid1, oid2) + # Other + with self.assertRaises(TypeError): oid1 < oid2 + with self.assertRaises(TypeError): oid1 <= oid2 + with self.assertRaises(TypeError): oid1 > oid2 + with self.assertRaises(TypeError): oid1 >= oid2 + if __name__ == '__main__': unittest.main() From a9c9f25ce2461b2766729011ea7cdb44ba674ca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 14 Apr 2013 14:08:46 +0200 Subject: [PATCH 04/12] oid: support rich comparisons (less than, etc.) --- src/oid.c | 32 ++++++++++++++++++++++---------- test/test_oid.py | 9 +++++---- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/oid.c b/src/oid.c index ce5ff9d..b40e157 100644 --- a/src/oid.c +++ b/src/oid.c @@ -183,12 +183,7 @@ PyObject * Oid_richcompare(PyObject *o1, PyObject *o2, int op) { PyObject *res; - - /* Support only equual (and not-equal). */ - if (op != Py_EQ && op != Py_NE) { - PyErr_SetNone(PyExc_TypeError); - return NULL; - } + int cmp; /* Comparing to something else than an Oid is not supported. */ if (!PyObject_TypeCheck(o2, &OidType)) { @@ -197,10 +192,27 @@ Oid_richcompare(PyObject *o1, PyObject *o2, int op) } /* Ok go. */ - if (git_oid_cmp(&((Oid*)o1)->oid, &((Oid*)o2)->oid) == 0) - res = (op == Py_EQ) ? Py_True : Py_False; - else - res = (op == Py_EQ) ? Py_False : Py_True; + cmp = git_oid_cmp(&((Oid*)o1)->oid, &((Oid*)o2)->oid); + switch (op) { + case Py_LT: + res = (cmp <= 0) ? Py_True: Py_False; + break; + case Py_LE: + res = (cmp < 0) ? Py_True: Py_False; + break; + case Py_EQ: + res = (cmp == 0) ? Py_True: Py_False; + break; + case Py_NE: + res = (cmp != 0) ? Py_True: Py_False; + break; + case Py_GT: + res = (cmp > 0) ? Py_True: Py_False; + break; + case Py_GE: + res = (cmp >= 0) ? Py_True: Py_False; + break; + } Py_INCREF(res); return res; diff --git a/test/test_oid.py b/test/test_oid.py index eb2a3fa..22dcb0b 100644 --- a/test/test_oid.py +++ b/test/test_oid.py @@ -77,10 +77,11 @@ class OidTest(utils.BareRepoTestCase): self.assertNotEqual(oid1, oid2) # Other - with self.assertRaises(TypeError): oid1 < oid2 - with self.assertRaises(TypeError): oid1 <= oid2 - with self.assertRaises(TypeError): oid1 > oid2 - with self.assertRaises(TypeError): oid1 >= oid2 + self.assertTrue(oid1 < oid2) + self.assertTrue(oid1 <= oid2) + self.assertFalse(oid1 == oid2) + self.assertFalse(oid1 > oid2) + self.assertFalse(oid1 >= oid2) if __name__ == '__main__': From f74a211f1428488b3b3632d2e722c07b00b8551e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Thu, 18 Apr 2013 20:32:30 +0200 Subject: [PATCH 05/12] oid: now only accept Oid or hex, not raw Every method that takes an oid has changed what it accepts. Before it was (in both Python 2 and 3): - An Oid object - An hex oid, represented as a unicode string - A raw oid, represented as a bytes string Now the behaviour is different between Python 2 and 3. Now in Python 2 we take: - An Oid object - An hex oid, represented as a bytes or unicode string Now in Python 3 we take: - An Oid object - An hex oid, represented as a unicode string We have dropt direct support for raw strings. To use a raw string first build an Oid object: oid = Oid(raw=raw) We have also dropt support for short raw oids. The Oid constructor takes full oids, if passed short oids the behavior is undefined. --- src/oid.c | 87 +++++++++++++++++++++++++---------------- test/test_oid.py | 11 ++++++ test/test_repository.py | 30 +++++++------- 3 files changed, 80 insertions(+), 48 deletions(-) diff --git a/src/oid.c b/src/oid.c index b40e157..f1a6f52 100644 --- a/src/oid.c +++ b/src/oid.c @@ -45,63 +45,71 @@ git_oid_to_python(const git_oid *oid) return (PyObject*)py_oid; } - int -py_str_to_git_oid(PyObject *py_str, git_oid *oid) +_oid_from_hex(PyObject *py_oid, git_oid *oid) { PyObject *py_hex; - char *hex_or_bin; int err; + char *hex; Py_ssize_t len; - /* Case 1: Git Oid */ - if (PyObject_TypeCheck(py_str, (PyTypeObject*)&OidType)) { - git_oid_cpy(oid, &((Oid*)py_str)->oid); - return GIT_OID_RAWSZ; - } - - /* Case 2: raw sha (bytes) */ - if (PyBytes_Check(py_str)) { - err = PyBytes_AsStringAndSize(py_str, &hex_or_bin, &len); +#if PY_MAJOR_VERSION == 2 + /* Bytes (only supported in Python 2) */ + if (PyBytes_Check(py_oid)) { + err = PyBytes_AsStringAndSize(py_oid, &hex, &len); if (err) return -1; - if (len > GIT_OID_RAWSZ) { - PyErr_SetObject(PyExc_ValueError, py_str); + + err = git_oid_fromstrn(oid, hex, len); + if (err < 0) { + PyErr_SetObject(Error_type(err), py_oid); return -1; } - memcpy(oid->id, (const unsigned char*)hex_or_bin, len); - return len * 2; - } - /* Case 3: hex sha (unicode) */ - if (PyUnicode_Check(py_str)) { - py_hex = PyUnicode_AsASCIIString(py_str); + return len; + } +#endif + + /* Unicode */ + if (PyUnicode_Check(py_oid)) { + py_hex = PyUnicode_AsASCIIString(py_oid); if (py_hex == NULL) return -1; - err = PyBytes_AsStringAndSize(py_hex, &hex_or_bin, &len); + + err = PyBytes_AsStringAndSize(py_hex, &hex, &len); if (err) { Py_DECREF(py_hex); return -1; } - err = git_oid_fromstrn(oid, hex_or_bin, len); - + err = git_oid_fromstrn(oid, hex, len); Py_DECREF(py_hex); - if (err < 0) { - PyErr_SetObject(Error_type(err), py_str); + PyErr_SetObject(Error_type(err), py_oid); return -1; } + return len; } /* Type error */ - PyErr_Format(PyExc_TypeError, - "Git object id must be byte or a text string, not: %.200s", - Py_TYPE(py_str)->tp_name); + PyErr_SetObject(PyExc_TypeError, py_oid); return -1; } +int +py_str_to_git_oid(PyObject *py_oid, git_oid *oid) +{ + /* Oid */ + if (PyObject_TypeCheck(py_oid, (PyTypeObject*)&OidType)) { + git_oid_cpy(oid, &((Oid*)py_oid)->oid); + return GIT_OID_RAWSZ; + } + + /* Hex */ + return _oid_from_hex(py_oid, oid); +} + int py_str_to_git_oid_expand(git_repository *repo, PyObject *py_str, git_oid *oid) { @@ -152,6 +160,8 @@ Oid_init(Oid *self, PyObject *args, PyObject *kw) char *keywords[] = {"raw", "hex", NULL}; PyObject *raw = NULL, *hex = NULL; int err; + char *bytes; + Py_ssize_t len; if (!PyArg_ParseTupleAndKeywords(args, kw, "|OO", keywords, &raw, &hex)) return -1; @@ -166,12 +176,23 @@ Oid_init(Oid *self, PyObject *args, PyObject *kw) return -1; } - /* Get the oid. */ - if (raw != NULL) - err = py_str_to_git_oid(raw, &self->oid); - else - err = py_str_to_git_oid(hex, &self->oid); + /* Case 1: raw */ + if (raw != NULL) { + err = PyBytes_AsStringAndSize(raw, &bytes, &len); + if (err) + return -1; + if (len > GIT_OID_RAWSZ) { + PyErr_SetObject(PyExc_ValueError, raw); + return -1; + } + + memcpy(self->oid.id, (const unsigned char*)bytes, len); + return 0; + } + + /* Case 2: hex */ + err = _oid_from_hex(hex, &self->oid); if (err < 0) return -1; diff --git a/test/test_oid.py b/test/test_oid.py index 22dcb0b..97c7979 100644 --- a/test/test_oid.py +++ b/test/test_oid.py @@ -33,6 +33,7 @@ from __future__ import unicode_literals # Import from the Standard Library from binascii import unhexlify +from sys import version_info import unittest # Import from pygit2 @@ -55,6 +56,16 @@ class OidTest(utils.BareRepoTestCase): self.assertEqual(oid.raw, RAW) self.assertEqual(oid.hex, HEX) + def test_hex_bytes(self): + if version_info.major == 2: + hex = bytes(HEX) + oid = Oid(hex=hex) + self.assertEqual(oid.raw, RAW) + self.assertEqual(oid.hex, HEX) + else: + hex = bytes(HEX, "ascii") + self.assertRaises(TypeError, Oid, hex=hex) + def test_none(self): self.assertRaises(ValueError, Oid) diff --git a/test/test_repository.py b/test/test_repository.py index 39fea40..373730e 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -48,8 +48,9 @@ from . import utils HEAD_SHA = '784855caf26449a1914d2cf62d12b9374d76ae78' PARENT_SHA = 'f5e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87' # HEAD^ -A_HEX_SHA = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' -A_BIN_SHA = binascii.unhexlify(A_HEX_SHA.encode('ascii')) +BLOB_HEX = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' +BLOB_RAW = binascii.unhexlify(BLOB_HEX.encode('ascii')) +BLOB_OID = Oid(raw=BLOB_RAW) class RepositoryTest(utils.BareRepoTestCase): @@ -71,15 +72,15 @@ class RepositoryTest(utils.BareRepoTestCase): self.assertRaises(TypeError, self.repo.read, 123) self.assertRaisesWithArg(KeyError, '1' * 40, self.repo.read, '1' * 40) - ab = self.repo.read(A_BIN_SHA) - a = self.repo.read(A_HEX_SHA) + ab = self.repo.read(BLOB_OID) + a = self.repo.read(BLOB_HEX) self.assertEqual(ab, a) self.assertEqual((GIT_OBJ_BLOB, b'a contents\n'), a) a2 = self.repo.read('7f129fd57e31e935c6d60a0c794efe4e6927664b') self.assertEqual((GIT_OBJ_BLOB, b'a contents 2\n'), a2) - a_hex_prefix = A_HEX_SHA[:4] + a_hex_prefix = BLOB_HEX[:4] a3 = self.repo.read(a_hex_prefix) self.assertEqual((GIT_OBJ_BLOB, b'a contents\n'), a3) @@ -93,29 +94,28 @@ class RepositoryTest(utils.BareRepoTestCase): def test_contains(self): self.assertRaises(TypeError, lambda: 123 in self.repo) - self.assertTrue(A_BIN_SHA in self.repo) - self.assertTrue(A_BIN_SHA[:10] in self.repo) - self.assertTrue(A_HEX_SHA in self.repo) - self.assertTrue(A_HEX_SHA[:10] in self.repo) + self.assertTrue(BLOB_OID in self.repo) + self.assertTrue(BLOB_HEX in self.repo) + self.assertTrue(BLOB_HEX[:10] in self.repo) self.assertFalse('a' * 40 in self.repo) self.assertFalse('a' * 20 in self.repo) def test_iterable(self): l = [ obj for obj in self.repo ] - self.assertTrue(A_HEX_SHA in l) + self.assertTrue(BLOB_HEX in l) def test_lookup_blob(self): self.assertRaises(TypeError, lambda: self.repo[123]) - self.assertEqual(self.repo[A_BIN_SHA].hex, A_HEX_SHA) - a = self.repo[A_HEX_SHA] + self.assertEqual(self.repo[BLOB_OID].hex, BLOB_HEX) + a = self.repo[BLOB_HEX] self.assertEqual(b'a contents\n', a.read_raw()) - self.assertEqual(A_HEX_SHA, a.hex) + self.assertEqual(BLOB_HEX, a.hex) self.assertEqual(GIT_OBJ_BLOB, a.type) def test_lookup_blob_prefix(self): - a = self.repo[A_HEX_SHA[:5]] + a = self.repo[BLOB_HEX[:5]] self.assertEqual(b'a contents\n', a.read_raw()) - self.assertEqual(A_HEX_SHA, a.hex) + self.assertEqual(BLOB_HEX, a.hex) self.assertEqual(GIT_OBJ_BLOB, a.type) def test_lookup_commit(self): From 4cf1fc2371f9eb9caf6db3fd93190391ae05ba16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Thu, 18 Apr 2013 23:57:19 +0200 Subject: [PATCH 06/12] Make Oid hashable --- src/oid.c | 10 +++++++++- src/utils.h | 5 +++++ test/test_oid.py | 10 ++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/oid.c b/src/oid.c index f1a6f52..ae91d04 100644 --- a/src/oid.c +++ b/src/oid.c @@ -200,6 +200,14 @@ Oid_init(Oid *self, PyObject *args, PyObject *kw) } +Py_hash_t +Oid_hash(PyObject *oid) +{ + /* TODO Randomize (use _Py_HashSecret) to avoid collission DoS attacks? */ + return *(Py_hash_t*) ((Oid*)oid)->oid.id; +} + + PyObject * Oid_richcompare(PyObject *o1, PyObject *o2, int op) { @@ -279,7 +287,7 @@ PyTypeObject OidType = { 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ - 0, /* tp_hash */ + (hashfunc)Oid_hash, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ diff --git a/src/utils.h b/src/utils.h index 0dbe20b..2ce8074 100644 --- a/src/utils.h +++ b/src/utils.h @@ -54,6 +54,11 @@ #define to_encoding(x) PyUnicode_DecodeASCII(x, strlen(x), "strict") #endif +#ifndef Py_hash_t + #define Py_hash_t long +#endif + + #define CHECK_REFERENCE(self)\ if (self->reference == NULL) {\ PyErr_SetString(GitError, "deleted reference");\ diff --git a/test/test_oid.py b/test/test_oid.py index 97c7979..5e6b223 100644 --- a/test/test_oid.py +++ b/test/test_oid.py @@ -94,6 +94,16 @@ class OidTest(utils.BareRepoTestCase): self.assertFalse(oid1 > oid2) self.assertFalse(oid1 >= oid2) + def test_hash(self): + s = set() + s.add(Oid(raw=RAW)) + s.add(Oid(hex=HEX)) + self.assertEqual(len(s), 1) + + s.add(Oid(hex="0000000000000000000000000000000000000000")) + s.add(Oid(hex="0000000000000000000000000000000000000001")) + self.assertEqual(len(s), 3) + if __name__ == '__main__': unittest.main() From 94f44feb2275d41a25efef68728f5ccf16f68d79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Thu, 18 Apr 2013 23:59:34 +0200 Subject: [PATCH 07/12] Fix unit tests with Python 2.6 --- test/test_oid.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_oid.py b/test/test_oid.py index 5e6b223..c303ce6 100644 --- a/test/test_oid.py +++ b/test/test_oid.py @@ -57,7 +57,7 @@ class OidTest(utils.BareRepoTestCase): self.assertEqual(oid.hex, HEX) def test_hex_bytes(self): - if version_info.major == 2: + if version_info[0] == 2: hex = bytes(HEX) oid = Oid(hex=hex) self.assertEqual(oid.raw, RAW) From 37f0fed249194044d4a00d398bfc8bb32cd8a880 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 20 Apr 2013 00:03:01 +0200 Subject: [PATCH 08/12] docs: add a section about the Oid type --- docs/objects.rst | 62 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/docs/objects.rst b/docs/objects.rst index aa7534c..1b61775 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -5,10 +5,66 @@ Git objects .. contents:: Contents :local: +In the first place Git is a key-value storage system. The keys are called +OIDs, for Object id, and the values stored are called Objects. -In the first place Git is a key-value storage system. The values stored are -called *objects*, there are four types (commits, trees, blobs and tags), -for each type pygit2 has a Python class:: +Oids +================= + +The oid is the `SHA-1 `_ hash of an +object. It is 20 bytes long: + +- When we represent an oid as a 20 bytes Python string, we say it is a raw + oid. + +- When we represent an oid as a 40 chars Python string, we sayt it is a hex + oid. + +However, most of the time we will use the Oid type. We can explicetly create +an Oid object from its raw or hexadecimal form:: + + >>> hex = "cff3ceaefc955f0dbe1957017db181bc49913781" + >>> oid1 = Oid(hex=hex) + + >>> from binascii import unhexlify + >>> raw = unhexlify(hex) + >>> oid2 = Oid(raw=raw) + + >>> print oid1 == oid2 + True + +And in the opposite direction, we can get the raw or hexadecimal form from +an Oid object: + +.. autoattribute:: pygit2.Oid.raw +.. autoattribute:: pygit2.Oid.hex + +The Oid type supports: + +- rich comparisons, not just for equality, also: lesser-than, lesser-or-equal, + etc. + +- hashing, so Oid objects can be used as keys in a dictionary + + +Python 2 and Python 3 +--------------------- + +There is a difference on how the library handles hex oids, depending on +whether we are using Python 2 or 3. + +- In Python 2, we can represent an hexadecimal oid using a bytes string + (``str``) or a text string (``unicode``) + +- In Python 3, hexadecimal oids can only be represented using unicode + strings. + + +Objects +================= + +There are four types (commits, trees, blobs and tags), for each type pygit2 +has a Python class:: >>> # Show commits and trees >>> commit From 7261f4e1e7f5c0aa895b4a99c3ce10c2b963bc7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 20 Apr 2013 08:48:03 +0200 Subject: [PATCH 09/12] Now Note.oid returns an Oid object, not an hex --- src/note.c | 2 +- test/test_note.py | 26 ++++++++++++++------------ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/note.c b/src/note.c index 73dc90a..c7ef0f2 100644 --- a/src/note.c +++ b/src/note.c @@ -72,7 +72,7 @@ PyDoc_STRVAR(Note_oid__doc__, PyObject * Note_oid__get__(Note *self) { - return git_oid_to_py_str(git_note_oid(self->note)); + return git_oid_to_python(git_note_oid(self->note)); } diff --git a/test/test_note.py b/test/test_note.py index a493540..37c00fd 100644 --- a/test/test_note.py +++ b/test/test_note.py @@ -37,18 +37,19 @@ from . import utils NOTE = ('6c8980ba963cad8b25a9bcaf68d4023ee57370d8', 'note message') NOTES = [ - ('ab533997b80705767be3dae8cbb06a0740809f79', 'First Note - HEAD\n', - '784855caf26449a1914d2cf62d12b9374d76ae78'), - ('d879714d880671ed84f8aaed8b27fca23ba01f27', 'Second Note - HEAD~1\n', - 'f5e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87') -] + ('ab533997b80705767be3dae8cbb06a0740809f79', 'First Note - HEAD\n', + '784855caf26449a1914d2cf62d12b9374d76ae78'), + ('d879714d880671ed84f8aaed8b27fca23ba01f27', 'Second Note - HEAD~1\n', + 'f5e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87') + ] class NotesTest(utils.BareRepoTestCase): - + def test_create_note(self): annotated_id = self.repo.revparse_single('HEAD~3').hex author = committer = Signature('Foo bar', 'foo@bar.com', 12346, 0) - note_id = self.repo.create_note(NOTE[1], author, committer, annotated_id) + note_id = self.repo.create_note(NOTE[1], author, committer, + annotated_id) self.assertEqual(NOTE[0], note_id.hex) # check the note blob @@ -57,22 +58,23 @@ class NotesTest(utils.BareRepoTestCase): def test_lookup_note(self): annotated_id = self.repo.head.hex note = self.repo.lookup_note(annotated_id) - self.assertEqual(NOTES[0][0], note.oid) + self.assertEqual(NOTES[0][0], note.oid.hex) self.assertEqual(NOTES[0][1], note.message) def test_remove_note(self): note = self.repo.lookup_note(self.repo.head.hex) author = committer = Signature('Foo bar', 'foo@bar.com', 12346, 0) note.remove(author, committer) - self.assertRaises(KeyError, lambda: self.repo.lookup_note(self.repo.head.hex)) + self.assertRaises(KeyError, self.repo.lookup_note, self.repo.head.hex) def test_iterate_notes(self): for i, note in enumerate(self.repo.notes()): - entry = (note.oid, note.message, note.annotated_id) - self.assertEqual(NOTES[i],entry) + entry = (note.oid.hex, note.message, note.annotated_id) + self.assertEqual(NOTES[i], entry) def test_iterate_non_existing_ref(self): - self.assertRaises(KeyError, lambda: self.repo.notes("refs/notes/bad_ref")) + self.assertRaises(KeyError, self.repo.notes, "refs/notes/bad_ref") + if __name__ == '__main__': unittest.main() From a3a928a513f681613a4e470cf33921dc645b6919 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 20 Apr 2013 09:03:13 +0200 Subject: [PATCH 10/12] Now Reference.target returns an Oid, not hex If the reference is direct, of course. --- src/reference.c | 39 +++++++++++++++++---------------------- test/test_refs.py | 2 +- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/src/reference.c b/src/reference.c index 0de1134..880fc06 100644 --- a/src/reference.c +++ b/src/reference.c @@ -204,18 +204,17 @@ Reference_target__get__(Reference *self) CHECK_REFERENCE(self); - /* Get the target */ - if (GIT_REF_OID == git_reference_type(self->reference)) { - return git_oid_to_py_str(git_reference_target(self->reference)); - } else { - c_name = git_reference_symbolic_target(self->reference); - if (c_name == NULL) { - PyErr_SetString(PyExc_ValueError, "no target available"); - return NULL; - } + /* Case 1: Direct */ + if (GIT_REF_OID == git_reference_type(self->reference)) + return git_oid_to_python(git_reference_target(self->reference)); + + /* Case 2: Symbolic */ + c_name = git_reference_symbolic_target(self->reference); + if (c_name == NULL) { + PyErr_SetString(PyExc_ValueError, "no target available"); + return NULL; } - /* Make a PyString and return it */ return to_path(c_name); } @@ -262,21 +261,17 @@ PyDoc_STRVAR(Reference_oid__doc__, "Object id."); PyObject * Reference_oid__get__(Reference *self) { - const git_oid *oid; - CHECK_REFERENCE(self); - /* Get the oid (only for "direct" references) */ - oid = git_reference_target(self->reference); - if (oid == NULL) { - PyErr_SetString(PyExc_ValueError, - "oid is only available if the reference is direct " - "(i.e. not symbolic)"); - return NULL; - } + /* Case 1: Direct */ + if (GIT_REF_OID == git_reference_type(self->reference)) + return git_oid_to_python(git_reference_target(self->reference)); - /* Convert and return it */ - return git_oid_to_python(oid); + /* Get the oid (only for "direct" references) */ + PyErr_SetString(PyExc_ValueError, + "oid is only available if the reference is direct " + "(i.e. not symbolic)"); + return NULL; } int diff --git a/test/test_refs.py b/test/test_refs.py index fb00182..6ef1b99 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -176,7 +176,7 @@ class ReferencesTest(utils.RepoTestCase): self.assertTrue('refs/tags/version1' in refs) reference = self.repo.lookup_reference('refs/tags/version1') self.assertEqual(reference.hex, LAST_COMMIT) - self.assertEqual(reference.target, LAST_COMMIT) + self.assertEqual(reference.target.hex, LAST_COMMIT) # try to create existing reference self.assertRaises(ValueError, self.repo.create_reference, From fd6768550008745309c39ead6a3c2a41b5f58adb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 20 Apr 2013 09:38:12 +0200 Subject: [PATCH 11/12] The repository iterator now returns Oids, not hexs --- src/repository.c | 11 +++++------ test/test_repository.py | 3 ++- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/repository.c b/src/repository.c index 74c7a50..10a0007 100644 --- a/src/repository.c +++ b/src/repository.c @@ -132,10 +132,10 @@ static int Repository_build_as_iter(const git_oid *oid, void *accum) { int err; - PyObject *oid_str = git_oid_to_py_str(oid); + PyObject *py_oid = git_oid_to_python(oid); - err = PyList_Append((PyObject*)accum, oid_str); - Py_DECREF(oid_str); + err = PyList_Append((PyObject*)accum, py_oid); + Py_DECREF(py_oid); return err; } @@ -152,11 +152,10 @@ Repository_as_iter(Repository *self) err = git_odb_foreach(odb, Repository_build_as_iter, (void*)accum); git_odb_free(odb); - if (err == GIT_EUSER) { + if (err == GIT_EUSER) return NULL; - } else if (err < 0) { + if (err < 0) return Error_set(err); - } return PyObject_GetIter(accum); } diff --git a/test/test_repository.py b/test/test_repository.py index 373730e..5bc5f61 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -102,7 +102,8 @@ class RepositoryTest(utils.BareRepoTestCase): def test_iterable(self): l = [ obj for obj in self.repo ] - self.assertTrue(BLOB_HEX in l) + oid = Oid(hex=BLOB_HEX) + self.assertTrue(oid in l) def test_lookup_blob(self): self.assertRaises(TypeError, lambda: self.repo[123]) From fbd93f82aab001cae101525fa1872044cc93436c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 20 Apr 2013 10:09:52 +0200 Subject: [PATCH 12/12] Python 2: Return a byte string for hex oids --- src/oid.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/oid.c b/src/oid.c index ae91d04..0e32355 100644 --- a/src/oid.c +++ b/src/oid.c @@ -150,7 +150,12 @@ git_oid_to_py_str(const git_oid *oid) char hex[GIT_OID_HEXSZ]; git_oid_fmt(hex, oid); + + #if PY_MAJOR_VERSION == 2 + return PyBytes_FromStringAndSize(hex, GIT_OID_HEXSZ); + #else return to_unicode_n(hex, GIT_OID_HEXSZ, "utf-8", "strict"); + #endif }