Port to Python 3.
authorMark Wooding <mdw@distorted.org.uk>
Tue, 22 Oct 2019 18:12:28 +0000 (19:12 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sat, 11 Apr 2020 11:44:21 +0000 (12:44 +0100)
Most of this is `#ifdef ...' ... `#endif' noise, with a few tweaks
thrown in.

Some notes on specific parts of the port.

  * buffer.c: The Python 3 buffer protocol is completely different.
    Read buffers work differently, but aren't problematic; write buffers
    can now be held open for an extended period, so we need the locking
    machinery that was added recently.

  * catacomb.c: Module initialization has changed, but isn't a great
    deal more difficult.

  * catacomb/__init__.py, catacomb/pwsafe.py, pock, pwsafe: There are a
    number of places which need language-version switches, but none of
    them is especially interesting.  This diff is noisier than it should
    be because I couldn't adjust the indentation in advance.

  * mp.c: With the abolition of a separate fixnum type, `mp_frompylong'
    needed to express the fast path from a fixnum in a different way.

  * pwsafe: The hacking to alter the error-handling behaviour associated
    with the `stdout' stream is deeply unpleasant.  Sorry.

  * pyke/pyke.h: Most of the porting work happens here, with alternative
    definitions for the various macros introduced earlier.

  * .gitignore: Ignore Python 3 `__pycache__/' turds.  Python 3 leaves
    its pre-tokenized files in `__pycache__/' directories, which somehow
    manage to be much more objectionable than the loose Python 2 `*.pyc'
    files.  Ignore these.

  * debian/: Add the necessary things to build a Python 3 extension
    package.

mapping.c
pyke-mLib.c
pyke.c
pyke.h

index 3ad1ac7..ad02d25 100644 (file)
--- a/mapping.c
+++ b/mapping.c
@@ -257,6 +257,354 @@ static const PyTypeObject itemiter_pytype_skel = {
   0                                    /* @tp_is_gc@ */
 };
 
+/*----- Mapping views -----------------------------------------------------*/
+
+#ifdef PY3
+
+static PyTypeObject *keyview_pytype, *itemview_pytype, *valview_pytype;
+
+typedef struct view_pyobj {
+  PyObject_HEAD
+  PyObject *map;
+} view_pyobj;
+#define VIEW_MAP(o) (((view_pyobj *)(o))->map)
+
+static PyObject *gmap_mkview(PyObject *me, PyTypeObject *ty)
+{
+  view_pyobj *v = PyObject_NEW(view_pyobj, ty);
+  v->map = me; Py_INCREF(me);
+  return ((PyObject *)v);
+}
+
+static void view_pydealloc(PyObject *me)
+  { Py_DECREF(VIEW_MAP(me)); FREEOBJ(me); }
+
+#define BINOP(op, update)                                              \
+  static PyObject *view_py##op(PyObject *me, PyObject *you)            \
+  {                                                                    \
+    PyObject *set = 0;                                                 \
+    PyObject *rc = 0;                                                  \
+                                                                       \
+    set = PySet_New(me); if (!set) goto end;                           \
+    if (!PyObject_CallMethod(set, #update, "(O)", you)) goto end;      \
+    rc = set; set = 0;                                                 \
+  end:                                                                 \
+    Py_XDECREF(set);                                                   \
+    return (rc);                                                       \
+  }
+BINOP(and, intersection_update)
+BINOP(or, update)
+BINOP(xor, symmetric_difference_update)
+#undef BINOP
+
+static int all_contained_p(PyObject *x, PyObject *y)
+{
+  PyObject *i = 0, *e;
+  int b, rc = -1;
+
+  i = PyObject_GetIter(x); if (!i) goto end;
+  for (;;) {
+    e = PyIter_Next(i); if (!e) break;
+    b = PySequence_Contains(y, e); Py_DECREF(e); if (b < 0) goto end;
+    if (!b) { rc = 0; goto end; }
+  }
+  if (PyErr_Occurred()) goto end;
+  rc = 1;
+end:
+  Py_XDECREF(i);
+  return (rc);
+}
+
+static Py_ssize_t view_pysize(PyObject *me)
+  { return (PyMapping_Size(VIEW_MAP(me))); }
+
+static PyObject *view_pyrichcompare(PyObject *me, PyObject *you, int op)
+{
+  PyObject *map = ITER_MAP(me);
+  Py_ssize_t mysz, yoursz;
+  int b;
+
+  mysz = PyMapping_Size(map); if (mysz < 0) return (0);
+  yoursz = PyObject_Size(you);
+  if (yoursz < 0) { PyErr_Clear(); RETURN_NOTIMPL; }
+
+  switch (op) {
+    case Py_EQ:
+      if (mysz != yoursz) RETURN_FALSE;
+      b = all_contained_p(you, me);
+      break;
+    case Py_NE:
+      if (mysz != yoursz) RETURN_TRUE;
+      b = all_contained_p(you, me);
+      break;
+    case Py_LT:
+      if (mysz >= yoursz) RETURN_FALSE;
+      b = all_contained_p(me, you);
+      break;
+    case Py_LE:
+      if (mysz > yoursz) RETURN_FALSE;
+      b = all_contained_p(me, you);
+      break;
+    case Py_GE:
+      if (mysz < yoursz) RETURN_FALSE;
+      b = all_contained_p(you, me);
+      break;
+    case Py_GT:
+      if (mysz <= yoursz) RETURN_FALSE;
+      b = all_contained_p(you, me);
+      break;
+    default:
+      abort();
+  }
+  if (b < 0) return (0);
+  return (getbool(b));
+}
+
+static PyObject *keyview_pyiter(PyObject *me)
+  { return (gmap_mkiter(VIEW_MAP(me), keyiter_pytype)); }
+
+static int keyview_pyhaskey(PyObject *me, PyObject *k)
+{
+  PyObject *map = VIEW_MAP(me);
+  const struct gmap_ops *gmops = GMAP_OPS(map);
+  return (gmops->lookup(map, k, 0) ? 1 : PyErr_Occurred() ? -1 : 0);
+}
+
+static int itemview_pyhaskey(PyObject *me, PyObject *it)
+{
+  PyObject *map = VIEW_MAP(me);
+  const struct gmap_ops *gmops = GMAP_OPS(map);
+  void *e;
+  int b;
+  PyObject *v;
+
+  if (!PyTuple_Check(it) || PyTuple_GET_SIZE(it) != 2) return (0);
+  e = gmops->lookup(map, PyTuple_GET_ITEM(it, 0), 0);
+  if (!e) return (PyErr_Occurred() ? -1 : 0);
+  v = gmops->entry_value(map, e); if (!v) return (-1);
+  b = PyObject_RichCompareBool(v, PyTuple_GET_ITEM(it, 1), Py_EQ);
+  Py_DECREF(v); return (b);
+}
+
+static PyObject *valview_pyiter(PyObject *me)
+  { return (gmap_mkiter(VIEW_MAP(me), valiter_pytype)); }
+
+static PyObject *itemview_pyiter(PyObject *me)
+  { return (gmap_mkiter(VIEW_MAP(me), itemiter_pytype)); }
+
+static const PyNumberMethods view_pynumber = {
+  0,                                   /* @nb_add@ */
+  0,                                   /* @nb_subtract@ */
+  0,                                   /* @nb_multiply@ */
+#ifdef PY2
+  0,                                   /* @nb_divide@ */
+#endif
+  0,                                   /* @nb_remainder@ */
+  0,                                   /* @nb_divmod@ */
+  0,                                   /* @nb_power@ */
+  0,                                   /* @nb_negative@ */
+  0,                                   /* @nb_positive@ */
+  0,                                   /* @nb_absolute@ */
+  0,                                   /* @nb_nonzero@ */
+  0,                                   /* @nb_invert@ */
+  0,                                   /* @nb_lshift@ */
+  0,                                   /* @nb_rshift@ */
+  view_pyand,                          /* @nb_and@ */
+  view_pyxor,                          /* @nb_xor@ */
+  view_pyor,                           /* @nb_or@ */
+  0,                                   /* @nb_coerce@ */
+  0,                                   /* @nb_int@ */
+  0,                                   /* @nb_long@ */
+  0,                                   /* @nb_float@ */
+  0,                                   /* @nb_oct@ */
+  0,                                   /* @nb_hex@ */
+};
+
+static const PySequenceMethods keyview_pysequence = {
+  view_pysize,                         /* @sq_length@ */
+  0,                                   /* @sq_concat@ */
+  0,                                   /* @sq_repeat@ */
+  0,                                   /* @sq_item@ */
+  0,                                   /* @sq_slice@ */
+  0,                                   /* @sq_ass_item@ */
+  0,                                   /* @sq_ass_slice@ */
+  keyview_pyhaskey,                    /* @sq_contains@ */
+  0,                                   /* @sq_inplace_concat@ */
+  0,                                   /* @sq_inplace_repeat@ */
+};
+
+static const PyTypeObject keyview_pytype_skel = {
+  PyVarObject_HEAD_INIT(0, 0)          /* Header */
+  "_KeyView",                          /* @tp_name@ */
+  sizeof(view_pyobj),                  /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  view_pydealloc,                      /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  PYNUMBER(view),                      /* @tp_as_number@ */
+  PYSEQUENCE(keyview),                 /* @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@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+  "View of a mapping's keys.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  view_pyrichcompare,                  /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  keyview_pyiter,                      /* @tp_iter@ */
+  0,                                   /* @tp_iternext@ */
+  0,                                   /* @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@ */
+  0,                                   /* @tp_init@ */
+  PyType_GenericAlloc,                 /* @tp_alloc@ */
+  abstract_pynew,                      /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+static const PySequenceMethods valview_pysequence = {
+  view_pysize,                         /* @sq_length@ */
+  0,                                   /* @sq_concat@ */
+  0,                                   /* @sq_repeat@ */
+  0,                                   /* @sq_item@ */
+  0,                                   /* @sq_slice@ */
+  0,                                   /* @sq_ass_item@ */
+  0,                                   /* @sq_ass_slice@ */
+  0,                                   /* @sq_contains@ */
+  0,                                   /* @sq_inplace_concat@ */
+  0,                                   /* @sq_inplace_repeat@ */
+};
+
+static const PyTypeObject valview_pytype_skel = {
+  PyVarObject_HEAD_INIT(0, 0)          /* Header */
+  "_ValueView",                                /* @tp_name@ */
+  sizeof(view_pyobj),                  /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  view_pydealloc,                      /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  PYNUMBER(view),                      /* @tp_as_number@ */
+  PYSEQUENCE(valview),                 /* @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@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+  "View of a mapping's values.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  valview_pyiter,                      /* @tp_iter@ */
+  0,                                   /* @tp_iternext@ */
+  0,                                   /* @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@ */
+  0,                                   /* @tp_init@ */
+  PyType_GenericAlloc,                 /* @tp_alloc@ */
+  abstract_pynew,                      /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+static const PySequenceMethods itemview_pysequence = {
+  view_pysize,                         /* @sq_length@ */
+  0,                                   /* @sq_concat@ */
+  0,                                   /* @sq_repeat@ */
+  0,                                   /* @sq_item@ */
+  0,                                   /* @sq_slice@ */
+  0,                                   /* @sq_ass_item@ */
+  0,                                   /* @sq_ass_slice@ */
+  itemview_pyhaskey,                   /* @sq_contains@ */
+  0,                                   /* @sq_inplace_concat@ */
+  0,                                   /* @sq_inplace_repeat@ */
+};
+
+static const PyTypeObject itemview_pytype_skel = {
+  PyVarObject_HEAD_INIT(0, 0)          /* Header */
+  "_ItemView",                         /* @tp_name@ */
+  sizeof(view_pyobj),                  /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  view_pydealloc,                      /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  PYNUMBER(view),                      /* @tp_as_number@ */
+  PYSEQUENCE(itemview),                        /* @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@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+  "View of a mapping's key/value items.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  view_pyrichcompare,                  /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  itemview_pyiter,                     /* @tp_iter@ */
+  0,                                   /* @tp_iternext@ */
+  0,                                   /* @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@ */
+  0,                                   /* @tp_init@ */
+  PyType_GenericAlloc,                 /* @tp_alloc@ */
+  abstract_pynew,                      /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+#endif
+
 /*----- Other mapping protocol support ------------------------------------*/
 
 Py_ssize_t gmap_pysize(PyObject *me)
@@ -319,6 +667,19 @@ const PySequenceMethods gmap_pysequence = {
   0                                    /* @sq_inplace_repeat@ */
 };
 
+#ifdef PY3
+
+PyObject *gmapmeth_keys(PyObject *me)
+  { return (gmap_mkview(me, keyview_pytype)); }
+
+PyObject *gmapmeth_values(PyObject *me)
+  { return (gmap_mkview(me, valview_pytype)); }
+
+PyObject *gmapmeth_items(PyObject *me)
+  { return (gmap_mkview(me, itemview_pytype)); }
+
+#else
+
 PyObject *gmapmeth_has_key(PyObject *me, PyObject *arg)
 {
   PyObject *k;
@@ -408,6 +769,8 @@ PyObject *gmapmeth_itervalues(PyObject *me)
 PyObject *gmapmeth_iteritems(PyObject *me)
   { return (gmap_mkiter(me, itemiter_pytype)); }
 
+#endif
+
 PyObject *gmap_pyiter(PyObject *me)
   { return gmap_mkiter(me, keyiter_pytype); }
 
@@ -493,8 +856,12 @@ static int update_core(PyObject *me, PyObject *map)
   unsigned foundp;
   int rc = -1;
 
-  v = PyObject_CallMethod(map, "iteritems", 0);
+  v = PyObject_CallMethod(map, PY23("iteritems", "items"), 0);
+#ifdef PY3
+  if (v) { i = PyObject_GetIter(v); Py_DECREF(v); v = 0; }
+#else
   i = v; v = 0;
+#endif
 
   if (i) {
     for (;;) {
@@ -576,6 +943,11 @@ void pyke_gmap_pyinit(void)
   INITTYPE(keyiter, root);
   INITTYPE(itemiter, root);
   INITTYPE(valiter, root);
+#ifdef PY3
+  INITTYPE(keyview, root);
+  INITTYPE(valview, root);
+  INITTYPE(itemview, root);
+#endif
 }
 
 void pyke_gmap_pyinsert(PyObject *mod)
@@ -583,6 +955,11 @@ void pyke_gmap_pyinsert(PyObject *mod)
   INSERT("_KeyIter", keyiter_pytype);
   INSERT("_ValueIter", valiter_pytype);
   INSERT("_ItemIter", itemiter_pytype);
+#ifdef PY3
+  INSERT("_KeyView", keyview_pytype);
+  INSERT("_ValueView", valview_pytype);
+  INSERT("_ItemView", itemview_pytype);
+#endif
 }
 
 /*----- That's all, folks -------------------------------------------------*/
index fd6f895..30f6735 100644 (file)
@@ -52,8 +52,10 @@ PyObject *getk64(kludge64 u)
   Py_DECREF(i); i = t;
   if ((j = PyLong_FromUnsignedLong(LO64(u))) == 0) goto end;
   if ((t = PyNumber_InPlaceOr(i, j)) == 0) goto end;
+#  ifdef PY2
   Py_DECREF(i); i = t;
   if ((t = PyNumber_Int(i)) == 0) goto end;
+#  endif
   rc = t;
 end:
   Py_XDECREF(i);
diff --git a/pyke.c b/pyke.c
index 03f1c1f..330f15c 100644 (file)
--- a/pyke.c
+++ b/pyke.c
@@ -50,11 +50,13 @@ int convulong(PyObject *o, void *pp)
   PyObject *t;
 
   if (!o) VALERR("can't delete");
+#ifdef PY2
   if (PyInt_Check(o)) {
     long i = PyInt_AS_LONG(o);
     if (i < 0) VALERR("must be nonnegative");
     *p = i;
   } else
+#endif
   {
     if ((t = PyNumber_Long(o)) == 0) goto end;
     *p = PyLong_AsUnsignedLong(t);
@@ -110,6 +112,7 @@ int convbin(PyObject *o, void *pp)
     r->sz = BIN_LEN(o);
     return (1);
   }
+#ifdef PY2
   if (PyUnicode_Check(o)) {
     o = _PyUnicode_AsDefaultEncodedString(o, 0);
     if (!o) return (0);
@@ -117,6 +120,7 @@ int convbin(PyObject *o, void *pp)
     r->sz = PyString_GET_SIZE(o);
     return (1);
   }
+#endif
   return (PyObject_AsReadBuffer(o, &r->p, &r->sz) ? 0 : 1);
 }
 
@@ -274,6 +278,9 @@ void *newtype(PyTypeObject *metaty,
   if (ty->ht_name)
     ty->ht_type.tp_name = TEXT_STR(ty->ht_name);
   ty->ht_slots = 0;
+#ifdef PY3
+  ty->ht_qualname = 0;
+#endif
   (void)PyObject_INIT(&ty->ht_type, metaty);
   Py_INCREF(metaty);
   return (ty);
@@ -281,6 +288,10 @@ void *newtype(PyTypeObject *metaty,
 
 void typeready(PyTypeObject *ty)
 {
+#ifdef PY3
+  PyHeapTypeObject *hty = (PyHeapTypeObject *)ty;
+  hty->ht_qualname = hty->ht_name;
+#endif
   PyType_Ready(ty);
   PyDict_SetItemString(ty->tp_dict, "__module__", modname);
 }
@@ -312,7 +323,8 @@ PyObject *mkexc(PyObject *mod, PyObject *base,
     while (mm->ml_name) {
       if ((func = PyCFunction_NewEx((/*unconst*/ PyMethodDef *)mm,
                                    0, mod)) == 0 ||
-         (meth = PyMethod_New(func, 0, exc)) == 0 ||
+         (meth = PY23(PyMethod_New(func, 0, exc),
+                      PyInstanceMethod_New(func))) == 0 ||
          PyDict_SetItemString(dict, mm->ml_name, meth))
        goto fail;
       Py_DECREF(func); func = 0;
diff --git a/pyke.h b/pyke.h
index 06c30c2..a60fd76 100644 (file)
--- a/pyke.h
+++ b/pyke.h
@@ -64,6 +64,15 @@ PRIVATE_SYMBOLS;
 
 /*----- Python version compatibility hacks --------------------------------*/
 
+/* Explicit version switching. */
+#if PY_VERSION_HEX >= 0x03000000
+#  define PY3 1
+#  define PY23(two, three) three
+#else
+#  define PY2 1
+#  define PY23(two, three) two
+#endif
+
 /* The handy `Py_TYPE' and `Py_SIZE' macros turned up in 2.6.  Define them if
  * they're not already here.
  */
@@ -82,6 +91,18 @@ PRIVATE_SYMBOLS;
 #  define PyVarObject_HEAD_INIT(super, sz) PyObject_HEAD_INIT(super) sz,
 #endif
 
+/* Python 3 doesn't have `int', only `long', even though it's called `int' at
+ * the Python level.  Provide some obvious macros to fill in the gaps.
+ */
+#ifdef PY3
+#  define PyInt_Check PyLong_Check
+#  define PyInt_FromLong PyLong_FromLong
+#  define PyInt_AS_LONG PyLong_AS_LONG
+#  define PyInt_AsLong PyLong_AsLong
+#  define PyInt_AsUnsignedLongMask PyLong_AsUnsignedLongMask
+#  define PyNumber_Int PyNumber_Long
+#endif
+
 /* Python 3.2 changed the type of hash values, so paper over this annoying
  * difference.
  */
@@ -89,6 +110,13 @@ PRIVATE_SYMBOLS;
   typedef long Py_hash_t;
 #endif
 
+/* Python 3 always has the `CHECKTYPES' behaviour, and doesn't define the
+ * flag.
+ */
+#ifdef PY3
+#  define Py_TPFLAGS_CHECKTYPES 0
+#endif
+
 /* Plain octet strings.  Python 2 calls these `str', while Python 3 calls
  * them `bytes'.  We call them `bin' here, and define the following.
  *
@@ -115,6 +143,25 @@ PRIVATE_SYMBOLS;
  #   * `YN' is a format character for `PyArg_ParseTuple...' for retrieving an
  *     octet string and length from any sort-of vaguely binary-ish object.
  */
+#ifdef PY3
+#  define BINOBJ PyBytesObject
+#  define BIN_TYPE PyBytes_Type
+#  define BIN_CHECK(obj) PyBytes_Check(obj)
+#  define BIN_PTR(obj) PyBytes_AS_STRING(obj)
+#  define BIN_LEN(obj) PyBytes_GET_SIZE(obj)
+#  define BIN_FROMSTR(str) PyBytes_FromString(str)
+#  define BIN_FROMSTRLEN(str, len) PyBytes_FromStringAndSize(str, len)
+#  define BIN_FORMAT PyBytes_FromFormat
+#  define BIN_VFORMAT PyBytes_FromFormatV
+#  define BIN_PREPAREWRITE(obj, ptr, sz) do {                          \
+     (obj) = PyBytes_FromStringAndSize(0, (sz));                       \
+     (ptr) = PyBytes_AS_STRING(obj);                                   \
+   } while (0)
+#  define BIN_DONEWRITE(obj, sz) do Py_SIZE(obj) = (sz); while (0)
+#  define BIN_SETLEN(obj, len) do Py_SIZE(obj) = (len); while (0)
+#  define Y "y"
+#  define YN "y#"
+#else
 #  define BINOBJ PyStringObject
 #  define BIN_TYPE PyString_Type
 #  define BIN_CHECK(obj) PyString_Check(obj)
