In addition to calling python functions from C, we want to make script writing easier by exposing C functions to python. For now, just wrap nbdkit_set_error(), as that will be needed for an optimal implementation of a zero() callback. Signed-off-by: Eric Blake <eblake@redhat.com> --- v2: tweak docs --- plugins/python/nbdkit-python-plugin.pod | 30 +++++++++++++++++++++++++----- plugins/python/python.c | 18 ++++++++++++++++++ 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/plugins/python/nbdkit-python-plugin.pod b/plugins/python/nbdkit-python-plugin.pod index 63e5f96..a1e3d2b 100644 --- a/plugins/python/nbdkit-python-plugin.pod +++ b/plugins/python/nbdkit-python-plugin.pod @@ -63,9 +63,22 @@ does not need to be executable. In fact it's a good idea not to do that, because running the plugin directly as a Python script won't work. +=head2 METHODS + +Your script may use C<import nbdkit> to have access to the following +methods in the C<nbdkit> module: + + nbdkit.set_error(I<err>) + +Record C<err> as the reason you are about to throw an exception. C<err> +should correspond to usual errno values, where it may help to +C<import errno>. + =head2 EXCEPTIONS -Python callbacks should throw exceptions to indicate errors. +Python callbacks should throw exceptions to indicate errors. Remember +to use C<nbdkit.set_error> if you need to control which error is sent +back to the client; if omitted, the client will see an error of C<EIO>. =head2 PYTHON CALLBACKS @@ -158,7 +171,8 @@ disk starting at C<offset>. NBD only supports whole reads, so your function should try to read the whole region (perhaps requiring a loop). If the read fails or -is partial, your function should throw an exception. +is partial, your function should throw an exception, optionally using +C<nbdkit.set_error> first. =item C<pwrite> @@ -174,7 +188,8 @@ C<offset>. NBD only supports whole writes, so your function should try to write the whole region (perhaps requiring a loop). If the write -fails or is partial, your function should throw an exception. +fails or is partial, your function should throw an exception, + optionally using C<nbdkit.set_error> first. =item C<flush> @@ -186,6 +201,9 @@ fails or is partial, your function should throw an exception. The body of your C<flush> function should do a L<sync(2)> or L<fdatasync(2)> or equivalent on the backing store. +If the flush fails, your function should throw an exception, optionally +using C<nbdkit.set_error> first. + =item C<trim> (Optional) @@ -194,7 +212,8 @@ L<fdatasync(2)> or equivalent on the backing store. # no return value The body of your C<trim> function should "punch a hole" in the -backing store. +backing store. If the trim fails, your function should throw an +exception, optionally using C<nbdkit.set_error> first. =back @@ -210,7 +229,8 @@ constructs. =item Missing: C<errno_is_reliable> This is not needed because the process of gluing Python code into C cannot -reliably use C<errno>. +reliably use C<errno>. Instead, call C<nbdkit.set_error> when reporting +a failure. =item Missing: C<name>, C<version>, C<longname>, C<description>, C<config_help> diff --git a/plugins/python/python.c b/plugins/python/python.c index 7e9fc1e..631411e 100644 --- a/plugins/python/python.c +++ b/plugins/python/python.c @@ -54,6 +54,23 @@ static const char *script; static PyObject *module; +static PyObject * +set_error (PyObject *self, PyObject *args) +{ + int err; + + if (!PyArg_ParseTuple(args, "i", &err)) + return NULL; + nbdkit_set_error (err); + Py_RETURN_NONE; +} + +static PyMethodDef NbdkitMethods[] = { + { "set_error", set_error, METH_VARARGS, + "Store an errno value prior to throwing an exception" }, + { NULL } +}; + /* Is a callback defined? */ static int callback_defined (const char *name, PyObject **obj_rtn) @@ -91,6 +108,7 @@ static void py_load (void) { Py_Initialize (); + Py_InitModule("nbdkit", NbdkitMethods); } static void -- 2.9.3