pyke/mapping.c, key.c: Make the mapping code more intrusive and complete.
[pyke] / mapping.c
index 363a2f0..4a0d412 100644 (file)
--- a/mapping.c
+++ b/mapping.c
 
 /*----- Iteration ---------------------------------------------------------*/
 
-static PyTypeObject *itemiter_pytype, *valiter_pytype;
+static PyTypeObject *keyiter_pytype, *itemiter_pytype, *valiter_pytype;
+
+union iterstate {
+  void *external;
+  void *internal[4];
+};
 
 typedef struct iter_pyobj {
   PyObject_HEAD
   PyObject *map;
-  PyObject *i;
+  union iterstate iter;
 } iter_pyobj;
 #define ITER_MAP(o) (((iter_pyobj *)(o))->map)
-#define ITER_I(o) (((iter_pyobj *)(o))->i)
+#define ITER_ITER(o) (&((iter_pyobj *)(o))->iter)
+#define ITER_EXTERNALP(o)                                              \
+  (GMAP_OPS(ITER_MAP(o))->isz > sizeof(union iterstate))
+#define ITER_I(o) (ITER_EXTERNALP(o) ? ITER_ITER(o)->external          \
+                                    : &ITER_ITER(o)->internal)
+
+static void *iter_init(PyObject *me, union iterstate *iter)
+{
+  const gmap_ops *gmops = GMAP_OPS(me);
+  void *i;
+
+  if (gmops->isz <= sizeof(*iter)) i = &iter->internal;
+  else { i = iter->external = PyObject_Malloc(gmops->isz); assert(i); }
+  gmops->iter_init(me, i);
+  return (i);
+}
+
+static void iter_free(PyObject *me, union iterstate *iter)
+  { if (GMAP_OPS(me)->isz > sizeof(*iter)) PyObject_Free(iter->external); }
 
 static void iter_pydealloc(PyObject *me)
-  { Py_DECREF(ITER_MAP(me)); Py_DECREF(ITER_I(me)); FREEOBJ(me); }
+{
+  PyObject *map = ITER_MAP(me);
+  iter_free(map, ITER_ITER(me));
+  Py_DECREF(map); FREEOBJ(me);
+}
 
-static PyObject *itemiter_pynext(PyObject *me)
+static PyObject *gmap_mkiter(PyObject *me, PyTypeObject *ty)
 {
-  PyObject *k = 0, *v = 0, *rc = 0;
+  iter_pyobj *iter = PyObject_NEW(iter_pyobj, ty);
 
-  if ((k = PyIter_Next(ITER_I(me))) != 0 &&
-      (v = PyObject_GetItem(ITER_MAP(me), k)) != 0)
-    rc = Py_BuildValue("(OO)", k, v);
-  Py_XDECREF(k); Py_XDECREF(v);
-  return (rc);
+  iter->map = me; Py_INCREF(me);
+  iter_init(me, &iter->iter);
+  return ((PyObject *)iter);
 }
 
