Richard W.M. Jones
2019-Jun-28 18:27 UTC
[Libguestfs] [PATCH libnbd v3] python: Raise a custom exception containing error string and errno.
Following Eric's suggestions from v2, this adds .string, .errno and .__str__ properties. The .string property returns the error string. The .errno property returns the errno (from the errno module), or None. The __str__ property makes the exception nicely printable. Rich.
Richard W.M. Jones
2019-Jun-28 18:27 UTC
[Libguestfs] [PATCH libnbd v3] python: Raise a custom exception containing error string and errno.
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
Eric Blake
2019-Jun-28 18:53 UTC
Re: [Libguestfs] [PATCH libnbd v3] python: Raise a custom exception containing error string and errno.
On 6/28/19 1:27 PM, Richard W.M. Jones wrote:> 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)Cool - in the time I spent writing my reply to v2 1/1, your reaction to my reply on 0/1 figured out the way to get what we want:> @@ -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 ErrorImplement all the cool stuff in pure Python on top of the bare-bones minimum :) Lots less hassle than writing it in C code. I like it!> + > +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__ = _strLooks good to me now! Thanks for figuring this out while I was struggling with reading lots of documentation on C bindings. ACK -- Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3226 Virtualization: qemu.org | libvirt.org
Eric Blake
2019-Jun-28 20:01 UTC
Re: [Libguestfs] [PATCH libnbd v3] python: Raise a custom exception containing error string and errno.
On 6/28/19 1:27 PM, Richard W.M. Jones wrote:> 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). >> +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 ()));These three lines could probably be compressed into: Py_BuildValue("si", nbd_get_error(), nbd_get_errno ()); I'll look for other shortcuts like that while auditing for proper error checking. -- Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3226 Virtualization: qemu.org | libvirt.org
Possibly Parallel Threads
- [PATCH libnbd v3] python: Raise a custom exception containing error string and errno.
- Re: [PATCH libnbd v3] python: Raise a custom exception containing error string and errno.
- [PATCH libnbd v2] python: Raise a custom exception containing error string and errno.
- [PATCH libnbd v3] python: Raise a custom exception containing error string and errno.
- [PATCH libnbd] python: Raise a custom exception containing error string and errno.