]> git.ipfire.org Git - pakfire.git/commitdiff
python: Add functions to register a progress callback
authorMichael Tremer <michael.tremer@ipfire.org>
Fri, 14 Mar 2025 16:04:19 +0000 (16:04 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Fri, 14 Mar 2025 16:04:19 +0000 (16:04 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/python/ctx.c
src/python/ctx.h
tests/python/ctx.py

index 375f7308dc4033bc7a52eca198cdb737381a2e25..204dc7056adbc2b95da10f5e3f2236b795b97727 100644 (file)
@@ -198,6 +198,7 @@ static void Ctx_dealloc(CtxObject* self) {
        }
 
        Py_XDECREF(self->callbacks.confirm);
+       Py_XDECREF(self->callbacks.progress);
        Py_XDECREF(self->logger);
 
        Py_TYPE(self)->tp_free((PyObject *)self);
@@ -263,6 +264,150 @@ static PyObject* Ctx_set_confirm_callback(CtxObject* self, PyObject* args) {
        Py_RETURN_NONE;
 }
 
+// Progress Callback
+
+static int Ctx_progress_start(struct pakfire_ctx* ctx,
+               struct pakfire_progress* progress, void* data, unsigned long int value) {
+       PyObject* ret = NULL;
+       PyObject* p = data;
+       int r = -1;
+
+       // Fetch the start method
+       PyObject* start = PyObject_GetAttrString(p, "start");
+       if (!start)
+               goto ERROR;
+
+       // Call it with the value
+       ret = PyObject_CallFunction(start, "k", value);
+       if (!ret)
+               goto ERROR;
+
+       // Success
+       r = 0;
+
+ERROR:
+       Py_XDECREF(start);
+       Py_XDECREF(ret);
+
+       return r;
+}
+
+static int Ctx_progress_finish(struct pakfire_ctx* ctx,
+               struct pakfire_progress* progress, void* data) {
+       PyObject* ret = NULL;
+       PyObject* p = data;
+       int r = -1;
+
+       // Fetch the finish method
+       PyObject* finish = PyObject_GetAttrString(p, "finish");
+       if (!finish)
+               goto ERROR;
+
+       // Call it with any arguments
+       ret = PyObject_CallNoArgs(finish);
+       if (!ret)
+               goto ERROR;
+
+       // Success
+       r = 0;
+
+ERROR:
+       Py_XDECREF(finish);
+       Py_XDECREF(ret);
+
+       return r;
+}
+
+static int Ctx_progress_update(struct pakfire_ctx* ctx,
+               struct pakfire_progress* progress, void* data, unsigned long int value) {
+       PyObject* ret = NULL;
+       PyObject* p = data;
+       int r = -1;
+
+       // Fetch the update method
+       PyObject* update = PyObject_GetAttrString(p, "update");
+       if (!update)
+               goto ERROR;
+
+       // Call it with the value
+       ret = PyObject_CallFunction(update, "k", value);
+       if (!ret)
+               goto ERROR;
+
+       // Success
+       r = 0;
+
+ERROR:
+       Py_XDECREF(update);
+       Py_XDECREF(ret);
+
+       return r;
+}
+
+static void Ctx_progress_free(
+               struct pakfire_ctx* ctx, struct pakfire_progress* progress, void* data) {
+       PyObject* p = data;
+
+       // Free the object
+       Py_XDECREF(p);
+}
+
+static int Ctx_progress_callback(
+               struct pakfire_ctx* ctx, void* data, struct pakfire_progress* progress) {
+       PyObject* callback = data;
+       PyObject* p = NULL;
+
+       // Fetch the title
+       const char* title = pakfire_progress_get_title(progress);
+
+       // Call the callback to create a new progress object
+       p = PyObject_CallFunction(callback, "z", title);
+       if (!p)
+               return -1;
+
+       // Set callback data
+       pakfire_progress_set_callback_data(progress, p);
+
+       // Register the progress callbacks with the progress object
+
+       // Set start callback
+       pakfire_progress_set_start_callback(progress, Ctx_progress_start);
+
+       // Set finish callback
+       pakfire_progress_set_finish_callback(progress, Ctx_progress_finish);
+
+       // Set update callback
+       pakfire_progress_set_update_callback(progress, Ctx_progress_update);
+
+       // Set free callback
+       pakfire_progress_set_free_callback(progress, Ctx_progress_free);
+
+       return 0;
+}
+
+static PyObject* Ctx_set_progress_callback(CtxObject* self, PyObject* args) {
+       PyObject* callback = NULL;
+
+       // Parse arguments
+       if (!PyArg_ParseTuple(args, "O", &callback))
+               return NULL;
+
+       // Check if the callback is actually callable
+       if (!PyCallable_Check(callback)) {
+               PyErr_SetString(PyExc_ValueError, "Callback is not callable");
+               return NULL;
+       }
+
+       // Store a reference to the callback
+       self->callbacks.progress = callback;
+       Py_INCREF(self->callbacks.progress);
+
+       // Set the progress callback of the context
+       pakfire_ctx_set_progress_callback(self->ctx, Ctx_progress_callback, self->callbacks.progress);
+
+       Py_RETURN_NONE;
+}
+
 // Cache Path
 
 static PyObject* Ctx_get_cache_path(CtxObject* self) {
@@ -306,6 +451,12 @@ static struct PyMethodDef Ctx_methods[] = {
                METH_VARARGS,
                NULL,
        },
+       {
+               "set_progress_callback",
+               (PyCFunction)Ctx_set_progress_callback,
+               METH_VARARGS,
+               NULL,
+       },
        {
                "set_logger",
                (PyCFunction)Ctx_set_logger,
index 81d12ad987dd79080dba26f6df012ad3b7aa7933..c5a0e3a885bd7fc3a9243d6d9592642b17930d5c 100644 (file)
@@ -34,6 +34,7 @@ typedef struct {
        // Callbacks
        struct {
                PyObject* confirm;
+               PyObject* progress;
        } callbacks;
 } CtxObject;
 
index 80ce2d9e28c7620b2d43dea4ae67eb248b30273f..3c9fc9230e5bfdaa9869e9241621afd6a36913a6 100755 (executable)
@@ -112,6 +112,20 @@ class CtxTests(tests.TestCase):
                # Set the confirm callback
                ctx.set_confirm_callback(confirm_callback)
 
+               # Try setting something that we cannot call
+               with self.assertRaises(ValueError):
+                       ctx.set_progress_callback(None)
+
+               def progress_callback(title):
+                       """
+                               This should actually return an object,
+                               but we don't expect this to be called ever.
+                       """
+                       pass
+
+               # Set the progress callback
+               ctx.set_progress_callback(progress_callback)
+
 
 if __name__ == "__main__":
        tests.main()