Previously errors caused a RuntimeException to be raised. This commit
defines a custom exception (libnbdmod.Error) which has two parameters,
the required error string, and the optional errno (which may be 0 if
unavailable).
For example:
$ ./run nbdsh -c 'h.pread(0, 0)'
Traceback (most recent call last):
File "/usr/lib64/python3.7/runpy.py", line 193, in _run_module_as_main
"__main__", mod_spec)
File "/usr/lib64/python3.7/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/home/rjones/d/libnbd/python/nbd.py", line 1133, in <module>
nbdsh.shell()
File "/home/rjones/d/libnbd/python/nbdsh.py", line 62, in shell
exec (c)
File "<string>", line 1, in <module>
File "/home/rjones/d/libnbd/python/nbd.py", line 453, in pread
return libnbdmod.pread (self._o, count, offset, flags)
libnbdmod.Error: ('nbd_pread: invalid state: START: the handle must be connected and
finished handshaking with the server: Transport endpoint is not connected', 107)
---
generator/generator | 31 ++++++++++++++++++++++++++++++-
python/t/610-exception.py | 34 ++++++++++++++++++++++++++++++++++
2 files changed, 64 insertions(+), 1 deletion(-)
diff --git a/generator/generator b/generator/generator
index 157a9cb..11ab8b8 100755
--- a/generator/generator
+++ b/generator/generator
@@ -3337,6 +3337,19 @@ get_handle (PyObject *obj)
return (struct nbd_handle *) PyCapsule_GetPointer(obj, \"nbd_handle\");
}
+/* nbd.Error exception. */
+extern PyObject *nbd_internal_py_Error;
+
+static inline void
+raise_exception ()
+{
+ PyObject *args = PyTuple_New (2);
+
+ PyTuple_SetItem (args, 0, PyUnicode_FromString (nbd_get_error ()));
+ PyTuple_SetItem (args, 1, PyLong_FromLong (nbd_get_errno ()));
+ PyErr_SetObject (nbd_internal_py_Error, args);
+}
+
";
List.iter (
@@ -3390,6 +3403,9 @@ static struct PyModuleDef moduledef = {
NULL, /* m_free */
};
+/* nbd.Error exception. */
+PyObject *nbd_internal_py_Error;
+
extern PyMODINIT_FUNC PyInit_libnbdmod (void);
PyMODINIT_FUNC
@@ -3401,6 +3417,19 @@ PyInit_libnbdmod (void)
if (mod == NULL)
return NULL;
+ nbd_internal_py_Error = PyErr_NewExceptionWithDoc (
+ \"libnbdmod.Error\",
+ \"Exception thrown when the underlying libnbd call fails. This\\n\"
+ \"exception carries a two element tuple. The first element is a\\n\"
+ \"printable string containing the error message. The second\\n\"
+ \"element is the optional errno (which may be 0 in some cases\\n\"
+ \"if the error does not correspond to a system call failure).\\n\",
+ NULL, NULL
+ );
+ if (nbd_internal_py_Error == NULL)
+ return NULL;
+ PyModule_AddObject (mod, \"Error\", nbd_internal_py_Error);
+
return mod;
}
"
@@ -3796,7 +3825,7 @@ let print_python_binding name { args; ret } =
| RBool | RErr | RFd | RInt | RInt64 -> pr " if (ret == -1) {\n";
| RConstString | RString -> pr " if (ret == NULL) {\n";
);
- pr " PyErr_SetString (PyExc_RuntimeError, nbd_get_error ());\n";
+ pr " raise_exception ();\n";
pr " py_ret = NULL;\n";
pr " goto out;\n";
pr " }\n";
diff --git a/python/t/610-exception.py b/python/t/610-exception.py
new file mode 100644
index 0000000..f27b57d
--- /dev/null
+++ b/python/t/610-exception.py
@@ -0,0 +1,34 @@
+# libnbd Python bindings
+# Copyright (C) 2010-2019 Red Hat Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+import nbd
+import libnbdmod
+
+h = nbd.NBD ()
+
+try:
+ # This will always throw an exception because the handle is not
+ # connected.
+ h.pread (0, 0)
+except libnbdmod.Error as ex:
+ str = ex.args[0]
+ errno = ex.args[1]
+ print ("str = %s\nerr = %d\n" % (str, errno))
+ exit (0)
+
+# If we reach here then we didn't catch the exception above.
+exit (1)
--
2.22.0