From 7b8ae0e10c6593dafca6680db4575514bd5b9475 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= <jdavid@itaapy.com>
Date: Wed, 10 Aug 2011 22:52:42 +0200
Subject: [PATCH] Python 3, now pygit2 builds

Tests do not yet pass.
---
 pygit2.c      | 169 ++++++++++++++++++++++++++++++--------------------
 test/utils.py |   8 ++-
 2 files changed, 107 insertions(+), 70 deletions(-)

diff --git a/pygit2.c b/pygit2.c
index 623ad63..59aa7b5 100644
--- a/pygit2.c
+++ b/pygit2.c
@@ -29,10 +29,25 @@
 #include <Python.h>
 #include <git2.h>
 
-/* Define PyVarObject_HEAD_INIT for Python 2.5 */
+/* Python 2.5 support */
+#ifndef Py_TYPE
+  #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
+#endif
 #ifndef PyVarObject_HEAD_INIT
-#define PyVarObject_HEAD_INIT(type, size) \
-    PyObject_HEAD_INIT(type) size,
+  #define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size,
+#endif
+
+/* Python 3 support */
+#if PY_MAJOR_VERSION >= 3
+  #define PyInt_AsLong PyLong_AsLong
+  #define PyInt_Check PyLong_Check
+  #define PyInt_FromLong PyLong_FromLong
+  #define PyString_AS_STRING PyBytes_AS_STRING
+  #define PyString_AsString PyBytes_AsString
+  #define PyString_Check PyBytes_Check
+  #define PyString_FromString PyBytes_FromString
+  #define PyString_FromStringAndSize PyBytes_FromStringAndSize
+  #define PyString_Size PyBytes_Size
 #endif
 
 
@@ -175,8 +190,8 @@ Error_set_py_obj(int err, PyObject *py_obj)
 
     if (err == GIT_ENOTOID && !PyString_Check(py_obj)) {
         PyErr_Format(PyExc_TypeError,
-                     "Git object id must be 40 byte hexadecimal str, or 20 byte binary str: %.200s",
-                     py_obj->ob_type->tp_name);
+                     "Git object id must be byte string, not: %.200s",
+                     Py_TYPE(py_obj)->tp_name);
         return NULL;
     }
     else if (err == GIT_ENOTFOUND) {
@@ -322,7 +337,7 @@ Repository_dealloc(Repository *self)
     if (self->repo)
         git_repository_free(self->repo);
     Py_XDECREF(self->index);
-    self->ob_type->tp_free((PyObject*)self);
+    Py_TYPE(self)->tp_free((PyObject*)self);
 }
 
 static int
