Port to Python 3.
[pyke] / mapping.c
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 -------------------------------------------------*/