catacomb-python.h, *.c: Fix how Python `pgen' handlers handle exceptions.
[pyke] / util.c
diff --git a/util.c b/util.c
index 8a4b87e..5abd7c9 100644 (file)
--- a/util.c
+++ b/util.c
@@ -33,6 +33,7 @@
 /*----- External values ---------------------------------------------------*/
 
 static PyObject *modname = 0;
+PyObject *home_module = 0;
 
 /*----- Conversions -------------------------------------------------------*/
 
@@ -356,6 +357,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;
@@ -729,11 +825,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)