@@ -847,8 +862,7 @@ static PyMappingMethods Repository_as_mapping = {
 };
 
 static PyTypeObject RepositoryType = {
-    PyObject_HEAD_INIT(NULL)
-    0,                                         /* ob_size           */
+    PyVarObject_HEAD_INIT(NULL, 0)
     "pygit2.Repository",                       /* tp_name           */
     sizeof(Repository),                        /* tp_basicsize      */
     0,                                         /* tp_itemsize       */
@@ -893,7 +907,7 @@ Object_dealloc(Object* self)
 {
     git_object_close(self->obj);
     Py_XDECREF(self->repo);
-    self->ob_type->tp_free((PyObject*)self);
+    Py_TYPE(self)->tp_free((PyObject*)self);
 }
 
 static PyObject *
@@ -957,8 +971,7 @@ static PyMethodDef Object_methods[] = {
 };
 
 static PyTypeObject ObjectType = {
-    PyObject_HEAD_INIT(NULL)
-    0,                                         /* ob_size           */
+    PyVarObject_HEAD_INIT(NULL, 0)
     "pygit2.Object",                           /* tp_name           */
     sizeof(Object),                            /* tp_basicsize      */
     0,                                         /* tp_itemsize       */
@@ -1101,8 +1114,7 @@ static PyGetSetDef Commit_getseters[] = {
 };
 
 static PyTypeObject CommitType = {
-    PyObject_HEAD_INIT(NULL)
-    0,                                         /* ob_size           */
+    PyVarObject_HEAD_INIT(NULL, 0)
     "pygit2.Commit",                           /* tp_name           */
     sizeof(Commit),                            /* tp_basicsize      */
     0,                                         /* tp_itemsize       */
@@ -1146,7 +1158,7 @@ static void
 TreeEntry_dealloc(TreeEntry *self)
 {
     Py_XDECREF(self->tree);
-    self->ob_type->tp_free((PyObject *)self);
+    Py_TYPE(self)->tp_free((PyObject *)self);
 }
 
 static PyObject *
@@ -1190,8 +1202,7 @@ static PyMethodDef TreeEntry_methods[] = {
 };
 
 static PyTypeObject TreeEntryType = {
-    PyObject_HEAD_INIT(NULL)
-    0,                                         /* ob_size           */
+    PyVarObject_HEAD_INIT(NULL, 0)
     "pygit2.TreeEntry",                        /* tp_name           */
     sizeof(TreeEntry),                         /* tp_basicsize      */
     0,                                         /* tp_itemsize       */
@@ -1354,7 +1365,7 @@ Tree_getitem(Tree *self, PyObject *value)
     else {
         PyErr_Format(PyExc_TypeError,
                      "Tree entry index must be int or str, not %.200s",
-                     value->ob_type->tp_name);
+                     Py_TYPE(value)->tp_name);
         return NULL;
     }
 }
@@ -1377,8 +1388,7 @@ static PyMappingMethods Tree_as_mapping = {
 };
 
 static PyTypeObject TreeType = {
-    PyObject_HEAD_INIT(NULL)
-    0,                                         /* ob_size           */
+    PyVarObject_HEAD_INIT(NULL, 0)
     "pygit2.Tree",                             /* tp_name           */
     sizeof(Tree),                              /* tp_basicsize      */
     0,                                         /* tp_itemsize       */
@@ -1475,8 +1485,7 @@ static PyGetSetDef Blob_getseters[] = {
 };
 
 static PyTypeObject BlobType = {
-    PyObject_HEAD_INIT(NULL)
-    0,                                         /* ob_size           */
+    PyVarObject_HEAD_INIT(NULL, 0)
     "pygit2.Blob",                             /* tp_name           */
     sizeof(Blob),                              /* tp_basicsize      */
     0,                                         /* tp_itemsize       */
@@ -1520,7 +1529,7 @@ static void
 Tag_dealloc(Tag *self)
 {
     Py_XDECREF(self->target);
-    self->ob_type->tp_free((PyObject*)self);
+    Py_TYPE(self)->tp_free((PyObject*)self);
 }
 
 static PyObject *
@@ -1580,8 +1589,7 @@ static PyGetSetDef Tag_getseters[] = {
 };
 
 static PyTypeObject TagType = {
-    PyObject_HEAD_INIT(NULL)
-    0,                                         /* ob_size           */
+    PyVarObject_HEAD_INIT(NULL, 0)
     "pygit2.Tag",                              /* tp_name           */
     sizeof(Tag),                               /* tp_basicsize      */
     0,                                         /* tp_itemsize       */
@@ -1652,7 +1660,7 @@ Index_dealloc(Index* self)
     if (self->own_obj)
         git_index_free(self->index);
     Py_XDECREF(self->repo);
-    self->ob_type->tp_free((PyObject*)self);
+    Py_TYPE(self)->tp_free((PyObject*)self);
 }
 
 static PyObject *
@@ -1749,7 +1757,7 @@ Index_get_position(Index *self, PyObject *value)
     else {
         PyErr_Format(PyExc_TypeError,
                      "Index entry key must be int or str, not %.200s",
-                     value->ob_type->tp_name);
+                     Py_TYPE(value)->tp_name);
         return -1;
     }
 
@@ -1905,8 +1913,7 @@ static PyMappingMethods Index_as_mapping = {
 };
 
 static PyTypeObject IndexType = {
-    PyObject_HEAD_INIT(NULL)
-    0,                                         /* ob_size           */
+    PyVarObject_HEAD_INIT(NULL, 0)
     "pygit2.Index",                            /* tp_name           */
     sizeof(Index),                             /* tp_basicsize      */
     0,                                         /* tp_itemsize       */
@@ -2001,7 +2008,7 @@ static PyTypeObject IndexIterType = {
 static void
 IndexEntry_dealloc(IndexEntry *self)
 {
-    self->ob_type->tp_free((PyObject*)self);
+    Py_TYPE(self)->tp_free((PyObject*)self);
 }
 
 static PyObject *
@@ -2030,8 +2037,7 @@ static PyGetSetDef IndexEntry_getseters[] = {
 };
 
 static PyTypeObject IndexEntryType = {
-    PyObject_HEAD_INIT(NULL)
-    0,                                         /* ob_size           */
+    PyVarObject_HEAD_INIT(NULL, 0)
     "pygit2.IndexEntry",                       /* tp_name           */
     sizeof(IndexEntry),                        /* tp_basicsize      */
     0,                                         /* tp_itemsize       */
@@ -2076,7 +2082,7 @@ Walker_dealloc(Walker *self)
 {
     git_revwalk_free(self->walk);
     Py_DECREF(self->repo);
-    self->ob_type->tp_free((PyObject*)self);
+    Py_TYPE(self)->tp_free((PyObject*)self);
 }
 
 static PyObject *
@@ -2178,8 +2184,7 @@ static PyMethodDef Walker_methods[] = {
 };
 
 static PyTypeObject WalkerType = {
-    PyObject_HEAD_INIT(NULL)
-    0,                                         /* ob_size           */
+    PyVarObject_HEAD_INIT(NULL, 0)
     "pygit2.Walker",                           /* tp_name           */
     sizeof(Walker),                            /* tp_basicsize      */
     0,                                         /* tp_itemsize       */
@@ -2198,7 +2203,7 @@ static PyTypeObject WalkerType = {
     0,                                         /* tp_getattro       */
     0,                                         /* tp_setattro       */
     0,                                         /* tp_as_buffer      */
-    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER, /* tp_flags          */
+    Py_TPFLAGS_DEFAULT,                        /* tp_flags          */
     "Revision walker",                         /* tp_doc            */
     0,                                         /* tp_traverse       */
     0,                                         /* tp_clear          */
@@ -2390,8 +2395,7 @@ static PyGetSetDef Reference_getseters[] = {
 };
 
 static PyTypeObject ReferenceType = {
-    PyObject_HEAD_INIT(NULL)
-    0,                                         /* ob_size           */
+    PyVarObject_HEAD_INIT(NULL, 0)
     "pygit2.Reference",                        /* tp_name           */
     sizeof(Reference),                         /* tp_basicsize      */
     0,                                         /* tp_itemsize       */
@@ -2466,55 +2470,50 @@ static PyMethodDef module_methods[] = {
     {NULL}
 };
 
-PyMODINIT_FUNC
-initpygit2(void)
+PyObject*
+moduleinit(PyObject* m)
 {
-    PyObject* m;
+    if (m == NULL)
+        return NULL;
 
     GitError = PyErr_NewException("pygit2.GitError", NULL, NULL);
 
     RepositoryType.tp_new = PyType_GenericNew;
     if (PyType_Ready(&RepositoryType) < 0)
-        return;
+        return NULL;
 
     /* Do not set 'tp_new' for Git objects. To create Git objects use the
      * Repository.create_XXX methods */
     if (PyType_Ready(&ObjectType) < 0)
-        return;
+        return NULL;
     CommitType.tp_base = &ObjectType;
     if (PyType_Ready(&CommitType) < 0)
-        return;
+        return NULL;
     TreeType.tp_base = &ObjectType;
     if (PyType_Ready(&TreeType) < 0)
-        return;
+        return NULL;
     BlobType.tp_base = &ObjectType;
     if (PyType_Ready(&BlobType) < 0)
-        return;
+        return NULL;
     TagType.tp_base = &ObjectType;
     if (PyType_Ready(&TagType) < 0)
-        return;
+        return NULL;
 
     TreeEntryType.tp_new = PyType_GenericNew;
     if (PyType_Ready(&TreeEntryType) < 0)
-        return;
+        return NULL;
     IndexType.tp_new = PyType_GenericNew;
     if (PyType_Ready(&IndexType) < 0)
-        return;
+        return NULL;
     IndexEntryType.tp_new = PyType_GenericNew;
     if (PyType_Ready(&IndexEntryType) < 0)
-        return;
+        return NULL;
     WalkerType.tp_new = PyType_GenericNew;
     if (PyType_Ready(&WalkerType) < 0)
-        return;
+        return NULL;
     ReferenceType.tp_new = PyType_GenericNew;
     if (PyType_Ready(&ReferenceType) < 0)
-        return;
-
-    m = Py_InitModule3("pygit2", module_methods,
-                       "Python bindings for libgit2.");
-
-    if (m == NULL)
-      return;
+        return NULL;
 
     Py_INCREF(GitError);
     PyModule_AddObject(m, "GitError", GitError);
@@ -2558,26 +2557,60 @@ initpygit2(void)
     PyModule_AddIntConstant(m, "GIT_SORT_TOPOLOGICAL", GIT_SORT_TOPOLOGICAL);
     PyModule_AddIntConstant(m, "GIT_SORT_TIME", GIT_SORT_TIME);
     PyModule_AddIntConstant(m, "GIT_SORT_REVERSE", GIT_SORT_REVERSE);
-    PyModule_AddIntConstant(m,"GIT_REF_OID", GIT_REF_OID);
-    PyModule_AddIntConstant(m,"GIT_REF_SYMBOLIC", GIT_REF_SYMBOLIC);
-    PyModule_AddIntConstant(m,"GIT_REF_PACKED", GIT_REF_PACKED);
+    PyModule_AddIntConstant(m, "GIT_REF_OID", GIT_REF_OID);
+    PyModule_AddIntConstant(m, "GIT_REF_SYMBOLIC", GIT_REF_SYMBOLIC);
+    PyModule_AddIntConstant(m, "GIT_REF_PACKED", GIT_REF_PACKED);
 
     /** Git status flags **/
-    PyModule_AddIntConstant(m,"GIT_STATUS_CURRENT", GIT_STATUS_CURRENT);
+    PyModule_AddIntConstant(m, "GIT_STATUS_CURRENT", GIT_STATUS_CURRENT);
 
     /* Flags for index status */
-    PyModule_AddIntConstant(m,"GIT_STATUS_INDEX_NEW", GIT_STATUS_INDEX_NEW);
-    PyModule_AddIntConstant(m,"GIT_STATUS_INDEX_MODIFIED",
+    PyModule_AddIntConstant(m, "GIT_STATUS_INDEX_NEW", GIT_STATUS_INDEX_NEW);
+    PyModule_AddIntConstant(m, "GIT_STATUS_INDEX_MODIFIED",
                             GIT_STATUS_INDEX_MODIFIED);
-    PyModule_AddIntConstant(m,"GIT_STATUS_INDEX_DELETED" ,
+    PyModule_AddIntConstant(m, "GIT_STATUS_INDEX_DELETED" ,
                             GIT_STATUS_INDEX_DELETED);
 
     /* Flags for worktree status */
-    PyModule_AddIntConstant(m,"GIT_STATUS_WT_NEW", GIT_STATUS_WT_NEW);
-    PyModule_AddIntConstant(m,"GIT_STATUS_WT_MODIFIED" ,
+    PyModule_AddIntConstant(m, "GIT_STATUS_WT_NEW", GIT_STATUS_WT_NEW);
+    PyModule_AddIntConstant(m, "GIT_STATUS_WT_MODIFIED" ,
                             GIT_STATUS_WT_MODIFIED);
-    PyModule_AddIntConstant(m,"GIT_STATUS_WT_DELETED", GIT_STATUS_WT_DELETED);
+    PyModule_AddIntConstant(m, "GIT_STATUS_WT_DELETED", GIT_STATUS_WT_DELETED);
 
     /* Flags for ignored files */
-    PyModule_AddIntConstant(m,"GIT_STATUS_IGNORED", GIT_STATUS_IGNORED);
+    PyModule_AddIntConstant(m, "GIT_STATUS_IGNORED", GIT_STATUS_IGNORED);
+
+    return m;
 }
+
+
+#if PY_MAJOR_VERSION < 3
+  PyMODINIT_FUNC
+  initpygit2(void)
+  {
+      PyObject* m;
+      m = Py_InitModule3("pygit2", module_methods,
+                         "Python bindings for libgit2.");
+      moduleinit(m);
+  }
+#else
+  static struct PyModuleDef moduledef = {
+      PyModuleDef_HEAD_INIT,
+      "pygit2",                        /* m_name */
+      "Python bindings for libgit2.",  /* m_doc */
+      -1,                              /* m_size */
+      module_methods,                  /* m_methods */
+      NULL,                            /* m_reload */
+      NULL,                            /* m_traverse */
+      NULL,                            /* m_clear */
+      NULL,                            /* m_free */
+  };
+
+  PyMODINIT_FUNC
+  PyInit_pygit2(void)
+  {
+      PyObject* m;
+      m = PyModule_Create(&moduledef);
+      return moduleinit(m);
+  }
+#endif
diff --git a/test/utils.py b/test/utils.py
index e973a02..ddc0ce0 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -29,6 +29,7 @@ __author__ = 'dborowitz@google.com (Dave Borowitz)'
 
 import os
 import shutil
+import sys
 import tarfile
 import tempfile
 import unittest
@@ -44,8 +45,11 @@ class BaseTestCase(unittest.TestCase):
     def assertRaisesWithArg(self, exc_class, arg, func, *args, **kwargs):
         try:
             func(*args, **kwargs)
-        except exc_class, e:
-            self.assertEqual((arg,), e.args)
+        except exc_class:
+            # XXX Use the 'exc_class as exc_value' syntax as soon as we drop
+            # support for Python 2.5
+            exc_value = sys.exc_info()[1]
+            self.assertEqual((arg,), exc_value.args)
         else:
             self.fail('%s(%r) not raised' % (exc_class.__name__, arg))