[Libguestfs] [PATCH libnbd v3] python: Raise a custom exception containing error string and errno.

Richard W.M. Jones rjones at redhat.com
Fri Jun 28 18:27:01 UTC 2019


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




More information about the Libguestfs mailing list