LINUX.ORG.RU

python C API: метод класса в качестве callback'а

 ,


0

3

Есть у меня некая библиотека на C, к которой написан биндинг, успешно используемый питоном.

Нужно теперь интегрировать это в существующее приложение, чтобы работало как с gobject, то есть я делаю:


import mycoolcbindingnetlib
class Myclass:
    def __init__(self)
...
   mycoolcbindingnetlib.reg_event_handler(self.mycallbackfn)
   def mycallbackfn(self, par1, par2)
...

и чтобы mycallbackfn нормально пользовался состоянием класса и имел все преимущества метода класса, будучи вызван в качестве callback'а из C-шной библиотеки?

★★★★★
Ответ на: комментарий от true_admin

gobject мне не нужен, он просто как пример приведен. Мне просто нужно сделать как он делает.

slapin ★★★★★
() автор топика
Ответ на: комментарий от slapin

Я не понимаю что ты хочешь. Если питоновский биндинг уже есть то в чём проблема? Даже если он там просто в виде методов ты можешь обернуть своим классом-обёрткой.

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

надо допилить биндинг, чтобы без обертки. Сейчас - я не могу указать коллбак как метод объекта. надо чтобы можно было. Ищу советов, так как питоньи доки по теме безмерно скромны. Хотя такое возможно, на примере того же gobject. Но код у него уж больно сложный и непонятный...

slapin ★★★★★
() автор топика
Ответ на: комментарий от slapin

Сейчас - я не могу указать коллбак как метод объекта

Этого я не понял. Если ты о том стрёмном примере что ты привёл то ты всё делаешь неправильно. Приведи код в нормальном виде с сообщением об ошибке. Щас я вижу ты 1) пытаешься добавить колбэк раньше объявления метода 2) используешь self за пределами методов, это точно работать не будет.

питоньи доки по теме безмерно скромны

O_o да там будь здоров, сам по ним биндинги писал. Читай http://docs.python.org/extending/index.html и http://docs.python.org/c-api/index.html

Хотя щас бы я бы делал это при помощи cython или swig.

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

Ну идея такая: Либа работает сама по себе после инициализации, и главный цикл в ней. В питоне должен быть зарегистрироват callback, который будет дергаться либой.


import lapb
from binascii import hexlify

class PackStuff:
   def __init__(self):
         lapb.register(self.rcv)
         lapb.loop()
   def rcv(self, data, len):
         # processing data
         print hexlify(data)
