From 9a9ffd5594a5d27bbecf6160de2c33d44870f5bd Mon Sep 17 00:00:00 2001 From: Vasilii Chernov Date: Wed, 17 Feb 2016 17:20:25 +0100 Subject: Refactor pcipywrap to object --- pywrap/pcipywrap.c | 380 +++++++++++++++++++++++------------------------------ pywrap/pcipywrap.h | 68 ++++++++++ pywrap/pcipywrap.i | 32 ++--- pywrap/server.py | 39 +++--- 4 files changed, 268 insertions(+), 251 deletions(-) create mode 100644 pywrap/pcipywrap.h (limited to 'pywrap') diff --git a/pywrap/pcipywrap.c b/pywrap/pcipywrap.c index 639fa15..11e3ce7 100644 --- a/pywrap/pcipywrap.c +++ b/pywrap/pcipywrap.c @@ -1,11 +1,5 @@ #include "pcipywrap.h" -/*! - * \brief Global pointer to pcilib_t context. - * Used by set_pcilib and read_register. - */ -pcilib_t* __ctx = 0; - char* full_log = NULL; /*! @@ -34,6 +28,7 @@ char* vmake_str(const char* msg, va_list vl) return buf; } + /*! * \brief Wraping for vsnprintf function, that saves string to char* * \return saved from vsnprintf string @@ -105,175 +100,6 @@ void __redirect_logs_to_exeption() pcilib_get_logger_context()); } -/*! - * Destructor for pcilib_t - */ -void close_pcilib_instance(void *ctx) -{ - if(ctx != __ctx) - pcilib_close(ctx); -} - -PyObject* create_pcilib_instance(const char *fpga_device, const char *model) -{ - //opening device - pcilib_t* ctx = pcilib_open(fpga_device, model); - if(!ctx) - { - set_python_exception("Failed pcilib_open(%s, %s)", fpga_device, model); - return NULL; - } - return PyCObject_FromVoidPtr((void*)ctx, close_pcilib_instance); -} - -void close_curr_pcilib_instance() -{ - if(__ctx) - { - pcilib_close(__ctx); - __ctx = NULL; - } -} - -PyObject* get_curr_pcilib_instance() -{ - return PyByteArray_FromStringAndSize((const char*)&__ctx, sizeof(pcilib_t*)); -} - -PyObject* set_pcilib(PyObject* addr) -{ - if(!PyCObject_Check(addr)) - { - set_python_exception("Incorrect addr type. Only PyCObject is allowed"); - return NULL; - } - - __ctx = (pcilib_t*)PyCObject_AsVoidPtr(addr); - - return PyInt_FromLong((long)1); -} - -PyObject* read_register(const char *regname, const char *bank) -{ - if(!__ctx) - { - set_python_exception("pcilib_t handler not initialized"); - return NULL; - } - - pcilib_value_t val = {0}; - pcilib_register_value_t reg_value; - - int err; - - err = pcilib_read_register(__ctx, bank, regname, ®_value); - if(err) - { - set_python_exception("Failed pcilib_read_register"); - return NULL; - } - - err = pcilib_set_value_from_register_value(__ctx, &val, reg_value); - if(err) - { - set_python_exception("Failed pcilib_set_value_from_register_value"); - return NULL; - } - - return pcilib_get_value_as_pyobject(__ctx, &val, NULL); -} - -PyObject* write_register(PyObject* val, const char *regname, const char *bank) -{ - if(!__ctx) - { - pcilib_error("pcilib_t handler not initialized"); - return NULL; - } - - - pcilib_value_t val_internal = {0}; - pcilib_register_value_t reg_value; - - int err; - - err = pcilib_set_value_from_pyobject(__ctx, &val_internal, val); - - if(err) - { - set_python_exception("Failed pcilib_set_value_from_pyobject"); - return NULL; - } - - - reg_value = pcilib_get_value_as_register_value(__ctx, &val_internal, &err); - if(err) - { - set_python_exception("Failed pcilib_set_value_from_pyobject, (error %i)", err); - return NULL; - } - - err = pcilib_write_register(__ctx, bank, regname, reg_value); - - if(err) - { - set_python_exception("Failed pcilib_set_value_from_pyobject, (error %i)", err); - return NULL; - } - - return PyInt_FromLong((long)1); -} - -PyObject* get_property(const char *prop) -{ - if(!__ctx) - { - set_python_exception("pcilib_t handler not initialized"); - return NULL; - } - - int err; - pcilib_value_t val = {0}; - - err = pcilib_get_property(__ctx, prop, &val); - - if(err) - { - set_python_exception("Failed pcilib_get_property, (error %i)", err); - return NULL; - } - - return pcilib_get_value_as_pyobject(__ctx, &val, NULL); -} - -PyObject* set_property(PyObject* val, const char *prop) -{ - int err; - - if(!__ctx) - { - set_python_exception("pcilib_t handler not initialized"); - return NULL; - } - - pcilib_value_t val_internal = {0}; - err = pcilib_set_value_from_pyobject(__ctx, &val_internal, val); - if(err) - { - set_python_exception("pcilib_set_value_from_pyobject, (error %i)", err); - return NULL; - } - - err = pcilib_set_property(__ctx, prop, &val_internal); - if(err) - { - set_python_exception("pcilib_set_property, (error %i)", err); - return NULL; - } - - return PyInt_FromLong((long)1); -} - /*! * \brief Wrap for PyDict_SetItem, with decrease reference counting after set. */ @@ -295,9 +121,9 @@ void pcilib_pylist_append(PyObject* list, PyObject* value) Py_XDECREF(value); } -void add_pcilib_value_to_dict(PyObject* dict, pcilib_value_t* val, const char *name) +void add_pcilib_value_to_dict(pcilib_t* ctx, PyObject* dict, pcilib_value_t* val, const char *name) { - PyObject *py_val = (PyObject*)pcilib_get_value_as_pyobject(__ctx, val, NULL); + PyObject *py_val = (PyObject*)pcilib_get_value_as_pyobject(ctx, val, NULL); if(py_val) pcilib_pydict_set_item(dict, @@ -309,7 +135,7 @@ void add_pcilib_value_to_dict(PyObject* dict, pcilib_value_t* val, const char *n PyString_FromString("invalid")); } -PyObject * pcilib_convert_property_info_to_pyobject(pcilib_property_info_t listItem) +PyObject * pcilib_convert_property_info_to_pyobject(pcilib_t* ctx, pcilib_property_info_t listItem) { PyObject* pylistItem = PyDict_New(); @@ -386,7 +212,7 @@ PyObject * pcilib_convert_property_info_to_pyobject(pcilib_property_info_t listI return pylistItem; } -PyObject * pcilib_convert_register_info_to_pyobject(pcilib_register_info_t listItem) +PyObject * pcilib_convert_register_info_to_pyobject(pcilib_t* ctx, pcilib_register_info_t listItem) { PyObject* pylistItem = PyDict_New(); @@ -430,20 +256,20 @@ PyObject * pcilib_convert_register_info_to_pyobject(pcilib_register_info_t listI modes); pcilib_value_t defval = {0}; - pcilib_set_value_from_register_value(__ctx, &defval, listItem.defvalue); - add_pcilib_value_to_dict(pylistItem, &defval, "defvalue"); + pcilib_set_value_from_register_value(ctx, &defval, listItem.defvalue); + add_pcilib_value_to_dict(ctx, pylistItem, &defval, "defvalue"); if(listItem.range) { pcilib_value_t minval = {0}; - pcilib_set_value_from_register_value(__ctx, &minval, listItem.range->min); - + pcilib_set_value_from_register_value(ctx, &minval, listItem.range->min); + pcilib_value_t maxval = {0}; - pcilib_set_value_from_register_value(__ctx, &maxval, listItem.range->max); + pcilib_set_value_from_register_value(ctx, &maxval, listItem.range->max); PyObject* range = PyDict_New(); - add_pcilib_value_to_dict(range, &minval, "min"); - add_pcilib_value_to_dict(range, &maxval, "max"); + add_pcilib_value_to_dict(ctx, range, &minval, "min"); + add_pcilib_value_to_dict(ctx, range, &maxval, "max"); pcilib_pydict_set_item(pylistItem, PyString_FromString("range"), range); @@ -458,17 +284,17 @@ PyObject * pcilib_convert_register_info_to_pyobject(pcilib_register_info_t listI PyObject* valuesItem = PyDict_New(); pcilib_value_t val = {0}; - pcilib_set_value_from_register_value(__ctx, &val, listItem.values[j].value); + pcilib_set_value_from_register_value(ctx, &val, listItem.values[j].value); pcilib_value_t min = {0}; - pcilib_set_value_from_register_value(__ctx, &min, listItem.values[j].min); + pcilib_set_value_from_register_value(ctx, &min, listItem.values[j].min); pcilib_value_t max = {0}; - pcilib_set_value_from_register_value(__ctx, &max, listItem.values[j].max); + pcilib_set_value_from_register_value(ctx, &max, listItem.values[j].max); - add_pcilib_value_to_dict(valuesItem, &val, "value"); - add_pcilib_value_to_dict(valuesItem, &min, "min"); - add_pcilib_value_to_dict(valuesItem, &max, "max"); + add_pcilib_value_to_dict(ctx, valuesItem, &val, "value"); + add_pcilib_value_to_dict(ctx, valuesItem, &min, "min"); + add_pcilib_value_to_dict(ctx, valuesItem, &max, "max"); if(listItem.values[j].name) pcilib_pydict_set_item(valuesItem, @@ -491,71 +317,187 @@ PyObject * pcilib_convert_register_info_to_pyobject(pcilib_register_info_t listI return pylistItem; } -PyObject* get_registers_list(const char *bank) +Pcipywrap *new_Pcipywrap(const char* fpga_device, const char* model) { - if(!__ctx) + //opening device + pcilib_t* ctx = pcilib_open(fpga_device, model); + if(!ctx) + { + set_python_exception("Failed pcilib_open(%s, %s)", fpga_device, model); + return NULL; + } + Pcipywrap *self; + self = (Pcipywrap *) malloc(sizeof(Pcipywrap)); + self->shared = 0; + self->ctx = ctx; + return self; +} + +Pcipywrap *create_Pcipywrap(PyObject* ctx) +{ + if(!PyCObject_Check(ctx)) { - set_python_exception("pcilib_t handler not initialized"); + set_python_exception("Incorrect ctx type. Only PyCObject is allowed"); return NULL; } - pcilib_register_info_t *list = pcilib_get_register_list(__ctx, bank, PCILIB_LIST_FLAGS_DEFAULT); + Pcipywrap *self; + self = (Pcipywrap *) malloc(sizeof(Pcipywrap)); + self->shared = 1; + self->ctx = PyCObject_AsVoidPtr(ctx); + return self; +} + +void delete_Pcipywrap(Pcipywrap *self) { + if(!self->shared) + pcilib_close(self->ctx); + + free(self); +} + +PyObject* Pcipywrap_read_register(Pcipywrap *self, const char *regname, const char *bank) +{ + pcilib_value_t val = {0}; + pcilib_register_value_t reg_value; + + int err; + + err = pcilib_read_register(self->ctx, bank, regname, ®_value); + if(err) + { + set_python_exception("Failed pcilib_read_register"); + return NULL; + } + + err = pcilib_set_value_from_register_value(self->ctx, &val, reg_value); + if(err) + { + set_python_exception("Failed pcilib_set_value_from_register_value"); + return NULL; + } + + return pcilib_get_value_as_pyobject(self->ctx, &val, NULL); +} + +PyObject* Pcipywrap_write_register(Pcipywrap *self, PyObject* val, const char *regname, const char *bank) +{ + pcilib_value_t val_internal = {0}; + pcilib_register_value_t reg_value; + + int err; + + err = pcilib_set_value_from_pyobject(self->ctx, &val_internal, val); + + if(err) + { + set_python_exception("Failed pcilib_set_value_from_pyobject"); + return NULL; + } + + + reg_value = pcilib_get_value_as_register_value(self->ctx, &val_internal, &err); + if(err) + { + set_python_exception("Failed pcilib_set_value_from_pyobject, (error %i)", err); + return NULL; + } + + err = pcilib_write_register(self->ctx, bank, regname, reg_value); + + if(err) + { + set_python_exception("Failed pcilib_set_value_from_pyobject, (error %i)", err); + return NULL; + } + + return PyInt_FromLong((long)1); +} + +PyObject* Pcipywrap_get_property(Pcipywrap *self, const char *prop) +{ + int err; + pcilib_value_t val = {0}; + + err = pcilib_get_property(self->ctx, prop, &val); + + if(err) + { + set_python_exception("Failed pcilib_get_property, (error %i)", err); + return NULL; + } + + return pcilib_get_value_as_pyobject(self->ctx, &val, NULL); +} + +PyObject* Pcipywrap_set_property(Pcipywrap *self, PyObject* val, const char *prop) +{ + int err; + + pcilib_value_t val_internal = {0}; + err = pcilib_set_value_from_pyobject(self->ctx, &val_internal, val); + if(err) + { + set_python_exception("pcilib_set_value_from_pyobject, (error %i)", err); + return NULL; + } + + err = pcilib_set_property(self->ctx, prop, &val_internal); + if(err) + { + set_python_exception("pcilib_set_property, (error %i)", err); + return NULL; + } + + return PyInt_FromLong((long)1); +} + +PyObject* Pcipywrap_get_registers_list(Pcipywrap *self, const char *bank) +{ + pcilib_register_info_t *list = pcilib_get_register_list(self->ctx, bank, PCILIB_LIST_FLAGS_DEFAULT); PyObject* pyList = PyList_New(0); - for(int i = 0; i < __ctx->num_reg; i++) + for(int i = 0; i < ((pcilib_t*)self->ctx)->num_reg; i++) { //serialize item attributes - PyObject* pylistItem = pcilib_convert_register_info_to_pyobject(list[i]); + PyObject* pylistItem = pcilib_convert_register_info_to_pyobject(self->ctx, list[i]); pcilib_pylist_append(pyList, pylistItem); } - pcilib_free_register_info(__ctx, list); + pcilib_free_register_info(self->ctx, list); return pyList; } -PyObject* get_register_info(const char* reg,const char *bank) +PyObject* Pcipywrap_get_register_info(Pcipywrap *self, const char* reg,const char *bank) { - if(!__ctx) - { - set_python_exception("pcilib_t handler not initialized"); - return NULL; - } - - pcilib_register_info_t *info = pcilib_get_register_info(__ctx, bank, reg, PCILIB_LIST_FLAGS_DEFAULT); + pcilib_register_info_t *info = pcilib_get_register_info(self->ctx, bank, reg, PCILIB_LIST_FLAGS_DEFAULT); if(!info) { return NULL; } - PyObject* py_info = pcilib_convert_register_info_to_pyobject(info[0]); + PyObject* py_info = pcilib_convert_register_info_to_pyobject(self->ctx, info[0]); - pcilib_free_register_info(__ctx, info); + pcilib_free_register_info(self->ctx, info); return py_info; } -PyObject* get_property_list(const char* branch) +PyObject* Pcipywrap_get_property_list(Pcipywrap *self, const char* branch) { - if(!__ctx) - { - set_python_exception("pcilib_t handler not initialized"); - return NULL; - } - - pcilib_property_info_t *list = pcilib_get_property_list(__ctx, branch, PCILIB_LIST_FLAGS_DEFAULT); + pcilib_property_info_t *list = pcilib_get_property_list(self->ctx, branch, PCILIB_LIST_FLAGS_DEFAULT); PyObject* pyList = PyList_New(0); for(int i = 0; list[i].path; i++) { //serialize item attributes - PyObject* pylistItem = pcilib_convert_property_info_to_pyobject(list[i]); + PyObject* pylistItem = pcilib_convert_property_info_to_pyobject(self->ctx, list[i]); pcilib_pylist_append(pyList, pylistItem); } - pcilib_free_property_info(__ctx, list); + pcilib_free_property_info(self->ctx, list); return pyList; } diff --git a/pywrap/pcipywrap.h b/pywrap/pcipywrap.h new file mode 100644 index 0000000..8389445 --- /dev/null +++ b/pywrap/pcipywrap.h @@ -0,0 +1,68 @@ +#ifndef PCIPYWRAP_H +#define PCIPYWRAP_H + +#include "pci.h" +#include "error.h" +#include + +typedef struct { + void* ctx; + int shared; +} Pcipywrap; + +/*! + * \brief Redirect pcilib standart log stream to exeption text. + * Logger will accumulate errors untill get message, starts with "#E". + * After that, logger will write last error, and all accumulated errors + * to Python exeption text + */ +void __redirect_logs_to_exeption(); + +/*! + * \brief Wraps for pcilib_open function. + * \param[in] fpga_device path to the device file [/dev/fpga0] + * \param[in] model specifies the model of hardware, autodetected if NULL is passed + * \return Pointer to pcilib_t, created by pcilib_open; NULL with exeption text, if failed. + */ +PyObject* create_pcilib_instance(const char *fpga_device, const char *model); + +Pcipywrap *new_Pcipywrap(const char* fpga_device, const char* model); +Pcipywrap *create_Pcipywrap(PyObject* ctx); +void delete_Pcipywrap(Pcipywrap *self); + +/*! + * \brief Reads register value. Wrap for pcilib_read_register function. + * \param[in] regname the name of the register + * \param[in] bank should specify the bank name if register with the same name may occur in multiple banks, NULL otherwise + * \return register value, can be integer or float type; NULL with exeption text, if failed. + */ +PyObject* Pcipywrap_read_register(Pcipywrap *self, const char *regname, const char *bank); + +/*! + * \brief Writes value to register. Wrap for pcilib_write_register function. + * \param[in] val Register value, that needs to be set. Can be int, float or string. + * \param[in] regname the name of the register + * \param[in] bank should specify the bank name if register with the same name may occur in multiple banks, NULL otherwise + * \return 1, serialized to PyObject or NULL with exeption text, if failed. + */ +PyObject* Pcipywrap_write_register(Pcipywrap *self, PyObject* val, const char *regname, const char *bank); + +/*! + * \brief Reads propety value. Wrap for pcilib_get_property function. + * \param[in] prop property name (full name including path) + * \return property value, can be integer or float type; NULL with exeption text, if failed. + */ +PyObject* Pcipywrap_get_property(Pcipywrap *self, const char *prop); + +/*! + * \brief Writes value to property. Wrap for pcilib_set_property function. + * \param[in] prop property name (full name including path) + * \param[in] val Property value, that needs to be set. Can be int, float or string. + * \return 1, serialized to PyObject or NULL with exeption text, if failed. + */ +PyObject* Pcipywrap_set_property(Pcipywrap *self, PyObject* val, const char *prop); +PyObject* Pcipywrap_get_registers_list(Pcipywrap *self, const char *bank); +PyObject* Pcipywrap_get_register_info(Pcipywrap *self, const char* reg,const char *bank); +PyObject* Pcipywrap_get_property_list(Pcipywrap *self, const char* branch); + +#endif /* PCIPYWRAP_H */ diff --git a/pywrap/pcipywrap.i b/pywrap/pcipywrap.i index 952e145..88a746f 100644 --- a/pywrap/pcipywrap.i +++ b/pywrap/pcipywrap.i @@ -6,18 +6,20 @@ extern void __redirect_logs_to_exeption(); -extern PyObject* create_pcilib_instance(const char *fpga_device, const char *model = NULL); -extern PyObject* set_pcilib(PyObject* addr); -extern void close_curr_pcilib_instance(); -extern PyObject* get_curr_pcilib_instance(); - -extern PyObject* read_register(const char *regname, const char *bank = NULL); -extern PyObject* write_register(PyObject* val, const char *regname, const char *bank = NULL); - -extern PyObject* get_property(const char *prop); -extern PyObject* set_property(PyObject* val, const char *prop); - -extern PyObject* get_registers_list(const char *bank = NULL); -extern PyObject* get_register_info(const char* reg,const char *bank = NULL); - -extern PyObject* get_property_list(const char* branch = NULL); +typedef struct { + %extend { + Pcipywrap(const char* fpga_device = NULL, const char* model = NULL); + Pcipywrap(PyObject* ctx){return create_Pcipywrap(ctx);} + ~Pcipywrap(); + + PyObject* read_register(const char *regname = NULL, const char *bank = NULL); + PyObject* write_register(PyObject* val, const char *regname, const char *bank = NULL); + + PyObject* get_property(const char *prop); + PyObject* set_property(PyObject* val, const char *prop); + + PyObject* get_registers_list(const char *bank = NULL); + PyObject* get_register_info(const char* reg,const char *bank = NULL); + PyObject* get_property_list(const char* branch = NULL); + } +} Pcipywrap; diff --git a/pywrap/server.py b/pywrap/server.py index acba791..03302a2 100644 --- a/pywrap/server.py +++ b/pywrap/server.py @@ -7,7 +7,10 @@ import sys from optparse import OptionParser class PcilibServerHandler(BaseHTTPServer.BaseHTTPRequestHandler): - + def __init__(s, pcilib, *args): + s.pcilib = pcilib + BaseHTTPServer.BaseHTTPRequestHandler.__init__(s, *args) + def do_HEAD(s): s.send_response(200) s.send_header('content-type', 'application/json') @@ -58,7 +61,7 @@ class PcilibServerHandler(BaseHTTPServer.BaseHTTPRequestHandler): registers = dict() try: - registers = pcipywrap.get_registers_list(bank) + registers = s.pcilib.get_registers_list(bank) except Exception as e: s.error(str(e), data) return @@ -87,7 +90,7 @@ class PcilibServerHandler(BaseHTTPServer.BaseHTTPRequestHandler): register = dict() try: - register = pcipywrap.get_register_info(reg, bank) + register = s.pcilib.get_register_info(reg, bank) except Exception as e: s.error(str(e), data) return @@ -109,7 +112,7 @@ class PcilibServerHandler(BaseHTTPServer.BaseHTTPRequestHandler): properties = dict() try: - properties = pcipywrap.get_property_list(branch) + properties = s.pcilib.get_property_list(branch) except Exception as e: s.error(str(e), data) return @@ -138,7 +141,7 @@ class PcilibServerHandler(BaseHTTPServer.BaseHTTPRequestHandler): value = 0 try: - value = pcipywrap.read_register(reg, bank) + value = s.pcilib.read_register(reg, bank) except Exception as e: s.error(str(e), data) return @@ -172,7 +175,7 @@ class PcilibServerHandler(BaseHTTPServer.BaseHTTPRequestHandler): bank = str(bank) try: - pcipywrap.write_register(value, reg, bank) + s.pcilib.write_register(value, reg, bank) except Exception as e: s.error(str(e), data) return @@ -197,7 +200,7 @@ class PcilibServerHandler(BaseHTTPServer.BaseHTTPRequestHandler): value = 0 try: - value = pcipywrap.get_property(prop) + value = s.pcilib.get_property(prop) except Exception as e: s.error(str(e), data) return @@ -228,7 +231,7 @@ class PcilibServerHandler(BaseHTTPServer.BaseHTTPRequestHandler): value = str(data.get('value', None)) try: - pcipywrap.set_property(value, prop) + s.pcilib.set_property(value, prop) except Exception as e: s.error(str(e), data) return @@ -255,10 +258,7 @@ class PcilibServerHandler(BaseHTTPServer.BaseHTTPRequestHandler): """open device context """ def openPcilibInstance(s, device, model): - pcipywrap.close_curr_pcilib_instance() - - lib = pcipywrap.create_pcilib_instance(device, model) - pcipywrap.set_pcilib(lib) + s.pcilib = pcipywrap.create_pcilib_instance(device, model) """Send help message""" def help(s, received_message = None): @@ -350,7 +350,7 @@ if __name__ == '__main__': parser = OptionParser() parser.add_option("-p", "--port", action="store", type="int", dest="port", default=9000, - help="Set server port (8888)") + help="Set server port (9000)") parser.add_option("-d", "--device", action="store", type="string", dest="device", default=str('/dev/fpga0'), help="FPGA device (/dev/fpga0)") @@ -377,14 +377,19 @@ if __name__ == '__main__': if not 'LD_LIBRARY_PATH' in os.environ: os.environ['LD_LIBRARY_PATH'] = os.environ["APP_PATH"] + "/pcilib" - #create pcilib instance and redirect logs to exeption - lib = pcipywrap.create_pcilib_instance(opts.device, opts.model) - pcipywrap.set_pcilib(lib) + #redirect logs to exeption pcipywrap.__redirect_logs_to_exeption() #start server pcilib_server = BaseHTTPServer.HTTPServer - httpd = pcilib_server((HOST_NAME, PORT_NUMBER), PcilibServerHandler) + + #pass Pcipywrap to to server handler + lib = pcipywrap.Pcipywrap(DEVICE, MODEL) + def handler(*args): + PcilibServerHandler(lib, *args) + + httpd = pcilib_server((HOST_NAME, PORT_NUMBER), handler) + print time.asctime(), "Server Starts - %s:%s" % (HOST_NAME, PORT_NUMBER) try: httpd.serve_forever() -- cgit v1.2.3