Skip to content

Commit

Permalink
Add unbind capability for python mod
Browse files Browse the repository at this point in the history
Patch by: thommey
  • Loading branch information
thommey committed Jul 16, 2024
1 parent 488be7d commit 63ad89e
Showing 1 changed file with 69 additions and 26 deletions.
95 changes: 69 additions & 26 deletions src/mod/python.mod/pycmds.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,23 @@
#include <tcl.h>
#include "src/mod/module.h"

struct py_bind {
tcl_bind_list_t *bindtable;
char tclcmdname[512];
PyObject *callback;
struct py_bind *next;
};

typedef struct {
PyObject_HEAD
char tclcmdname[512];
char tclcmdname[128];
} TclFunc;

typedef struct {
PyObject_HEAD
char tclcmdname[128];
char *flags;
char *mask;
tcl_bind_list_t *bindtable;
PyObject *callback;
} PythonBind;

static PyTypeObject TclFuncType;
static PyTypeObject TclFuncType, PythonBindType;
static int eval_idx = -1;

static struct py_bind *py_bindlist;

static PyObject *EggdropError; //create static Python Exception object

static Tcl_Obj *py_to_tcl_obj(PyObject *o); // generic conversion function
Expand Down Expand Up @@ -141,13 +141,13 @@ static PyObject *py_findircuser(PyObject *self, PyObject *args) {
static int tcl_call_python(ClientData cd, Tcl_Interp *irp, int objc, Tcl_Obj *const objv[])
{
PyObject *args = PyTuple_New(objc > 1 ? objc - 1: 0);
struct py_bind *bindinfo = cd;
PythonBind *bind = cd;

// objc[0] is procname
for (int i = 1; i < objc; i++) {
PyTuple_SET_ITEM(args, i - 1, Py_BuildValue("s", Tcl_GetStringFromObj(objv[i], NULL)));
}
if (!PyObject_Call(bindinfo->callback, args, NULL)) {
if (!PyObject_Call(bind->callback, args, NULL)) {
PyErr_Print();
Tcl_SetResult(irp, "Error calling python code", TCL_STATIC);
return TCL_ERROR;
Expand Down Expand Up @@ -199,6 +199,7 @@ static PyObject *py_parse_tcl_dict(PyObject *self, PyObject *args) {
strobj = Tcl_NewStringObj(str, -1);
if (Tcl_DictObjFirst(tclinterp, strobj, &search, &key, &value, &done) != TCL_OK) {
PyErr_SetString(EggdropError, "Supplied string is not a Tcl dictionary");
return NULL;
}
result = PyDict_New();
while (!done) {
Expand All @@ -212,10 +213,34 @@ static PyObject *py_parse_tcl_dict(PyObject *self, PyObject *args) {
return result;
}

static PyObject *py_unbind(PyObject *self, PyObject *args) {
PythonBind *bind;

if (!PyObject_TypeCheck(self, &PythonBindType)) {
PyErr_SetString(EggdropError, "Invalid argument for unbind method");
return NULL;
}

bind = (PythonBind *)self;
unbind_bind_entry(bind->bindtable, bind->flags, bind->mask, bind->tclcmdname);
// cleanup in python_bind_destroyed callback when Tcl command is destroyed
Py_RETURN_NONE;
}

void python_bind_destroyed(ClientData cd) {
PythonBind *bind = cd;

Py_DECREF(bind->callback);
nfree(bind->mask);
nfree(bind->flags);
Py_DECREF((PyObject *)bind);
}

static PyObject *py_bind(PyObject *self, PyObject *args) {
PyObject *callback;
PythonBind *bind;
Py_hash_t hash;
char *bindtype, *mask, *flags;
struct py_bind *bindinfo;
tcl_bind_list_t *tl;

// type flags mask callback
Expand All @@ -237,17 +262,18 @@ static PyObject *py_bind(PyObject *self, PyObject *args) {
}
Py_IncRef(callback);

bindinfo = nmalloc(sizeof *bindinfo);
bindinfo->bindtable = tl;
bindinfo->callback = callback;
bindinfo->next = py_bindlist;
snprintf(bindinfo->tclcmdname, sizeof bindinfo->tclcmdname, "*python:%s:%s:%" PRIxPTR, bindtype, mask, (uintptr_t)callback);
py_bindlist = bindinfo;
// TODO: deleteproc
Tcl_CreateObjCommand(tclinterp, bindinfo->tclcmdname, tcl_call_python, bindinfo, NULL);
// TODO: flags?
bind_bind_entry(tl, flags, mask, bindinfo->tclcmdname);
Py_RETURN_NONE;
bind = PyObject_New(PythonBind, &PythonBindType);
bind->mask = strdup(mask);
bind->flags = strdup(flags);
bind->bindtable = tl;
bind->callback = callback;
hash = PyObject_Hash((PyObject *)bind);
snprintf(bind->tclcmdname, sizeof bind->tclcmdname, "*python:%s:%" PRIx64, bindtype, (int64_t)hash);

Tcl_CreateObjCommand(tclinterp, bind->tclcmdname, tcl_call_python, bind, python_bind_destroyed);
bind_bind_entry(tl, flags, mask, bind->tclcmdname);

return (PyObject *)bind;
}

static Tcl_Obj *py_list_to_tcl_obj(PyObject *o) {
Expand Down Expand Up @@ -358,7 +384,7 @@ static PyObject *py_findtclfunc(PyObject *self, PyObject *args) {
return NULL;
}
result = PyObject_New(TclFunc, &TclFuncType);
strcpy(result->tclcmdname, cmdname);
strlcpy(result->tclcmdname, cmdname, sizeof result->tclcmdname);
return (PyObject *)result;
}

Expand Down Expand Up @@ -405,6 +431,22 @@ static PyTypeObject TclFuncType = {
.tp_call = python_call_tcl,
};

static PyMethodDef PythonBindMethods[] = {
{"unbind", py_unbind, METH_VARARGS, "deregister an eggdrop python bind"},
{NULL, NULL, 0, NULL} /* Sentinel */
};

static PyTypeObject PythonBindType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "eggdrop.PythonBind",
.tp_doc = "Eggdrop bind to a python callback",
.tp_basicsize = sizeof(PythonBind),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_new = PyType_GenericNew,
.tp_methods = PythonBindMethods
};

PyMODINIT_FUNC PyInit_eggdrop(void) {
PyObject *pymodobj, *eggtclmodobj, *pymoddict;

Expand All @@ -430,6 +472,7 @@ PyMODINIT_FUNC PyInit_eggdrop(void) {
PyDict_SetItemString(pymoddict, "eggdrop.tcl", eggtclmodobj);

PyType_Ready(&TclFuncType);
PyType_Ready(&PythonBindType);

return pymodobj;
}

0 comments on commit 63ad89e

Please sign in to comment.