--- /dev/null
+/*#############################################################################
+# #
+# Pakfire - The IPFire package management system #
+# Copyright (C) 2025 Pakfire development team #
+# #
+# 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 3 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, see <http://www.gnu.org/licenses/>. #
+# #
+#############################################################################*/
+
+#define PY_SSIZE_T_CLEAN
+#include <Python.h>
+
+#include <pakfire/transaction.h>
+
+#include "errors.h"
+#include "pakfire.h"
+#include "transaction.h"
+
+static PyObject* Transaction_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
+ TransactionObject* self = (TransactionObject *)type->tp_alloc(type, 0);
+ if (self) {
+ self->transaction = NULL;
+ }
+
+ return (PyObject*)self;
+}
+
+static void Transaction_dealloc(TransactionObject* self) {
+ if (self->transaction)
+ pakfire_transaction_unref(self->transaction);
+
+ Py_TYPE(self)->tp_free((PyObject *)self);
+}
+
+static int Transaction_init(TransactionObject* self, PyObject* args, PyObject* kwargs) {
+ PakfireObject* pakfire = NULL;
+ int allow_downgrade = 0;
+ int allow_uninstall = 0;
+ int without_recommended = 0;
+ int flags = 0;
+ int r;
+
+ const char* kwlist[] = {
+ "pakfire",
+ "allow_downgrade",
+ "allow_uninstall",
+ "without_recommended",
+ NULL,
+ };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|bbb", (char**)kwlist,
+ &PakfireType, &pakfire, &allow_downgrade, &allow_uninstall, &without_recommended))
+ return -1;
+
+ if (allow_downgrade)
+ flags |= PAKFIRE_TRANSACTION_ALLOW_DOWNGRADE;
+
+ if (allow_uninstall)
+ flags |= PAKFIRE_TRANSACTION_ALLOW_UNINSTALL;
+
+ if (without_recommended)
+ flags |= PAKFIRE_TRANSACTION_WITHOUT_RECOMMENDED;
+
+ // Create a new transaction
+ r = pakfire_transaction_create(&self->transaction, pakfire->pakfire, flags);
+ if (r < 0) {
+ errno = -r;
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+
+ return 0;
+}
+
+static PyObject* Transaction_request(TransactionObject* self,
+ const enum pakfire_job_action action, PyObject* args, PyObject* kwargs) {
+ const char* what = NULL;
+ int best = 0;
+ int essential = 0;
+ int keep_deps = 0;
+ int keep_orphaned = 0;
+ int flags = 0;
+ int r;
+
+ const char* kwlist[] = {
+ "best",
+ "essential",
+ "keeps_deps",
+ "keep_orphaned",
+ NULL,
+ };
+
+ // Parse arguments
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|bbbb", (char**)kwlist, &what,
+ &best, &essential, &keep_deps, &keep_orphaned))
+ return NULL;
+
+ // Do we need to catch any invalid flags here and raise ValueError?
+
+ // Add request
+ r = pakfire_transaction_request(self->transaction, action, what, flags);
+ if (r < 0) {
+ errno = -r;
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject* Transaction_install(TransactionObject* self, PyObject* args, PyObject* kwargs) {
+ return Transaction_request(self, PAKFIRE_JOB_INSTALL, args, kwargs);
+}
+
+static PyObject* Transaction_remove(TransactionObject* self, PyObject* args, PyObject* kwargs) {
+ return Transaction_request(self, PAKFIRE_JOB_ERASE, args, kwargs);
+}
+
+static PyObject* Transaction_update(TransactionObject* self, PyObject* args, PyObject* kwargs) {
+ return Transaction_request(self, PAKFIRE_JOB_UPDATE, args, kwargs);
+}
+
+static PyObject* Transaction_solve(TransactionObject* self) {
+ char* problems = NULL;
+ int r;
+
+ // Solve the transaction
+ r = pakfire_transaction_solve(self->transaction, PAKFIRE_SOLVE_SHOW_SOLUTIONS, &problems);
+ if (r < 0) {
+ errno = -r;
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto ERROR;
+
+ // Solving failed :(
+ } else if (r > 0) {
+ PyErr_SetString(PyExc_DependencyError, problems);
+ goto ERROR;
+ }
+
+ // Problems should never be set here
+ assert(problems == NULL);
+
+ // Return None if everything was okay
+ Py_RETURN_NONE;
+
+ERROR:
+ if (problems)
+ free(problems);
+
+ return NULL;
+}
+
+static struct PyMethodDef Transaction_methods[] = {
+ {
+ "install",
+ (PyCFunction)Transaction_install,
+ METH_VARARGS|METH_KEYWORDS,
+ NULL,
+ },
+ {
+ "remove",
+ (PyCFunction)Transaction_remove,
+ METH_VARARGS|METH_KEYWORDS,
+ NULL,
+ },
+ {
+ "solve",
+ (PyCFunction)Transaction_solve,
+ METH_NOARGS,
+ NULL,
+ },
+ {
+ "update",
+ (PyCFunction)Transaction_update,
+ METH_VARARGS|METH_KEYWORDS,
+ NULL,
+ },
+ { NULL },
+};
+
+PyTypeObject TransactionType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "pakfire.Transaction",
+ .tp_basicsize = sizeof(TransactionObject),
+ .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
+ .tp_new = Transaction_new,
+ .tp_dealloc = (destructor)Transaction_dealloc,
+ .tp_init = (initproc)Transaction_init,
+ .tp_doc = "Transaction Object",
+ .tp_methods = Transaction_methods,
+};
--- /dev/null
+/*#############################################################################
+# #
+# Pakfire - The IPFire package management system #
+# Copyright (C) 2025 Pakfire development team #
+# #
+# 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 3 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, see <http://www.gnu.org/licenses/>. #
+# #
+#############################################################################*/
+
+#ifndef PYTHON_PAKFIRE_TRANSACTION_H
+#define PYTHON_PAKFIRE_TRANSACTION_H
+
+#include <Python.h>
+
+#include <pakfire/transaction.h>
+
+typedef struct {
+ PyObject_HEAD
+ struct pakfire_transaction* transaction;
+} TransactionObject;
+
+extern PyTypeObject TransactionType;
+
+#endif /* PYTHON_PAKFIRE_TRANSACTION_H */