Merge branch '1.2.x' into 1.3.x
[pyke] / util.c
diff --git a/util.c b/util.c
index 2e6ee25..723c819 100644 (file)
--- a/util.c
+++ b/util.c
 
 #include "catacomb-python.h"
 
+/* #undef HAVE_LONG_LONG */
+
 /*----- External values ---------------------------------------------------*/
 
 static PyObject *modname = 0;
+PyObject *home_module = 0;
 
 /*----- Conversions -------------------------------------------------------*/
 
@@ -42,12 +45,17 @@ PyObject *getulong(unsigned long w)
     return (PyLong_FromUnsignedLong(w));
 }
 
+#ifndef HAVE_LONG_LONG
 static PyObject *i32 = 0;
 static int init_i32(void)
   { if (!i32 && (i32 = PyInt_FromLong(32)) == 0) return (-1); return (0); }
+#endif
 
 PyObject *getk64(kludge64 u)
 {
+#ifdef HAVE_LONG_LONG
+  return (PyLong_FromUnsignedLongLong(GET64(unsigned PY_LONG_LONG, u)));
+#else
   PyObject *i = 0, *j = 0, *t;
   PyObject *rc = 0;
 
@@ -63,6 +71,7 @@ end:
   if (i) Py_DECREF(i);
   if (j) Py_DECREF(j);
   return (rc);
+#endif
 }
 
 PyObject *getbool(int b)
@@ -99,15 +108,32 @@ end:
   return (0);
 }
 