@@ -132,6 +179,7 @@ PRIVATE_SYMBOLS;
 #  define BIN_SETLEN(obj, len) do Py_SIZE(obj) = (len); while (0)
 #  define Y "s"
 #  define YN "s#"
+#endif
 
 /* Text strings.  Both Python 2 and Python 3 call these `str', but they're
  * very different because a Python 3 `str' is Unicode inside.  When dealing
@@ -161,6 +209,52 @@ PRIVATE_SYMBOLS;
  *
  * (Use `s' and `s#' in `PyArg_ParseTuple...'.)
  */
+#ifdef PY3
+#  define TEXTOBJ PyUnicodeObject
+#  define TEXT_TYPE PyUnicode_Type
+#  define TEXT_CHECK(obj) PyUnicode_Check(obj)
+#  if PY_VERSION_HEX >= 0x03030000
+#    define TEXT_PTR(obj) PyUnicode_AsUTF8(obj)
+#    define TEXT_STR(obj) PyUnicode_AsUTF8(obj)
+#    define TEXT_PTRLEN(obj, ptr, len) do {                            \
+       Py_ssize_t len_;                                                        \
+       (ptr) = PyUnicode_AsUTF8AndSize((obj), &len_);                  \
+       (len) = len_;                                                   \
+     } while (0)
+#    define TEXT_PREPAREWRITE(obj, ptr, sz) do {                       \
+       (obj) = PyUnicode_New((sz), 127);                               \
+       (ptr) = PyUnicode_DATA(obj);                                    \
+     } while (0)
+#    define TEXT_DONEWRITE(obj, len) do {                              \
+       size_t len_ = (len);                                            \
+       assert(PyUnicode_IS_COMPACT_ASCII(obj));                                \
+       ((char *)PyUnicode_DATA(obj))[len_] = 0;                                \
+       ((PyASCIIObject *)(obj))->length = len_;                                \
+     } while (0)
+#  else
+#    define TEXT_PTR(obj) _PyUnicode_AsString(obj)
+#    define TEXT_STR(obj) _PyUnicode_AsString(obj)
+#    define TEXT_PTRLEN(obj, ptr, len) do {                            \
+       Py_ssize_t len_;                                                        \
+       (ptr) = _PyUnicode_AsStringAndSize((obj), &len_);               \
+       (len) = len_;                                                   \
+     } while (0)
+#    define TEXT_PREPAREWRITE(obj, ptr, sz) do {                       \
+       (obj) = PyBytes_FromStringAndSize(0, (sz));                     \
+       (ptr) = PyBytes_AS_STRING(obj);                                 \
+     } while (0)
+#    define TEXT_DONEWRITE(obj, len) do {                              \
+       PyObject *new_;                                                 \
+       Py_SIZE(obj) = (len);                                           \
+       new_ = PyUnicode_FromEncodedObject(obj, 0, 0);                  \
+       assert(new_); Py_DECREF(obj); (obj) = new_;                     \
+     } while (0)
+#  endif
+#  define TEXT_FORMAT PyUnicode_FromFormat
+#  define TEXT_VFORMAT PyUnicode_FromFormatV
+#  define TEXT_FROMSTR(str) PyUnicode_FromString(str)
+#  define TEXT_FROMSTRLEN(str, len) PyUnicode_FromStringAndSize(str, len)
+#else
 #  define TEXTOBJ PyStringObject
 #  define TEXT_TYPE PyString_Type
 #  define TEXT_CHECK(obj) PyString_Check(obj)