...
         if ord(data[0] == 0x01:
             self.hande_adc(data[1:])
         if ord(data[0] == 0x02:
             self.hande_test_req(data[1:])
...

if __name__ == "__main__":
    data = PackStuff()

Это то, как оно должно выглядеть. rcv уже будет объявлен на момент исполнения __init__ (так как class тупо исполняется раньше, это ж не компилятор).

Вот нужно реализовать биндинг, при котором rcv сможет нормально использовать self, чтобы иметь общий стейт на класс с методами и все работало как нормальный питон.

slapin ★★★★★
() автор топика
Ответ на: комментарий от true_admin

И в этих вот ссылках как раз и не написано как сделать такую вот вещь. При этом в нативном питоньем коде все работает само.

slapin ★★★★★
() автор топика
Ответ на: комментарий от true_admin

дык там не написано, как должен быть оформлен код register и как должна вызываться rcv, при условии, что там метод.

Сейчас сделано через функцию и глобальные переменные, что не есть гуд.

Сам код биндинга, если он поможет, закину через пару часов, как доберусь до связи.

slapin ★★★★★
() автор топика
Ответ на: комментарий от slapin

как должен быть оформлен код register и как должна вызываться rcv, при условии, что там метод.

так, я всё понял, ты вообще не открывал доки. Прочитай всё что я тебе кинул, вопросы отпадут. rcv вызывается через PyObject_Call, об этом и многом другом сам-знаешь-где всё написано.

true_admin ★★★★★
()
Ответ на: комментарий от tailgunner

угу, только ему надо встроить питон в сишную программу, об этом ни слова в посте потому что это с точки зрения ТС малозначительная информация. А биндинг у него уже есть.

true_admin ★★★★★
()
Ответ на: комментарий от tailgunner

Эээ, для чего? Я не понимаю какую работу ты хочешь переложить на ctypes. Или ты хочешь вывернуть приложение и из питона запускать этот lapb.loop() который потом обратно будет дёргать питоновские коллбэки? Т.е. профит в том чтобы меньше трахаться с C?

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

Не понял вопроса.

slapin> Либа работает сама по себе после инициализации, и главный цикл в ней. В питоне должен быть зарегистрироват callback, который будет дергаться либой.

я предлагаю использовать ctypes для создания этого самого callback.

ты хочешь вывернуть приложение и из питона запускать этот lapb.loop() который потом обратно будет дёргать питоновские коллбэки?

Ну, у меня в планах этого не было, но ничто не мешает сделать и так.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Всё, дошло, чтобы не мучаться с с пинячьм кодом на ctypes создать подобие сишного колбэка и через ctypes передать его запустить loop внутри библиотеки. Т.е. питонячий биндинг тут вообще не требуется.

true_admin ★★★★★
()

код биндинга: (то, как он выглядит сейчас). Если научиться вызывать метод, можно убрать кучу костылей с подпорками и сделать адекватно. использование:

import lapb
from binascii import hexlify
import time

def rdata(s, l):
    hexlify(s)

lapb.init("/dev/ttyS0")
lapb.cb(myfunc)
while True:
    lapb.get_next_data()
    lapb.send("hello")
    time.sleep(0.01)

Выглядит не очень, так скажем...

#include <Python.h>

static PyObject *rcv = NULL;

extern int op_ok(void);

#define NEEDS_NL()                                              \
        do {                                                    \
                if (op_ok() < 0) {                              \
                        PyErr_SetString(PyExc_TypeError,        \
                                "panic: not initialized");      \
                        return 0;                               \
                }                                               \
        } while(0)
static PyObject *cb(PyObject *self, PyObject *args)
{
        PyObject *temp;
        NEEDS_NL();
        if (PyArg_ParseTuple(args, "O", &temp)) {
                if (!PyCallable_Check(temp)) {
                        PyErr_SetString(PyExc_TypeError, "parameter must be callable");
                        return NULL;
                }
        }
        if (op_ok() < 0) {

        }
        Py_XINCREF(temp);
        Py_XDECREF(rcv);
        rcv = temp;
        Py_INCREF(Py_None);
        return Py_None;
}
int call_rcv(int type, unsigned char *data, int len)
{
        PyObject *result;
        PyObject *arglist;
        NEEDS_NL();
        if (data == NULL || len == 0) {
                arglist = Py_BuildValue("(i)", type);
        } else {
                arglist = Py_BuildValue("(is#)", type, data, len);
        }
        printf("Calling callback %p %p\n", rcv, arglist);
        if (!arglist) {
                /* FIXME: raise exception here or something */
                printf("Exception: unable to build value type = %d, data = %p, len = %d\n",
                        type, data, len);
                return 0;
        }
        result = PyObject_CallObject(rcv, arglist);
        printf("Called callback %p %p\n", rcv, result);
        Py_XDECREF(arglist);
        Py_XDECREF(result);
        return 0;
}

static PyObject *busyloop(PyObject *self, PyObject *args)
{
        extern void test(void);
        NEEDS_NL();
        test();
        Py_INCREF(Py_None);
        return Py_None;
}

static PyObject *init(PyObject *self, PyObject *args)
{
        extern void lapb_nl_init(char *tty);
        char *tty;
        if (PyArg_ParseTuple(args, "s", &tty))
                lapb_nl_init(tty);
        else {
                PyErr_SetString(PyExc_TypeError, "need string parameter");
                return NULL;
        }

        Py_INCREF(Py_None);
        return Py_None;
}

static PyObject *py_get_next_data(PyObject *self, PyObject *args)
{
        extern void get_next_data(void);
        NEEDS_NL();
        get_next_data();
        Py_INCREF(Py_None);
        return Py_None;
}
static PyObject *py_send_data(PyObject *self, PyObject *args)
{
        unsigned char *data = NULL;
        int size = 0;
        extern void send_data(unsigned char *data, int size);
        NEEDS_NL();
        if (PyArg_ParseTuple(args, "s#", &data, &size)) {
                if (data != NULL && size > 0)
                        send_data(data, size);
                else {
                        PyErr_SetString(PyExc_TypeError,
                                "Too little data during submit");
                        return NULL;
                }
        }
        Py_INCREF(Py_None);
        return Py_None;
}


static PyMethodDef LAPBMethods[] = {
        {"busyloop", busyloop, METH_VARARGS, "Run netlink busyloop"},
        {"cb", cb, METH_VARARGS, "Configure callback"},
        {"init", init, METH_VARARGS, "Init netlink"},
        {"send", py_send_data, METH_VARARGS, "Send data over netlink"},
        {"get_next_data", py_get_next_data, METH_VARARGS, "Get netlink data"},
        {NULL, NULL, 0, NULL},
};

PyMODINIT_FUNC initlapb(void)
{
        Py_InitModule("lapb", LAPBMethods);
}

slapin ★★★★★
() автор топика
Ответ на: комментарий от slapin

Выглядит нормально, только мигрируйте Py_RETURN_NONE. Я даже с хожу утечек памяти не нашёл, писал человек который имеет представление о питоне. Конечно, «extern void send_data ...» перекинуть наверх.

Но суть не в этом. Как сказал оратор сверху, вам хватит ctypes для ваших целей.

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

желание заказчика, хотят один файл на другой один файл на девайсах поменять.

slapin ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.