+#ifdef HAVE_UINT64
+#  define CONVu64(n) do {                                              \
+     kludge64 k;                                                       \
+     uint64 t;                                                         \
+     if (!convk64(o, &k)) goto end;                                    \
+     t = GET64(uint64, k);                                             \
+     if (t > MASK##n) VALERR("out of range");                          \
+     *p = t;                                                           \
+   } while (0)
+#else
+#  define CONVu64(n) assert(!"shouldn't be possible")
+#endif
+
 #define CONVU_(n)                                                      \
   int convu##n(PyObject *o, void *pp)                                  \
   {                                                                    \
     unsigned long u;                                                   \
     uint##n *p = pp;                                                   \
                                                                        \
-    if (!convulong(o, &u)) goto end;                                   \
-    if (u > MASK##n) VALERR("out of range");                           \
-    *p = u;                                                            \
+    if (MASK##n > ULONG_MAX)                                           \
+      CONVu64(n);                                                      \
+    else {                                                             \
+      if (!convulong(o, &u)) goto end;                                 \
+      if (u > MASK##n) VALERR("out of range");                         \
+      *p = u;                                                          \
+    }                                                                  \
     return (1);                                                                \
   end:                                                                 \
     return (0);                                                                \
@@ -129,11 +155,22 @@ end:
 
 int convk64(PyObject *o, void *pp)
 {
-  PyObject *i = 0, *t;
+  PyObject *i = 0;
   int rc = 0;
+#if HAVE_LONG_LONG
+  unsigned PY_LONG_LONG t;
+#else
+  PyObject *t;
   uint32 lo, hi;
+#endif
 
   if (!o) VALERR("can't delete");
+#if HAVE_LONG_LONG
+  if ((i = PyNumber_Long(o)) == 0) goto end;
+  t = PyLong_AsUnsignedLongLong(i);
+  if (t == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred()) goto end;
+  ASSIGN64(*(kludge64 *)pp, t);
+#else
   if (init_i32()) goto end;
   if ((i = PyNumber_Int(o)) == 0) goto end;
   lo = PyInt_AsUnsignedLongMask(i);
@@ -144,7 +181,9 @@ int convk64(PyObject *o, void *pp)
   Py_DECREF(i); i = t;
   if (PyObject_IsTrue(i)) VALERR("out of range");
   SET64(*(kludge64 *)pp, hi, lo);
+#endif
   rc = 1;
+
 end:
   if (i) Py_DECREF(i);
   return (rc);
@@ -241,14 +280,14 @@ PyTypeObject *inittype(PyTypeObject *tyskel, PyTypeObject *meta)
 void setconstants(PyObject *mod, const struct nameval *c)
 {
   PyObject *x;
+  unsigned long u;
 
   while (c->name) {
-    if (c->value > LONG_MAX)
-      x = PyLong_FromUnsignedLong(c->value);
-    else
-      x = PyInt_FromLong(c->value);
-    PyModule_AddObject(mod, (/*unconst*/ char *)c->name, x);
-    c++;
+    u = c->value;
+    if (u <= LONG_MAX) x = PyInt_FromLong(u);
+    else if (c->f&CF_SIGNED) x = PyInt_FromLong(-1 - (long)(ULONG_MAX - u));
+    else x = PyLong_FromUnsignedLong(u);
+    PyModule_AddObject(mod, (/*unconst*/ char *)c->name, x); c++;
   }
 }
 
@@ -285,13 +324,7 @@ PyObject *mkexc(PyObject *mod, PyObject *base,
   PyObject *func = 0;
   PyObject *meth = 0;
 
-  if ((nameobj = PyString_FromFormat("%s.%s",
-                                    PyModule_GetName(mod),
-                                    name)) == 0 ||
-      (dict = PyDict_New()) == 0 ||
-      (exc = PyErr_NewException(PyString_AS_STRING(nameobj),
-                               base, dict)) == 0)
-    goto fail;
+  if ((dict = PyDict_New()) == 0) goto fail;
 
   if (mm) {
     while (mm->ml_name) {
@@ -305,6 +338,13 @@ PyObject *mkexc(PyObject *mod, PyObject *base,
     }
   }
 
+  if ((nameobj = PyString_FromFormat("%s.%s",
+                                    PyModule_GetName(mod),
+                                    name)) == 0 ||
+      (exc = PyErr_NewException(PyString_AS_STRING(nameobj),
+                               base, dict)) == 0)
+    goto fail;
+
 done:
   Py_XDECREF(nameobj);
   Py_XDECREF(dict);
@@ -318,6 +358,101 @@ fail:
   goto done;
 }
 
+void report_lost_exception_v(struct excinfo *exc,
+                            const char *why, va_list ap)
+{
+  PyObject *hookfn = 0;
+  PyObject *whyobj = 0;
+  PyObject *obj = 0;
+
+  /* Make sure we start out without a pending exception, or this will get
+   * really confusing.
+   */
+  assert(!PyErr_Occurred());
+
+  /* Format the explanation. */
+  if (why) whyobj = PyString_FromFormatV(why, ap);
+  else { whyobj = Py_None; Py_INCREF(whyobj); }
+
+  /* Find our home module's `lostexchook' function.  This won't work if
+   * there's no module, or the function isn't defined, or it's `None'.
+   */
+  if (!home_module) goto sys;
+  hookfn = PyObject_GetAttrString(home_module, "lostexchook");
+  if (hookfn == Py_None) goto sys;
+  else if (hookfn) ;
+  else if (!PyErr_ExceptionMatches(PyExc_AttributeError)) goto ouch;
+  else { PyErr_Clear(); goto sys; }
+
+  /* Call the hook function. */
+  obj = PyObject_CallFunction(hookfn, "(OOOO)",
+                             whyobj, exc->ty, exc->val, exc->tb);
+  if (!obj) goto ouch;
+  goto end;
+
+  /* Something went wrong reporting the problem. */
+ouch:
+  PySys_WriteStderr("\n!!! FAILURE REPORTING LOST EXCEPTION\n");
+  PyErr_Print();
+  /* drop through... */
+
+  /* There was no hook, so try to do something sensible using
+   * `sys.excepthook'.
+   */
+sys:
+  PySys_WriteStderr("\n!!! LOST EXCEPTION: %s\n",
+                   PyString_AS_STRING(whyobj));
+  RESTORE_EXCINFO(exc);
+  PyErr_Print();
+  /* drop through... */
+
+  /* Clean up afterwards. */
+end:
+  Py_XDECREF(hookfn);
+  Py_XDECREF(whyobj);
+  Py_XDECREF(obj);
+}
+
+void report_lost_exception(struct excinfo *exc, const char *why, ...)
+{
+  va_list ap;
+
+  va_start(ap, why);
+  report_lost_exception_v(exc, why, ap);
+  va_end(ap);
+}
+
+void stash_exception(struct excinfo *exc, const char *why, ...)
+{
+  va_list ap;
+  struct excinfo stash;
+
+  if (!exc->ty)
+    STASH_EXCINFO(exc);
+  else {
+    va_start(ap, why);
+    STASH_EXCINFO(&stash);
+    report_lost_exception_v(&stash, why, ap);
+    va_end(ap);
+  }
+}
+
+void restore_exception(struct excinfo *exc, const char *why, ...)
+{
+  va_list ap;
+  struct excinfo stash;
+
+  if (!PyErr_Occurred())
+    RESTORE_EXCINFO(exc);
+  else {
+    va_start(ap, why);
+    STASH_EXCINFO(&stash);
+    report_lost_exception_v(exc, why, ap);
+    RESTORE_EXCINFO(&stash);
+    va_end(ap);
+  }
+}
+
 /*----- Generic dictionary methods ----------------------------------------*/
 
 static PyTypeObject *itemiter_pytype, *valiter_pytype;
@@ -369,7 +504,7 @@ static 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@ */
@@ -427,7 +562,7 @@ static PyTypeObject valiter_pytype_skel = {
     Py_TPFLAGS_BASETYPE,
 
   /* @tp_doc@ */
-"Iterates over the items of a mapping.",
+"Iterates over the values of a mapping.",
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
@@ -466,8 +601,7 @@ PySequenceMethods gmap_pysequence = {
 Py_ssize_t gmap_pysize(PyObject *me)
 {
   PyObject *i = 0, *x = 0;
-  int rc = -1;
-  int n = 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; }
@@ -609,7 +743,7 @@ PyObject *gmapmeth_get(PyObject *me, PyObject *arg, PyObject *kw)
 {
   PyObject *k, *def = Py_None, *v;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO:get",
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:get",
                                   (/*unconst*/ char **)def_kwlist,
                                   &k, &def))
     return (0);
@@ -622,7 +756,7 @@ PyObject *gmapmeth_setdefault(PyObject *me, PyObject *arg, PyObject *kw)
 {
   PyObject *k, *def = Py_None, *v;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO:setdefault",
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:setdefault",
                                   (/*unconst*/ char **)def_kwlist,
                                   &k, &def))
     return (0);
@@ -636,16 +770,18 @@ PyObject *gmapmeth_pop(PyObject *me, PyObject *arg, PyObject *kw)
 {
   PyObject *k, *def = 0, *v;
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO:pop",
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:pop",
                                   (/*unconst*/ char **)def_kwlist,
                                   &k, &def))
     return (0);
   if ((v = PyObject_GetItem(me, k)) != 0) {
     PyObject_DelItem(me, k);
     return (v);
-  }
-  PyErr_Clear();
-  RETURN_OBJ(def);
+  } else if (def) {
+    PyErr_Clear();
+    RETURN_OBJ(def);
+  } else
+    return (0);
 }
 
 PyObject *gmapmeth_update(PyObject *me, PyObject *arg)
@@ -675,7 +811,7 @@ PyObject *gmapmeth_popitem(PyObject *me, PyObject *arg)
   PyObject *i = 0, *k = 0, *v = 0, *rc = 0;
 
   if (!PyArg_ParseTuple(arg, ":popitem") ||
-      (i = PyObject_GetIter(me)))
+      (i = PyObject_GetIter(me)) == 0)
     goto end;
   if ((k = PyIter_Next(i)) == 0) {
     if (!PyErr_Occurred()) VALERR("popitem(): mapping is empty");
@@ -697,11 +833,29 @@ PyMethodDef gmap_pymethods[] = {
 
 /*----- Initialization ----------------------------------------------------*/
 
+static PyObject *meth__set_home_module(PyObject *me, PyObject *arg)
+{
+  PyObject *mod;
+
+  if (!PyArg_ParseTuple(arg, "O!:_set_home_module", &PyModule_Type, &mod))
+    return (0);
+  Py_XDECREF(home_module); home_module = mod; Py_INCREF(home_module);
+  RETURN_NONE;
+}
+
+static const PyMethodDef methods[] = {
+#define METHNAME(func) meth_##func
+  METH (_set_home_module,      "_set_home_module(MOD)")
+#undef METHNAME
+  { 0 }
+};
+
 void util_pyinit(void)
 {
   modname = PyString_FromString("catacomb");
   INITTYPE(itemiter, root);
   INITTYPE(valiter, root);
+  addmethods(methods);
 }
 
 void util_pyinsert(PyObject *mod)