@@ -179,6 +273,7 @@ PRIVATE_SYMBOLS;
 #  define TEXT_DONEWRITE(obj, sz) do { Py_SIZE(obj) = (sz); } while (0)
 #  define TEXT_FROMSTR(str) PyString_FromString(str)
 #  define TEXT_FROMSTRLEN(str, len) PyString_FromStringAndSize(str, len)
+#endif
 
 /*----- Utilities for returning values and exceptions ---------------------*/
 
@@ -556,6 +651,13 @@ typedef struct gmap_pyobj {
 #define GMAP_NAMETHDECL(func, doc)                                     \
   extern PyObject *gmapmeth_##func(PyObject *);
 
+#ifdef PY3
+#  define GMAP_DOROMETHODS(METH, KWMETH, NAMETH)                       \
+    NAMETH(keys,       "D.keys() -> LIST")                             \
+    NAMETH(values,     "D.values() -> LIST")                           \
+    NAMETH(items,      "D.items() -> LIST")                            \
+    KWMETH(get,                "D.get(KEY, [default = None]) -> VALUE")
+#else
 #  define GMAP_DOROMETHODS(METH, KWMETH, NAMETH)                       \
     METH  (has_key,    "D.has_key(KEY) -> BOOL")                       \
     NAMETH(keys,       "D.keys() -> LIST")                             \
@@ -565,6 +667,7 @@ typedef struct gmap_pyobj {
     NAMETH(itervalues, "D.itervalues() -> ITER")                       \
     NAMETH(iteritems,  "D.iteritems() -> ITER")                        \
     KWMETH(get,                "D.get(KEY, [default = None]) -> VALUE")
+#endif
 
 #define GMAP_DOMETHODS(METH, KWMETH, NAMETH)                           \
   GMAP_DOROMETHODS(METH, KWMETH, NAMETH)                               \