Blame SOURCES/0004-Add-new-functions-to-extend-exception-handling.patch

b10e8e
From e701c277cdb07d849a9f7b67aabfc4ff391f8970 Mon Sep 17 00:00:00 2001
b10e8e
From: Chris Lumens <clumens@redhat.com>
b10e8e
Date: Tue, 13 Jan 2015 11:13:17 -0500
b10e8e
Subject: [PATCH 4/7] Add new functions to extend exception handling.
b10e8e
b10e8e
We've been avoiding this for an awful long time, but there is now a need for
b10e8e
users of pyparted to be able to specify what answers should be given to parted
b10e8e
exceptions that ask a question.  Specifically, we need to be able to ask the
b10e8e
user whether to proceed with a disk containing a corrupted GPT disk label or not.
b10e8e
b10e8e
This adds a function to register a callback that can prompt the user (among other
b10e8e
possible actions) and a function to clear this callback to restore default
b10e8e
behavior.
b10e8e
---
b10e8e
 src/_pedmodule.c | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
b10e8e
 1 file changed, 125 insertions(+), 10 deletions(-)
b10e8e
b10e8e
diff --git a/src/_pedmodule.c b/src/_pedmodule.c
b10e8e
index a1c8947..7f02193 100644
b10e8e
--- a/src/_pedmodule.c
b10e8e
+++ b/src/_pedmodule.c
b10e8e
@@ -5,7 +5,7 @@
b10e8e
  * Python module that implements the libparted functionality via Python
b10e8e
  * classes and other high level language features.
b10e8e
  *
b10e8e
- * Copyright (C) 2007, 2008 Red Hat, Inc.
b10e8e
+ * Copyright (C) 2007-2015 Red Hat, Inc.
b10e8e
  *
b10e8e
  * This copyrighted material is made available to anyone wishing to use,
b10e8e
  * modify, copy, or redistribute it subject to the terms and conditions of
b10e8e
@@ -44,6 +44,8 @@
b10e8e
 char *partedExnMessage = NULL;
b10e8e
 unsigned int partedExnRaised = 0;
b10e8e
 
b10e8e
+PyObject *exn_handler = NULL;
b10e8e
+
b10e8e
 /* Docs strings are broken out of the module structure here to be at least a
b10e8e
  * little bit readable.
b10e8e
  */
b10e8e
@@ -190,6 +192,25 @@ PyDoc_STRVAR(unit_get_by_name_doc,
b10e8e
 "Returns a Unit given its textual representation.  Returns one of the\n"
b10e8e
 "UNIT_* constants.");
b10e8e
 