-static const PyTypeObject itemiter_pytype_skel = {
+static PyObject *keyiter_pynext(PyObject *me)
+{
+  PyObject *map = ITER_MAP(me);
+  const struct gmap_ops *gmops = GMAP_OPS(map);
+  void *e = gmops->iter_next(map, ITER_I(me));
+
+  if (!e) return (0);
+  else return (gmops->entry_key(map, e));
+}
+
+static const PyTypeObject keyiter_pytype_skel = {
   PyObject_HEAD_INIT(0) 0,             /* Header */
-  "ItemIter",                          /* @tp_name@ */
+  "_KeyIter",                          /* @tp_name@ */
   sizeof(iter_pyobj),                  /* @tp_basicsize@ */
   0,                                   /* @tp_itemsize@ */
 
@@ -79,14 +114,14 @@ static const PyTypeObject itemiter_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-  "Iterates over the items of a mapping.",
+  "Iterates over the keys of a mapping.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
   0,                                   /* @tp_richcompare@ */
   0,                                   /* @tp_weaklistoffset@ */
   PyObject_SelfIter,                   /* @tp_iter@ */
-  itemiter_pynext,                     /* @tp_iternext@ */
+  keyiter_pynext,                      /* @tp_iternext@ */
   0,                                   /* @tp_methods@ */
   0,                                   /* @tp_members@ */
   0,                                   /* @tp_getset@ */
@@ -104,17 +139,17 @@ static const PyTypeObject itemiter_pytype_skel = {
 
 static PyObject *valiter_pynext(PyObject *me)
 {
-  PyObject *k = 0, *rc = 0;
+  PyObject *map = ITER_MAP(me);
+  const struct gmap_ops *gmops = GMAP_OPS(map);
+  void *e = gmops->iter_next(map, ITER_I(me));
 
-  if ((k = PyIter_Next(ITER_I(me))) != 0)
-    rc = PyObject_GetItem(ITER_MAP(me), k);
-  Py_XDECREF(k);
-  return (rc);
+  if (!e) return (0);
+  else return (gmops->entry_value(map, e));
 }
 
 static const PyTypeObject valiter_pytype_skel = {
   PyObject_HEAD_INIT(0) 0,             /* Header */
-  "ValueIter",                         /* @tp_name@ */
+  "_ValueIter",                                /* @tp_name@ */
   sizeof(iter_pyobj),                  /* @tp_basicsize@ */
   0,                                   /* @tp_itemsize@ */
 
@@ -160,6 +195,117 @@ static const PyTypeObject valiter_pytype_skel = {
   0                                    /* @tp_is_gc@ */
 };
 
+static PyObject *itemiter_pynext(PyObject *me)
+{
+  PyObject *map = ITER_MAP(me);
+  const struct gmap_ops *gmops = GMAP_OPS(map);
+  void *e = gmops->iter_next(map, ITER_I(me));
+  PyObject *rc = 0;
+
+  if (e)
+    rc = Py_BuildValue("(NN)",
+                      gmops->entry_key(map, e),
+                      gmops->entry_value(map, e));
+  return (rc);
+}
+
+static const PyTypeObject itemiter_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "_ItemIter",                         /* @tp_name@ */
+  sizeof(iter_pyobj),                  /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  iter_pydealloc,                      /* @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@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+  "Iterates over the items of a mapping.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  PyObject_SelfIter,                   /* @tp_iter@ */
+  itemiter_pynext,                     /* @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@ */
+};
+
+/*----- Other mapping protocol support ------------------------------------*/
+
+Py_ssize_t gmap_pysize(PyObject *me)
+{
+  const gmap_ops *gmops = GMAP_OPS(me);
+  union iterstate iter;
+  void *i;
+  Py_ssize_t n = 0;
+
+  i = iter_init(me, &iter);
+  while (gmops->iter_next(me, i)) n++;
+  iter_free(me, &iter);
+  return (n);
+}
+
+PyObject *gmap_pylookup(PyObject *me, PyObject *key)
+{
+  const gmap_ops *gmops = GMAP_OPS(me);
+  void *e = gmops->lookup(me, key, 0);
+  PyObject *rc = 0;
+
+  if (!e) { if (!PyErr_Occurred()) MAPERR(key); else goto end; }
+  rc = gmops->entry_value(me, e);
+end:
+  return (rc);
+}
+
+int gmap_pystore(PyObject *me, PyObject *key, PyObject *value)
+{
+  const gmap_ops *gmops = GMAP_OPS(me);
+  unsigned f;
+  void *e = gmops->lookup(me, key, &f);
+  int rc = -1;
+
+  if (!e) goto end;
+  if (!value)
+    rc = gmops->del_entry(me, e);
+  else {
+    rc = gmops->set_entry(me, e, value);
+    if (rc && !f) gmops->del_entry(me, e);
+  }
+  rc = 0;
+end:
+  return (rc);
+}
+
+int gmap_pyhaskey(PyObject *me, PyObject *key)
+  { return (GMAP_OPS(me)->lookup(me, key, 0) ? 1 : PyErr_Occurred() ? -1 : 0); }
+
 const PySequenceMethods gmap_pysequence = {
   0,                                   /* @sq_length@ */
   0,                                   /* @sq_concat@ */
@@ -168,140 +314,118 @@ const PySequenceMethods gmap_pysequence = {
   0,                                   /* @sq_slice@ */
   0,                                   /* @sq_ass_item@ */
   0,                                   /* @sq_ass_slice@ */
-  PyMapping_HasKey,                    /* @sq_contains@ */
+  gmap_pyhaskey,                       /* @sq_contains@ */
   0,                                   /* @sq_inplace_concat@ */
   0                                    /* @sq_inplace_repeat@ */
 };
 
-/*----- Other mapping protocol support ------------------------------------*/
-
-Py_ssize_t gmap_pysize(PyObject *me)
-{
-  PyObject *i = 0, *x = 0;
-  Py_ssize_t rc = -1, n = 0;
-
-  if ((i = PyObject_GetIter(me)) == 0) goto done;
-  while ((x = PyIter_Next(i)) != 0) { n++; Py_DECREF(x); x = 0; }
-  if (PyErr_Occurred()) goto done;
-  rc = n;
-done:
-  Py_XDECREF(i); Py_XDECREF(x);
-  return (rc);
-}
-
 PyObject *gmapmeth_has_key(PyObject *me, PyObject *arg)
 {
   PyObject *k;
+  void *e;
   if (!PyArg_ParseTuple(arg, "O:has_key", &k)) return (0);
-  return (getbool(PyMapping_HasKey(me, k)));
+  e = GMAP_OPS(me)->lookup(me, k, 0);
+  if (e) RETURN_TRUE;
+  else if (!PyErr_Occurred()) RETURN_FALSE;
+  else return (0);
 }
 
 PyObject *gmapmeth_keys(PyObject *me)
 {
-  PyObject *l = 0, *i = 0, *k, *rc = 0;
+  const gmap_ops *gmops = GMAP_OPS(me);
+  union iterstate iter; void *i = 0, *e;
+  PyObject *l = 0, *k, *rc = 0;
   int err;
 
-  if ((l = PyList_New(0)) == 0 ||
-      (i = PyObject_GetIter(me)) == 0)
-    goto done;
-  while ((k = PyIter_Next(i)) != 0)
-    { err = PyList_Append(l, k); Py_DECREF(k); if (err) goto done; }
-  if (PyErr_Occurred()) goto done;
+  if ((l = PyList_New(0)) == 0) goto done;
+  i = iter_init(me, &iter);
+  while ((e = gmops->iter_next(me, i)) != 0) {
+    k = gmops->entry_key(me, e);
+    err = PyList_Append(l, k);
+    Py_DECREF(k);
+    if (err) goto done;
+  }
   rc = l; l = 0;
 done:
-  Py_XDECREF(l); Py_XDECREF(i);
+  Py_XDECREF(l);
+  if (i) iter_free(me, &iter);
   return (rc);
 }
 
 PyObject *gmapmeth_values(PyObject *me)
 {
-  PyObject *l = 0, *i = 0, *k, *v, *rc = 0;
-  int err = 0;
-
-  if ((l = PyList_New(0)) == 0 ||
-      (i = PyObject_GetIter(me)) == 0)
-    goto done;
-  while ((k = PyIter_Next(i)) != 0) {
-    if ((v = PyObject_GetItem(me, k)) == 0 ||
-       PyList_Append(l, v))
-      err = -1;
-    Py_DECREF(k); Py_XDECREF(v);
+  const gmap_ops *gmops = GMAP_OPS(me);
+  union iterstate iter; void *i = 0, *e;
+  PyObject *l = 0, *v, *rc = 0;
+  int err;
+
+  if ((l = PyList_New(0)) == 0) goto done;
+  i = iter_init(me, &iter);
+  while ((e = gmops->iter_next(me, i)) != 0) {
+    v = gmops->entry_value(me, e);
+    err = PyList_Append(l, v);
+    Py_DECREF(v);
     if (err) goto done;
   }
-  if (PyErr_Occurred()) goto done;
   rc = l; l = 0;
 done:
-  Py_XDECREF(l); Py_XDECREF(i);
+  Py_XDECREF(l);
+  if (i) iter_free(me, &iter);
   return (rc);
 }
 
 PyObject *gmapmeth_items(PyObject *me)
 {
-  PyObject *l = 0, *i = 0, *k, *v, *z, *rc = 0;
-  int err = 0;
-
-  if ((l = PyList_New(0)) == 0 ||
-      (i = PyObject_GetIter(me)) == 0)
-    goto done;
-  while ((k = PyIter_Next(i)) != 0) {
-    z = 0;
-    if ((v = PyObject_GetItem(me, k)) == 0 ||
-       (z = Py_BuildValue("(OO)", k, v)) == 0 ||
-       PyList_Append(l, z))
-      err = -1;
-    Py_DECREF(k); Py_XDECREF(v); Py_XDECREF(z);
+  const gmap_ops *gmops = GMAP_OPS(me);
+  union iterstate iter; void *i = 0, *e;
+  PyObject *l = 0, *z, *rc = 0;
+  int err;
+
+  if ((l = PyList_New(0)) == 0) goto done;
+  i = iter_init(me, &iter);
+  while ((e = gmops->iter_next(me, i)) != 0) {
+    if ((z = Py_BuildValue("(NN)",
+                          gmops->entry_key(me, e),
+                          gmops->entry_value(me, e))) == 0)
+      goto done;
+    err = PyList_Append(l, z);
+    Py_XDECREF(z);
     if (err) goto done;
   }
-  if (PyErr_Occurred()) goto done;
   rc = l; l = 0;
 done:
-  Py_XDECREF(l); Py_XDECREF(i);
+  Py_XDECREF(l);
+  if (i) iter_free(me, &iter);
   return (rc);
 }
 
 PyObject *gmapmeth_iterkeys(PyObject *me)
-  { return (PyObject_GetIter(me)); }
+  { return (gmap_mkiter(me, keyiter_pytype)); }
 
 PyObject *gmapmeth_itervalues(PyObject *me)
-{
-  PyObject *i;
-  iter_pyobj *ii;
-
-  if ((i = PyObject_GetIter(me)) == 0)
-    return (0);
-  ii = PyObject_NEW(iter_pyobj, valiter_pytype);
-  ii->map = me; Py_INCREF(me);
-  ii->i = i;
-  return ((PyObject *)ii);
-}
+  { return (gmap_mkiter(me, valiter_pytype)); }
 
 PyObject *gmapmeth_iteritems(PyObject *me)
-{
-  PyObject *i;
-  iter_pyobj *ii;
+  { return (gmap_mkiter(me, itemiter_pytype)); }
 
-  if ((i = PyObject_GetIter(me)) == 0)
-    return (0);
-  ii = PyObject_NEW(iter_pyobj, itemiter_pytype);
-  ii->map = me; Py_INCREF(me);
-  ii->i = i;
-  return ((PyObject *)ii);
-}
+PyObject *gmap_pyiter(PyObject *me)
+  { return gmap_mkiter(me, keyiter_pytype); }
 
 PyObject *gmapmeth_clear(PyObject *me)
 {
-  PyObject *i = 0, *k = 0, *rc = 0;
-
-  if ((i = PyObject_GetIter(me)) == 0)
-    goto end;
-  while ((k = PyIter_Next(i)) != 0) {
-    PyObject_DelItem(me, k);
-    Py_DECREF(k);
+  const gmap_ops *gmops = GMAP_OPS(me);
+  union iterstate iter;
+  void *i, *e;
+  PyObject *rc = 0;
+
+  i = iter_init(me, &iter);
+  for (;;) {
+    e = gmops->iter_next(me, i); if (!e) break;
+    if (gmops->del_entry(me, e)) goto end;
   }
-  if (PyErr_Occurred()) goto end;
+  iter_free(me, &iter);
   rc = me; Py_INCREF(me);
 end:
-  Py_XDECREF(i);
   return (rc);
 }
 
@@ -310,85 +434,135 @@ static const char *const def_kwlist[] = { "key", "default", 0 };
 
 PyObject *gmapmeth_get(PyObject *me, PyObject *arg, PyObject *kw)
 {
-  PyObject *k, *def = Py_None, *v;
+  const gmap_ops *gmops = GMAP_OPS(me);
+  PyObject *k, *def = Py_None;
+  void *e;
 
   if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:get", DEF_KWLIST, &k, &def))
     return (0);
-  if ((v = PyObject_GetItem(me, k)) != 0) return (v);
-  PyErr_Clear();
-  RETURN_OBJ(def);
+  e = gmops->lookup(me, k, 0);
+  if (e) return (gmops->entry_value(me, e));
+  else if (!PyErr_Occurred()) RETURN_OBJ(def);
+  else return (0);
 }
 
 PyObject *gmapmeth_setdefault(PyObject *me, PyObject *arg, PyObject *kw)
 {
-  PyObject *k, *def = Py_None, *v;
+  const gmap_ops *gmops = GMAP_OPS(me);
+  PyObject *k, *def = Py_None;
+  void *e;
+  unsigned f;
 
   if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:setdefault", DEF_KWLIST,
                                   &k, &def))
     return (0);
-  if ((v = PyObject_GetItem(me, k)) != 0) return (v);
-  PyErr_Clear();
-  if (PyObject_SetItem(me, k, def)) return (0);
-  RETURN_OBJ(def);
+  e = gmops->lookup(me, k, &f);
+  if (!e) return (0);
+  else if (f) return (gmops->entry_value(me, e));
+  else if (gmops->set_entry(me, e, def)) return (0);
+  else RETURN_OBJ(def);
 }
 
 PyObject *gmapmeth_pop(PyObject *me, PyObject *arg, PyObject *kw)
 {
-  PyObject *k, *def = 0, *v;
+  const gmap_ops *gmops = GMAP_OPS(me);
+  PyObject *k, *def = 0;
+  PyObject *rc = 0;
+  void *e;
 
   if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:pop", DEF_KWLIST, &k, &def))
-    return (0);
-  if ((v = PyObject_GetItem(me, k)) != 0) {
-    PyObject_DelItem(me, k);
-    return (v);
-  } else if (def) {
-    PyErr_Clear();
-    RETURN_OBJ(def);
-  } else
-    return (0);
+    goto end;
+  e = gmops->lookup(me, k, 0);
+  if (!e) {
+    if (PyErr_Occurred()) goto end;
+    else if (def) { rc = def; Py_INCREF(rc); }
+    else MAPERR(k);
+  } else {
+    rc = gmops->entry_value(me, e);
+    if (gmops->del_entry(me, e)) { Py_DECREF(rc); rc = 0; }
+  }
+end:
+  return (rc);
 }
 
-PyObject *gmapmeth_update(PyObject *me, PyObject *arg)
+static int update_core(PyObject *me, PyObject *map)
 {
-  PyObject *map, *i = 0, *k, *v, *rc = 0;
-  int err = 0;
-
-  if (!PyArg_ParseTuple(arg, "O:update", &map) ||
-      (i = PyObject_GetIter(map)) == 0)
-    goto end;
-  while ((k = PyIter_Next(i)) != 0) {
-    if ((v = PyObject_GetItem(map, k)) == 0 ||
-       PyObject_SetItem(me, k, v))
-      err = -1;
-    Py_DECREF(k); Py_XDECREF(v);
-    if (err) goto end;
+  const gmap_ops *gmops = GMAP_OPS(me);
+  PyObject *i = 0, *item = 0, *k = 0, *v = 0;
+  void *e;
+  unsigned foundp;
+  int rc = -1;
+
+  i = PyObject_CallMethod(map, "iteritems", 0);
+
+  if (i) {
+    for (;;) {
+      item = PyIter_Next(i); if (!item) break;
+      if (!PyTuple_Check(item) || PyTuple_GET_SIZE(item) != 2)
+       TYERR("wanted a pair");
+      k = PyTuple_GET_ITEM(item, 0); Py_INCREF(k);
+      v = PyTuple_GET_ITEM(item, 1); Py_INCREF(v);
+      e = gmops->lookup(me, k, &foundp); if (!e) goto end;
+      if (gmops->set_entry(me, e, v)) goto end;
+      Py_DECREF(item); Py_DECREF(k); Py_DECREF(v); item = k = v = 0;
+    }
+    if (PyErr_Occurred()) goto end;
+  } else {
+    PyErr_Clear();
+    i = PyObject_GetIter(map); if (!i) goto end;
+    for (;;) {
+      k = PyIter_Next(i); if (!k) goto end;
+      v = PyObject_GetItem(map, k); if (!v) goto end;
+      e = gmops->lookup(me, k, &foundp); if (!e) goto end;
+      if (gmops->set_entry(me, e, v)) goto end;
+      Py_DECREF(k); Py_DECREF(v); k = v = 0;
+    }
+    if (PyErr_Occurred()) goto end;
   }
-  if (PyErr_Occurred()) goto end;
-  rc = me; Py_INCREF(me);
+  rc = 0;
 end:
-  Py_XDECREF(i);
+  Py_XDECREF(i); Py_XDECREF(item);
+  Py_XDECREF(k); Py_XDECREF(v);
   return (rc);
 }
 
-PyObject *gmapmeth_popitem(PyObject *me)
+PyObject *gmapmeth_update(PyObject *me, PyObject *arg, PyObject *kw)
 {
-  PyObject *i = 0, *k = 0, *v = 0, *rc = 0;
+  PyObject *map = 0;
 
-  if ((i = PyObject_GetIter(me)) == 0)
-    goto end;
-  if ((k = PyIter_Next(i)) == 0) {
-    if (!PyErr_Occurred()) VALERR("popitem(): mapping is empty");
-    goto end;
+  if (!PyArg_ParseTuple(arg, "|O:update", &map)) return (0);
+  if (map && update_core(me, map)) return (0);
+  if (kw && update_core(me, kw)) return (0);
+  RETURN_ME;
+}
+
+PyObject *gmapmeth_popitem(PyObject *me)
+{
+  const gmap_ops *gmops = GMAP_OPS(me);
+  union iterstate iter;
+  void *i;
+  PyObject *rc = 0;
+  void *e;
+
+  i = iter_init(me, &iter);
+  e = gmops->iter_next(me, i);
+  iter_free(me, &iter);
+  if (!e)
+    MAPERR(Py_None);
+  else {
+    rc = Py_BuildValue("(NN)",
+                      gmops->entry_key(me, e), gmops->entry_value(me, e));
+    if (gmops->del_entry(me, e)) { Py_DECREF(rc); rc = 0; }
   }
-  if ((v = PyObject_GetItem(me, k)) == 0 ||
-      PyObject_DelItem(me, k))
-    goto end;
-  rc = Py_BuildValue("(OO)", k, v);
 end:
-  Py_XDECREF(i); Py_XDECREF(k); Py_XDECREF(v);
   return (rc);
 }
 
+const PyMethodDef gmapro_pymethods[] = {
+  GMAP_ROMETHODS
+  { 0 }
+};
+
 const PyMethodDef gmap_pymethods[] = {
   GMAP_METHODS
   { 0 }
@@ -398,14 +572,16 @@ const PyMethodDef gmap_pymethods[] = {
 
 void pyke_gmap_pyinit(void)
 {
+  INITTYPE(keyiter, root);
   INITTYPE(itemiter, root);
   INITTYPE(valiter, root);
 }
 
 void pyke_gmap_pyinsert(PyObject *mod)
 {
-  INSERT("ItemIter", itemiter_pytype);
-  INSERT("ValueIter", valiter_pytype);
+  INSERT("_KeyIter", keyiter_pytype);
+  INSERT("_ValueIter", valiter_pytype);
+  INSERT("_ItemIter", itemiter_pytype);
 }
 
 /*----- That's all, folks -------------------------------------------------*/