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