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(a)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