Previously errors caused a RuntimeException to be raised. This commit
defines a custom exception (nbd.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 1163, 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 483, in pread
return libnbdmod.pread (self._o, count, offset, flags)
nbd.Error: nbd_pread: invalid state: START: the handle must be connected and finished
handshaking with the server: Transport endpoint is not connected (ENOTCONN)
---
generator/generator | 53 ++++++++++++++++++++++++++++++++++++++-
python/t/610-exception.py | 32 +++++++++++++++++++++++
2 files changed, 84 insertions(+), 1 deletion(-)
diff --git a/generator/generator b/generator/generator
index 157a9cb..7c2fb59 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,11 @@ PyInit_libnbdmod (void)
if (mod == NULL)
return NULL;
+ nbd_internal_py_Error = PyErr_NewException (\"nbd.Error\", NULL, NULL);
+ if (nbd_internal_py_Error == NULL)
+ return NULL;
+ PyModule_AddObject (mod, \"Error\", nbd_internal_py_Error);
+
return mod;
}
"
@@ -3796,7 +3817,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";
@@ -3917,6 +3938,36 @@ Read the libnbd(3) man page to find out how to use the API.
import libnbdmod
+# Re-export Error exception as nbd.Error, adding some methods.
+from libnbdmod import Error
+
+Error.__doc__ = '''
+Exception thrown when the underlying libnbd call fails.
+
+This exception has two properties to query the error. Use
+the .string property to return a printable string containing
+the error message. Use the .errno property to return a
+Python errno (which may be None in some cases if the error
+did not correspond to a system call failure).
+'''
+
+Error.string = property (lambda self: self.args[0])
+
+def _errno (self):
+ import errno
+ try:
+ return errno.errorcode[self.args[1]]
+ except KeyError:
+ return None
+Error.errno = property (_errno)
+
+def _str (self):
+ if self.errno:
+ return (\"%%s (%%s)\" %% (self.string, self.errno))
+ else:
+ return (\"%%s\" %% self.string)
+Error.__str__ = _str
+
";
List.iter (fun (n, i) -> pr "%-30s = %d\n" n i) constants;
diff --git a/python/t/610-exception.py b/python/t/610-exception.py
new file mode 100644
index 0000000..847dfac
--- /dev/null
+++ b/python/t/610-exception.py
@@ -0,0 +1,32 @@
+# 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
+
+h = nbd.NBD ()
+
+try:
+ # This will always throw an exception because the handle is not
+ # connected.
+ h.pread (0, 0)
+except nbd.Error as ex:
+ print ("string = %s" % ex.string)
+ print ("errno = %s" % ex.errno)
+ exit (0)
+
+# If we reach here then we didn't catch the exception above.
+exit (1)
--
2.22.0