b10e8e
+PyDoc_STRVAR(register_exn_handler_doc,
b10e8e
+"register_exn_handler(function)\n\n"
b10e8e
+"When parted raises an exception, the function registered here will be called\n"
b10e8e
+"to help determine what action to take.  This does not bypass the exception\n"
b10e8e
+"handler pyparted uses.  Instead, it can be used to pop up a dialog to ask the\n"
b10e8e
+"user which course of action to take, or to provide a different default answer,\n"
b10e8e
+"or other actions.\n\n"
b10e8e
+"The given function must accept as arguments:  (1) an integer corresponding to\n"
b10e8e
+"one of the EXCEPTION_TYPE_* constants; (2) an integer corresponding to one of the\n"
b10e8e
+"EXCEPTION_OPT_* constants; and (3) a string that is the problem encountered by\n"
b10e8e
+"parted.  This string will already be translated.  The given function must return\n"
b10e8e
+"one of the EXCEPTION_RESOLVE_* constants instructing parted how to proceed.");
b10e8e
+
b10e8e
+PyDoc_STRVAR(clear_exn_handler_doc,
b10e8e
+"clear_exn_handler()\n\n"
b10e8e
+"Clear any previously added exception handling function.  This means the\n"
b10e8e
+"default behavior for all parted exceptions will be used, so only safe\n"
b10e8e
+"answers to any questions parted asks will be automatically provided.");
b10e8e
+
b10e8e
 PyDoc_STRVAR(_ped_doc,
b10e8e
 "This module implements an interface to libparted.\n\n"
b10e8e
 "pyparted provides two API layers:  a lower level that exposes the complete\n"
b10e8e
@@ -204,6 +225,25 @@ PyDoc_STRVAR(_ped_doc,
b10e8e
 "For complete documentation, refer to the docs strings for each _ped\n"
b10e8e
 "method, exception class, and subclass.");
b10e8e
 
b10e8e
+PyObject *py_ped_register_exn_handler(PyObject *s, PyObject *args) {
b10e8e
+    PyObject *fn = NULL;
b10e8e
+
b10e8e
+    if (!PyArg_ParseTuple(args, "O", &fn)) {
b10e8e
+        return NULL;
b10e8e
+    }
b10e8e
+
b10e8e
+    Py_DECREF(exn_handler);
b10e8e
+    exn_handler = fn;
b10e8e
+
b10e8e
+    Py_RETURN_TRUE;
b10e8e
+}
b10e8e
+
b10e8e
+PyObject *py_ped_clear_exn_handler(PyObject *s, PyObject *args) {
b10e8e
+    exn_handler = Py_None;
b10e8e
+    Py_INCREF(exn_handler);
b10e8e
+    Py_RETURN_TRUE;
b10e8e
+}
b10e8e
+
b10e8e
 /* all of the methods for the _ped module */
b10e8e
 static struct PyMethodDef PyPedModuleMethods[] = {
b10e8e
     {"libparted_version", (PyCFunction) py_libparted_get_version, METH_VARARGS,
b10e8e
@@ -211,6 +251,11 @@ static struct PyMethodDef PyPedModuleMethods[] = {
b10e8e
     {"pyparted_version", (PyCFunction) py_pyparted_version, METH_VARARGS,
b10e8e
                          pyparted_version_doc},
b10e8e
 
b10e8e
+    {"register_exn_handler", (PyCFunction) py_ped_register_exn_handler, METH_VARARGS,
b10e8e
+                             register_exn_handler_doc},
b10e8e
+    {"clear_exn_handler", (PyCFunction) py_ped_clear_exn_handler, METH_VARARGS,
b10e8e
+                          clear_exn_handler_doc},
b10e8e
+
b10e8e
     /* pyconstraint.c */
b10e8e
     {"constraint_new_from_min_max", (PyCFunction) py_ped_constraint_new_from_min_max,
b10e8e
                                     METH_VARARGS, constraint_new_from_min_max_doc},
b10e8e
@@ -337,6 +382,10 @@ PyObject *py_pyparted_version(PyObject *s, PyObject *args) {
b10e8e
  * main motivation for this function is that methods in our parted module need
b10e8e
  * to be able to raise specific, helpful exceptions instead of something
b10e8e
  * generic.
b10e8e
+ *
b10e8e
+ * It is also possible for callers to specify a function to help in deciding
b10e8e
+ * what to do with parted exceptions.  See the docs for the
b10e8e
+ * py_ped_register_exn_handler function.
b10e8e
  */
b10e8e
 static PedExceptionOption partedExnHandler(PedException *e) {
b10e8e
     switch (e->type) {
b10e8e
@@ -350,13 +399,29 @@ static PedExceptionOption partedExnHandler(PedException *e) {
b10e8e
 
b10e8e
                 if (partedExnMessage == NULL)
b10e8e
                     PyErr_NoMemory();
b10e8e
-
b10e8e
-                /*
b10e8e
-                 * return 'no' for yes/no question exceptions in libparted,
b10e8e
-                 * prevent any potential disk destruction and pass up an
b10e8e
-                 * exception to our caller
b10e8e
-                 */
b10e8e
-                return PED_EXCEPTION_NO;
b10e8e
+                else if (exn_handler && PyCallable_Check(exn_handler)) {
b10e8e
+                    PyObject *args, *retval;
b10e8e
+
b10e8e
+                    args = PyTuple_New(3);
b10e8e
+                    PyTuple_SetItem(args, 0, PyInt_FromLong(e->type));
b10e8e
+                    PyTuple_SetItem(args, 1, PyInt_FromLong(e->options));
b10e8e
+                    PyTuple_SetItem(args, 2, PyUnicode_FromString(e->message));
b10e8e
+
b10e8e
+                    retval = PyObject_CallObject(exn_handler, NULL);
b10e8e
+                    Py_DECREF(args);
b10e8e
+                    if (retval != NULL && (PyLong_AsLong(retval) == PED_EXCEPTION_UNHANDLED || (PyLong_AsLong(retval) & e->options) > 0))
b10e8e
+                        return PyLong_AsLong(retval);
b10e8e
+                    else
b10e8e
+                        /* Call failed, use the default value. */
b10e8e
+                        return PED_EXCEPTION_NO;
b10e8e
+                }
b10e8e
+                else {
b10e8e
+                    /* If no exception handling function was registered to
b10e8e
+                     * tell us what to do, return "no" for any yes/no
b10e8e
+                     * questions to prevent any potential disk destruction.
b10e8e
+                     */
b10e8e
+                    return PED_EXCEPTION_NO;
b10e8e
+                }
b10e8e
             } else {
b10e8e
                 partedExnRaised = 0;
b10e8e
                 return PED_EXCEPTION_IGNORE;
b10e8e
@@ -372,8 +437,29 @@ static PedExceptionOption partedExnHandler(PedException *e) {
b10e8e
 
b10e8e
             if (partedExnMessage == NULL)
b10e8e
                 PyErr_NoMemory();
b10e8e
-
b10e8e
-            return PED_EXCEPTION_CANCEL;
b10e8e
+            else if (exn_handler && PyCallable_Check(exn_handler)) {
b10e8e
+                PyObject *args, *retval;
b10e8e
+
b10e8e
+                args = PyTuple_New(3);
b10e8e
+                PyTuple_SetItem(args, 0, PyInt_FromLong(e->type));
b10e8e
+                PyTuple_SetItem(args, 1, PyInt_FromLong(e->options));
b10e8e
+                PyTuple_SetItem(args, 2, PyUnicode_FromString(e->message));
b10e8e
+
b10e8e
+                retval = PyObject_CallObject(exn_handler, NULL);
b10e8e
+                Py_DECREF(args);
b10e8e
+                if (retval != NULL && (PyLong_AsLong(retval) == PED_EXCEPTION_UNHANDLED || (PyLong_AsLong(retval) & e->options) > 0))
b10e8e
+                    return PyLong_AsLong(retval);
b10e8e
+                else
b10e8e
+                    /* Call failed, use the default value. */
b10e8e
+                    return PED_EXCEPTION_CANCEL;
b10e8e
+            }
b10e8e
+            else {
b10e8e
+                /* If no exception handling function was registered to tell us
b10e8e
+                 * what to do, return "cancel" for any questions to prevent
b10e8e
+                 * any potential disk destruction.
b10e8e
+                 */
b10e8e
+                return PED_EXCEPTION_CANCEL;
b10e8e
+            }
b10e8e
 
b10e8e
         /* Raise exceptions for internal parted bugs immediately. */
b10e8e
         case PED_EXCEPTION_BUG:
b10e8e
@@ -607,6 +693,35 @@ PyMODINIT_FUNC init_ped(void) {
b10e8e
     Py_INCREF(UnknownTypeException);
b10e8e
     PyModule_AddObject(m, "UnknownTypeException", UnknownTypeException);
b10e8e
 
b10e8e
+    /* Exception type constants. */
b10e8e
+    PyModule_AddIntConstant(m, "EXCEPTION_TYPE_INFORMATION", PED_EXCEPTION_INFORMATION);
b10e8e
+    PyModule_AddIntConstant(m, "EXCEPTION_TYPE_WARNING", PED_EXCEPTION_WARNING);
b10e8e
+    PyModule_AddIntConstant(m, "EXCEPTION_TYPE_ERROR", PED_EXCEPTION_ERROR);
b10e8e
+    PyModule_AddIntConstant(m, "EXCEPTION_TYPE_FATAL", PED_EXCEPTION_FATAL);
b10e8e
+    PyModule_AddIntConstant(m, "EXCEPTION_TYPE_BUG", PED_EXCEPTION_BUG);
b10e8e
+    PyModule_AddIntConstant(m, "EXCEPTION_TYPE_NO_FEATURE", PED_EXCEPTION_NO_FEATURE);
b10e8e
+
b10e8e
+    /* Exception resolution constants. */
b10e8e
+    PyModule_AddIntConstant(m, "EXCEPTION_RESOLVE_UNHANDLED", PED_EXCEPTION_UNHANDLED);
b10e8e
+    PyModule_AddIntConstant(m, "EXCEPTION_RESOLVE_FIX", PED_EXCEPTION_FIX);
b10e8e
+    PyModule_AddIntConstant(m, "EXCEPTION_RESOLVE_YES", PED_EXCEPTION_YES);
b10e8e
+    PyModule_AddIntConstant(m, "EXCEPTION_RESOLVE_NO", PED_EXCEPTION_NO);
b10e8e
+    PyModule_AddIntConstant(m, "EXCEPTION_RESOLVE_OK", PED_EXCEPTION_OK);
b10e8e
+    PyModule_AddIntConstant(m, "EXCEPTION_RESOLVE_RETRY", PED_EXCEPTION_RETRY);
b10e8e
+    PyModule_AddIntConstant(m, "EXCEPTION_RESOLVE_IGNORE", PED_EXCEPTION_IGNORE);
b10e8e
+    PyModule_AddIntConstant(m, "EXCEPTION_RESOLVE_CANCEL", PED_EXCEPTION_CANCEL);
b10e8e
+
b10e8e
+    /* Exception option constants. */
b10e8e
+    PyModule_AddIntConstant(m, "EXCEPTION_OPT_OK_CANCEL", PED_EXCEPTION_OK_CANCEL);
b10e8e
+    PyModule_AddIntConstant(m, "EXCEPTION_OPT_YES_NO", PED_EXCEPTION_YES_NO);
b10e8e
+    PyModule_AddIntConstant(m, "EXCEPTION_OPT_YES_NO_CANCEL", PED_EXCEPTION_YES_NO_CANCEL);
b10e8e
+    PyModule_AddIntConstant(m, "EXCEPTION_OPT_IGNORE_CANCEL", PED_EXCEPTION_IGNORE_CANCEL);
b10e8e
+    PyModule_AddIntConstant(m, "EXCEPTION_OPT_RETRY_CANCEL", PED_EXCEPTION_RETRY_CANCEL);
b10e8e
+    PyModule_AddIntConstant(m, "EXCEPTION_OPT_RETRY_IGNORE_CANCEL", PED_EXCEPTION_RETRY_IGNORE_CANCEL);
b10e8e
+
b10e8e
+    exn_handler = Py_None;
b10e8e
+    Py_INCREF(exn_handler);
b10e8e
+
b10e8e
     /* Set up our libparted exception handler. */
b10e8e
     ped_exception_set_handler(partedExnHandler);
b10e8e
 }
b10e8e
-- 
b10e8e
2.4.0
b10e8e