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

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