From f428d2a4e4226d547ea1992ae9c2204446c3caee Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sat, 3 Nov 2018 13:33:36 -0700 Subject: [PATCH] rework to separate out python module Remove the magic from the magic _db* modules. Combine as one 'devsup._dbapi'. libpyDevSup exists only to bootstrap python interpreter in IOCs. --- devsupApp/src/Makefile | 32 ++- devsupApp/src/{setup.c => dbapi.c} | 327 ++++++++++++----------------- devsupApp/src/dbbase.c | 136 ------------ devsupApp/src/dbdset.c | 33 ++- devsupApp/src/dbfield.c | 9 - devsupApp/src/devsup/__init__.py | 88 +++++--- devsupApp/src/devsup/db.py | 5 +- devsupApp/src/devsup/hooks.py | 5 +- devsupApp/src/devsup/ptable.py | 4 +- devsupApp/src/pyDevSup.dbd | 26 --- devsupApp/src/pydevsup.h | 6 +- pyIocApp/Makefile | 19 ++ pyIocApp/pyDevSup.dbd | 1 + pyIocApp/setup.c | 133 ++++++++++++ test.cmd | 3 +- 15 files changed, 401 insertions(+), 426 deletions(-) rename devsupApp/src/{setup.c => dbapi.c} (67%) delete mode 100644 devsupApp/src/dbbase.c delete mode 100644 devsupApp/src/pyDevSup.dbd create mode 100644 pyIocApp/pyDevSup.dbd create mode 100644 pyIocApp/setup.c diff --git a/devsupApp/src/Makefile b/devsupApp/src/Makefile index 349b5be..55479e7 100644 --- a/devsupApp/src/Makefile +++ b/devsupApp/src/Makefile @@ -1,35 +1,31 @@ TOP=../.. include $(TOP)/configure/CONFIG -PYMODULE = NO include $(TOP)/configure/CONFIG_PY #---------------------------------------- # ADD MACRO DEFINITIONS AFTER THIS LINE #============================= -#============================= -# Build the IOC application +INSTALL_SHRLIB = $(PY_INSTALL_DIR)/devsup -LIBRARY = pyDevSup$(PY_LD_VER) +LOADABLE_LIBRARY_HOST += _dbapi -SHRLIB_VERSION = 0 +TARGETS += $(COMMON_DIR)/pyDevSupCommon.dbd +DBDDEPENDS_FILES += pyDevSupCommon.dbd$(DEP) -DBD += pyDevSup.dbd +pyDevSupCommon_DBD += base.dbd -pyDevSup$(PY_LD_VER)_SYS_LIBS += python$(PY_LD_VER) +dbapi_CPPFLAGS += -DXEPICS_ARCH=\"$(T_A)\" +dbapi_CPPFLAGS += -DXPYDEV_BASE=\"$(abspath $(INSTALL_LOCATION))\" +dbapi_CPPFLAGS += -DXEPICS_BASE=\"$(EPICS_BASE)\" +dbapi_CPPFLAGS += -DPYDIR=\"python$(PY_VER)\" -setup_CPPFLAGS += -DXEPICS_ARCH=\"$(T_A)\" -setup_CPPFLAGS += -DXPYDEV_BASE=\"$(abspath $(INSTALL_LOCATION))\" -setup_CPPFLAGS += -DXEPICS_BASE=\"$(EPICS_BASE)\" -setup_CPPFLAGS += -DPYDIR=\"python$(PY_VER)\" +_dbapi_SRCS += dbapi.c +_dbapi_SRCS += dbrec.c +_dbapi_SRCS += dbfield.c +_dbapi_SRCS += dbdset.c -pyDevSup$(PY_LD_VER)_SRCS += setup.c -pyDevSup$(PY_LD_VER)_SRCS += dbbase.c -pyDevSup$(PY_LD_VER)_SRCS += dbrec.c -pyDevSup$(PY_LD_VER)_SRCS += dbfield.c -pyDevSup$(PY_LD_VER)_SRCS += dbdset.c - -pyDevSup$(PY_LD_VER)_LIBS += $(EPICS_BASE_IOC_LIBS) +_dbapi_SRCS += pyDevSupCommon_registerRecordDeviceDriver.cpp PY += devsup/__init__.py PY += devsup/_nullapi.py diff --git a/devsupApp/src/setup.c b/devsupApp/src/dbapi.c similarity index 67% rename from devsupApp/src/setup.c rename to devsupApp/src/dbapi.c index 9b4ad51..6632901 100644 --- a/devsupApp/src/setup.c +++ b/devsupApp/src/dbapi.c @@ -1,6 +1,3 @@ -/* Global interpreter setup - */ - /* python has its own ideas about which version to support */ #undef _POSIX_C_SOURCE #undef _XOPEN_SOURCE @@ -21,9 +18,13 @@ #include #include #include +#include +#include #include "pydevsup.h" +extern int pyDevSupCommon_registerRecordDeviceDriver(DBBASE *pbase); + typedef struct { const initHookState state; const char * const name; @@ -102,6 +103,20 @@ void pyfile(const char* file) PyGILState_Release(state); } + + +static const iocshArg argCode = {"python code", iocshArgString}; +static const iocshArg argFile = {"file", iocshArgString}; + +static const iocshArg* const codeArgs[] = {&argCode}; +static const iocshArg* const fileArgs[] = {&argFile}; + +static const iocshFuncDef codeDef = {"py", 1, codeArgs}; +static const iocshFuncDef fileDef = {"pyfile", 1, fileArgs}; + +static void codeRun(const iocshArgBuf *args){py(args[0].sval);} +static void fileRun(const iocshArgBuf *args){pyfile(args[0].sval);} + initHookState pyInitLastState = (initHookState)-1; static void pyhook(initHookState state) @@ -137,28 +152,127 @@ fail: PyGILState_Release(gilstate); } +static +PyObject *py_iocsh(PyObject *unused, PyObject *args, PyObject *kws) +{ + int ret; + static char* names[] = {"script", "cmd", NULL}; + char *script=NULL, *cmd=NULL; + + if(!PyArg_ParseTupleAndKeywords(args, kws, "|ss", names, &script, &cmd)) + return NULL; + + if(!(!script ^ !cmd)) { + PyErr_SetString(PyExc_ValueError, "iocsh requires a script file name or command string"); + return NULL; + } + + Py_BEGIN_ALLOW_THREADS { + if(script) + ret = iocsh(script); + else + ret = iocshCmd(cmd); + } Py_END_ALLOW_THREADS + + return PyInt_FromLong(ret); +} + +static +PyObject *py_dbReadDatabase(PyObject *unused, PyObject *args, PyObject *kws) +{ + long status; + static char* names[] = {"name", "fp", "path", "sub", NULL}; + char *fname=NULL, *path=NULL, *sub=NULL; + int fd=-1; + + if(!PyArg_ParseTupleAndKeywords(args, kws, "|siss", names, &fname, &fd, &path, &sub)) + return NULL; + + if(!((!fname) ^ (fd<0))) { + PyErr_SetString(PyExc_ValueError, "dbReadDatabase requires a file name or descriptor"); + return NULL; + } + + Py_BEGIN_ALLOW_THREADS { + if(fname) + status = dbReadDatabase(&pdbbase, fname, path, sub); + else { + FILE *ff = fdopen(fd, "r"); + status = dbReadDatabaseFP(&pdbbase, ff, path, sub); + // dbReadDatabaseFP() has called fclose() + } + } Py_END_ALLOW_THREADS + + if(status) { + char buf[30]; + errSymLookup(status, buf, sizeof(buf)); + PyErr_SetString(PyExc_RuntimeError, buf); + return NULL; + } + Py_RETURN_NONE; +} + +static +PyObject *py_iocInit(PyObject *unused) +{ + Py_BEGIN_ALLOW_THREADS { + iocInit(); + } Py_END_ALLOW_THREADS + + Py_RETURN_NONE; +} + +static +PyObject *py_pyDevSupCommon(PyObject *unused) +{ + Py_BEGIN_ALLOW_THREADS { + pyDevSupCommon_registerRecordDeviceDriver(pdbbase); + } Py_END_ALLOW_THREADS + + Py_RETURN_NONE; +} + +static struct PyMethodDef dbapimethod[] = { + {"iocsh", (PyCFunction)py_iocsh, METH_VARARGS|METH_KEYWORDS, + "Execute IOC shell script or command"}, + {"dbReadDatabase", (PyCFunction)py_dbReadDatabase, METH_VARARGS|METH_KEYWORDS, + "Load EPICS database file"}, + {"iocInit", (PyCFunction)py_iocInit, METH_NOARGS, + "Initialize IOC"}, + {"_dbd_setup", (PyCFunction)pyDBD_setup, METH_NOARGS, ""}, + {"_dbd_rrd_base", (PyCFunction)py_pyDevSupCommon, METH_NOARGS, ""}, + {"_dbd_cleanup", (PyCFunction)pyDBD_cleanup, METH_NOARGS, ""}, + {NULL} +}; + + #if PY_MAJOR_VERSION >= 3 static struct PyModuleDef dbapimodule = { PyModuleDef_HEAD_INIT, - "_dbapi", + "devsup._dbapi", NULL, -1, - NULL + &dbapimethod }; #endif -/* initialize "magic" builtin module */ PyMODINIT_FUNC init_dbapi(void) { - PyObject *mod = NULL, *hookdict; + PyObject *mod = NULL, *hookdict, *vertup, *obj; pystate *st; + pyDevReasonID = epicsThreadPrivateCreate(); + + iocshRegister(&codeDef, &codeRun); + iocshRegister(&fileDef, &fileRun); + initHookRegister(&pyhook); + import_array(); #if PY_MAJOR_VERSION >= 3 mod = PyModule_Create(&dbapimodule); #else - mod = Py_InitModule("_dbapi", NULL); + mod = Py_InitModule("devsup._dbapi", dbapimethod); #endif if(!mod) goto fail; @@ -178,43 +292,6 @@ PyMODINIT_FUNC init_dbapi(void) } } - if(pyField_prepare(mod)) - goto fail; - if(pyRecord_prepare(mod)) - goto fail; - - MODINIT_RET(mod); - -fail: - fprintf(stderr, "Failed to initialize builtin _dbapi module!\n"); - Py_XDECREF(mod); - MODINIT_RET(NULL); -} - - -#if PY_MAJOR_VERSION >= 3 -static struct PyModuleDef constantsmodule = { - PyModuleDef_HEAD_INIT, - "_dbconstants", - NULL, - -1, - NULL -}; -#endif - -/* initialize "magic" builtin module */ -PyMODINIT_FUNC init_dbconstants(void) -{ - PyObject *mod = NULL, *vertup; - -#if PY_MAJOR_VERSION >= 3 - mod = PyModule_Create(&constantsmodule); -#else - mod = Py_InitModule("_dbconstants", NULL); -#endif - if(!mod) - MODINIT_RET(NULL); - PyModule_AddIntMacro(mod, NO_ALARM); PyModule_AddIntMacro(mod, MINOR_ALARM); PyModule_AddIntMacro(mod, MAJOR_ALARM); @@ -277,150 +354,24 @@ PyMODINIT_FUNC init_dbconstants(void) if(vertup) PyModule_AddObject(mod, "pydevver", vertup); +#if PY_MAJOR_VERSION >= 3 || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION>=7) + obj = PyCapsule_New(pdbbase, "pdbbase", NULL); +#else + obj = PyCObject_FromVoidPtrAndDesc(pdbbase, "pdbbase", NULL); +#endif + if(!obj) + goto fail; + PyModule_AddObject(mod, "pdbbase", obj); + + if(pyField_prepare(mod)) + goto fail; + if(pyRecord_prepare(mod)) + goto fail; + MODINIT_RET(mod); -} -static void cleanupPy(void *junk) -{ - PyThreadState *state = PyGILState_GetThisThreadState(); - - PyEval_RestoreThread(state); - - /* special "fake" hook for shutdown */ - pyhook((initHookState)9999); - - pyDBD_cleanup(); - - pyField_cleanup(); - - Py_Finalize(); - - epicsThreadPrivateDelete(pyDevReasonID); -} - -/* Initialize the interpreter environment - */ -static void setupPyInit(void) -{ - PyImport_AppendInittab("_dbapi", init_dbapi); - PyImport_AppendInittab("_dbconstants", init_dbconstants); - PyImport_AppendInittab("_dbbase", init_dbbase); - - Py_Initialize(); - PyEval_InitThreads(); - - (void)PyEval_SaveThread(); - - epicsAtExit(&cleanupPy, NULL); -} - -static void extendPath(PyObject *list, - const char *base, - const char *archdir) -{ - PyObject *mod, *ret; - - mod = PyImport_ImportModule("os.path"); - if(!mod) - return; - - ret = PyObject_CallMethod(mod, "join", "sss", base, PYDIR, archdir); - if(ret && !PySequence_Contains(list, ret)) { - PyList_Insert(list, 0, ret); - } - Py_XDECREF(ret); - Py_DECREF(mod); - if(PyErr_Occurred()) { - PyErr_Print(); - PyErr_Clear(); - } -} - -static void insertDefaultPath(PyObject *list) -{ - const char *basedir, *pydevdir, *top, *arch; - - basedir = getenv("EPICS_BASE"); - if(!basedir) - basedir = XEPICS_BASE; - pydevdir = getenv("PYDEV_BASE"); - if(!pydevdir) - pydevdir = XPYDEV_BASE; - top = getenv("TOP"); - arch = getenv("ARCH"); - if(!arch) - arch = XEPICS_ARCH; - - assert(PyList_Check(list)); - assert(PySequence_Check(list)); - extendPath(list, basedir, arch); - extendPath(list, pydevdir, arch); - if(top) - extendPath(list, top, arch); -} - -static void setupPyPath(void) -{ - PyObject *mod, *path = NULL; - - mod = PyImport_ImportModule("sys"); - if(mod) - path = PyObject_GetAttrString(mod, "path"); +fail: + fprintf(stderr, "Failed to initialize builtin _dbapi module!\n"); Py_XDECREF(mod); - - if(path) { - PyObject *cur; - char cwd[PATH_MAX]; - - insertDefaultPath(path); - - /* prepend current directory */ - if(getcwd(cwd, sizeof(cwd)-1)) { - cwd[sizeof(cwd)-1] = '\0'; - cur = PyString_FromString(cwd); - if(cur) - PyList_Insert(path, 0, cur); - Py_XDECREF(cur); - } - } - Py_XDECREF(path); + MODINIT_RET(NULL); } - - -#include - -static const iocshArg argCode = {"python code", iocshArgString}; -static const iocshArg argFile = {"file", iocshArgString}; - -static const iocshArg* const codeArgs[] = {&argCode}; -static const iocshArg* const fileArgs[] = {&argFile}; - -static const iocshFuncDef codeDef = {"py", 1, codeArgs}; -static const iocshFuncDef fileDef = {"pyfile", 1, fileArgs}; - -static void codeRun(const iocshArgBuf *args){py(args[0].sval);} -static void fileRun(const iocshArgBuf *args){pyfile(args[0].sval);} - -static void pySetupReg(void) -{ - PyGILState_STATE state; - - pyDevReasonID = epicsThreadPrivateCreate(); - - setupPyInit(); - iocshRegister(&codeDef, &codeRun); - iocshRegister(&fileDef, &fileRun); - initHookRegister(&pyhook); - - state = PyGILState_Ensure(); - init_dbapi(); - setupPyPath(); - if(PyErr_Occurred()) { - PyErr_Print(); - PyErr_Clear(); - } - PyGILState_Release(state); -} - -#include -epicsExportRegistrar(pySetupReg); diff --git a/devsupApp/src/dbbase.c b/devsupApp/src/dbbase.c deleted file mode 100644 index 5d9e2f1..0000000 --- a/devsupApp/src/dbbase.c +++ /dev/null @@ -1,136 +0,0 @@ - -/* python has its own ideas about which version to support */ -#undef _POSIX_C_SOURCE -#undef _XOPEN_SOURCE - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "pydevsup.h" - -static -PyObject *py_iocsh(PyObject *unused, PyObject *args, PyObject *kws) -{ - int ret; - static char* names[] = {"script", "cmd", NULL}; - char *script=NULL, *cmd=NULL; - - if(!PyArg_ParseTupleAndKeywords(args, kws, "|ss", names, &script, &cmd)) - return NULL; - - if(!(!script ^ !cmd)) { - PyErr_SetString(PyExc_ValueError, "iocsh requires a script file name or command string"); - return NULL; - } - - Py_BEGIN_ALLOW_THREADS { - if(script) - ret = iocsh(script); - else - ret = iocshCmd(cmd); - } Py_END_ALLOW_THREADS - - return PyInt_FromLong(ret); -} - -static -PyObject *py_dbReadDatabase(PyObject *unused, PyObject *args, PyObject *kws) -{ - long status; - static char* names[] = {"name", "fp", "path", "sub", NULL}; - char *fname=NULL, *path=NULL, *sub=NULL; - int fd=-1; - - if(!PyArg_ParseTupleAndKeywords(args, kws, "|siss", names, &fname, &fd, &path, &sub)) - return NULL; - - if(!((!fname) ^ (fd<0))) { - PyErr_SetString(PyExc_ValueError, "dbReadDatabase requires a file name or descriptor"); - return NULL; - } - - Py_BEGIN_ALLOW_THREADS { - if(fname) - status = dbReadDatabase(&pdbbase, fname, path, sub); - else { - FILE *ff = fdopen(fd, "r"); - status = dbReadDatabaseFP(&pdbbase, ff, path, sub); - fclose(ff); - } - } Py_END_ALLOW_THREADS - - if(status) { - char buf[30]; - errSymLookup(status, buf, sizeof(buf)); - PyErr_SetString(PyExc_RuntimeError, buf); - return NULL; - } - Py_RETURN_NONE; -} - -static -PyObject *py_iocInit(PyObject *unused) -{ - Py_BEGIN_ALLOW_THREADS { - iocInit(); - } Py_END_ALLOW_THREADS - - Py_RETURN_NONE; -} - -static struct PyMethodDef dbbasemethods[] = { - {"iocsh", (PyCFunction)py_iocsh, METH_VARARGS|METH_KEYWORDS, - "Execute IOC shell script or command"}, - {"dbReadDatabase", (PyCFunction)py_dbReadDatabase, METH_VARARGS|METH_KEYWORDS, - "Load EPICS database file"}, - {"iocInit", (PyCFunction)py_iocInit, METH_NOARGS, - "Initialize IOC"}, - {NULL} -}; - -#if PY_MAJOR_VERSION >= 3 -static struct PyModuleDef dbbasemodule = { - PyModuleDef_HEAD_INIT, - "_dbbase", - NULL, - -1, - &dbbasemethods -}; -#endif - -/* initialize "magic" builtin module */ -PyMODINIT_FUNC init_dbbase(void) -{ - PyObject *mod = NULL, *obj = NULL; - -#if PY_MAJOR_VERSION >= 3 - mod = PyModule_Create(&dbbasemodule); -#else - mod = Py_InitModule("_dbbase", dbbasemethods); -#endif - if(!mod) - goto fail; - -#if PY_MAJOR_VERSION >= 3 || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION>=7) - obj = PyCapsule_New(pdbbase, "pdbbase", NULL); -#else - obj = PyCObject_FromVoidPtrAndDesc(pdbbase, "pdbbase", NULL); -#endif - if(!obj) - goto fail; - PyModule_AddObject(mod, "pdbbase", obj); - - MODINIT_RET(mod); -fail: - Py_XDECREF(obj); - Py_XDECREF(mod); - fprintf(stderr, "Failed to initialize builtin _dbbase module!\n"); - MODINIT_RET(NULL); -} diff --git a/devsupApp/src/dbdset.c b/devsupApp/src/dbdset.c index 1ecc6cd..badfb5c 100644 --- a/devsupApp/src/dbdset.c +++ b/devsupApp/src/dbdset.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include "pydevsup.h" @@ -471,8 +473,28 @@ int canIOScanRecord(dbCommon *prec) return !!priv->scanobj; } +static +const dset* pydsets[] = { + &pydevsupComSpec.com, + &pydevsupComIn.com, + &pydevsupComOut.com, +}; + +static const char* pydsetnames[] = { + "pydevsupComSpec", + "pydevsupComIn", + "pydevsupComOut", +}; + +PyObject* pyDBD_setup(PyObject *unused) +{ + registerDevices(pdbbase, NELEMENTS(pydsets), pydsetnames, pydsets); + registryFunctionAdd("python_asub", (REGISTRYFUNCTION)&python_asub); + Py_RETURN_NONE; +} + /* Called with GIL locked */ -void pyDBD_cleanup(void) +PyObject* pyDBD_cleanup(PyObject *unused) { ELLNODE *cur; inshutdown = 1; @@ -497,12 +519,5 @@ void pyDBD_cleanup(void) free(priv); } + Py_RETURN_NONE; } - -#include - -epicsExportAddress(dset, pydevsupComSpec); -epicsExportAddress(dset, pydevsupComIn); -epicsExportAddress(dset, pydevsupComOut); - -epicsRegisterFunction(python_asub); diff --git a/devsupApp/src/dbfield.c b/devsupApp/src/dbfield.c index cf0588a..895fd75 100644 --- a/devsupApp/src/dbfield.c +++ b/devsupApp/src/dbfield.c @@ -569,12 +569,3 @@ int pyField_prepare(PyObject *module) return 0; } -void pyField_cleanup(void) -{ - size_t i; - - for(i=0; i<=DBF_MENU; i++) { - Py_XDECREF(dbf2np[i]); - dbf2np[i] = NULL; - } -} diff --git a/devsupApp/src/devsup/__init__.py b/devsupApp/src/devsup/__init__.py index 977f926..f7a6988 100644 --- a/devsupApp/src/devsup/__init__.py +++ b/devsupApp/src/devsup/__init__.py @@ -1,28 +1,68 @@ -try: - import _dbapi - HAVE_DBAPI = True -except ImportError: - import devsup._nullapi as _dbapi - HAVE_DBAPI = False +import os +import atexit +import tempfile -try: - from _dbconstants import * -except ImportError: - EPICS_VERSION_STRING = "EPICS 0.0.0.0-0" - EPICS_DEV_SNAPSHOT = "" - EPICS_SITE_VERSION = "0" - EPICS_VERSION = 0 - EPICS_REVISION = 0 - EPICS_MODIFICATION = 0 - EPICS_PATCH_LEVEL = 0 +from . import _dbapi - XEPICS_ARCH = "nullos-nullarch" - XPYDEV_BASE = "invaliddir" - XEPICS_BASE = "invaliddir" - - epicsver = (0,0,0,0,"0","") - pydevver = (0,0) - - INVALID_ALARM = UDF_ALARM = 0 +from ._dbapi import (EPICS_VERSION_STRING, + EPICS_DEV_SNAPSHOT, + EPICS_SITE_VERSION, + EPICS_VERSION, + EPICS_REVISION, + EPICS_MODIFICATION, + EPICS_PATCH_LEVEL, + XEPICS_ARCH, + XPYDEV_BASE, + XEPICS_BASE, + epicsver, + pydevver, + INVALID_ALARM, + UDF_ALARM, + ) __all__ = [] + +_ready = [False] + +def _init(iocMain=False): + if _ready[0]: + return + _ready[0] = True + + if not iocMain: + # we haven't read/register base.dbd + _dbapi.dbReadDatabase(os.path.join(XEPICS_BASE, "dbd", "base.dbd")) + _dbapi._dbd_rrd_base() + + with tempfile.NamedTemporaryFile() as F: + F.write(""" +device(longin, INST_IO, pydevsupComIn, "Python Device") +device(longout, INST_IO, pydevsupComOut, "Python Device") + +device(ai, INST_IO, pydevsupComIn, "Python Device") +device(ao, INST_IO, pydevsupComOut, "Python Device") + +device(stringin, INST_IO, pydevsupComIn, "Python Device") +device(stringout, INST_IO, pydevsupComOut, "Python Device") + +device(bi, INST_IO, pydevsupComIn, "Python Device") +device(bo, INST_IO, pydevsupComOut, "Python Device") + +device(mbbi, INST_IO, pydevsupComIn, "Python Device") +device(mbbo, INST_IO, pydevsupComOut, "Python Device") + +device(mbbiDirect, INST_IO, pydevsupComIn, "Python Device") +device(mbboDirect, INST_IO, pydevsupComOut, "Python Device") + +device(waveform, INST_IO, pydevsupComIn, "Python Device") +device(aai, INST_IO, pydevsupComIn, "Python Device") +device(aao, INST_IO, pydevsupComOut, "Python Device") +""") + F.flush() + _dbapi.dbReadDatabase(F.name) + _dbapi._dbd_setup() + +@atexit.register +def _fini(): + print("ATEXIT") + _dbapi._dbd_cleanup() diff --git a/devsupApp/src/devsup/db.py b/devsupApp/src/devsup/db.py index f36d2f6..f936f34 100644 --- a/devsupApp/src/devsup/db.py +++ b/devsupApp/src/devsup/db.py @@ -3,10 +3,7 @@ import threading, sys, traceback, time from devsup.util import Worker, importmod -try: - import _dbapi -except ImportError: - import _nullapi as _dbapi +from . import _dbapi _rec_cache = {} _no_such_field = object() diff --git a/devsupApp/src/devsup/hooks.py b/devsupApp/src/devsup/hooks.py index 0cc8abb..9ac261a 100644 --- a/devsupApp/src/devsup/hooks.py +++ b/devsupApp/src/devsup/hooks.py @@ -4,10 +4,7 @@ import traceback from functools import wraps from collections import defaultdict -try: - import _dbapi -except ImportError: - import devsup._nullapi as _dbapi +from . import _dbapi __all__ = [ "hooknames", diff --git a/devsupApp/src/devsup/ptable.py b/devsupApp/src/devsup/ptable.py index 24e7570..800c0e6 100644 --- a/devsupApp/src/devsup/ptable.py +++ b/devsupApp/src/devsup/ptable.py @@ -7,8 +7,8 @@ import threading, inspect _tables = {} -from devsup.db import IOScanListThread -from devsup import INVALID_ALARM, UDF_ALARM +from .db import IOScanListThread +from . import INVALID_ALARM, UDF_ALARM __all__ = [ 'Parameter', diff --git a/devsupApp/src/pyDevSup.dbd b/devsupApp/src/pyDevSup.dbd deleted file mode 100644 index 4ca815f..0000000 --- a/devsupApp/src/pyDevSup.dbd +++ /dev/null @@ -1,26 +0,0 @@ -registrar(pySetupReg) - -device(longin, INST_IO, pydevsupComIn, "Python Device") -device(longout, INST_IO, pydevsupComOut, "Python Device") - -device(ai, INST_IO, pydevsupComIn, "Python Device") -device(ao, INST_IO, pydevsupComOut, "Python Device") - -device(stringin, INST_IO, pydevsupComIn, "Python Device") -device(stringout, INST_IO, pydevsupComOut, "Python Device") - -device(bi, INST_IO, pydevsupComIn, "Python Device") -device(bo, INST_IO, pydevsupComOut, "Python Device") - -device(mbbi, INST_IO, pydevsupComIn, "Python Device") -device(mbbo, INST_IO, pydevsupComOut, "Python Device") - -device(mbbiDirect, INST_IO, pydevsupComIn, "Python Device") -device(mbboDirect, INST_IO, pydevsupComOut, "Python Device") - -device(waveform, INST_IO, pydevsupComIn, "Python Device") -device(aai, INST_IO, pydevsupComIn, "Python Device") -device(aao, INST_IO, pydevsupComOut, "Python Device") - -function(python_asub) - diff --git a/devsupApp/src/pydevsup.h b/devsupApp/src/pydevsup.h index 9b7cbf8..76bcf28 100644 --- a/devsupApp/src/pydevsup.h +++ b/devsupApp/src/pydevsup.h @@ -17,12 +17,10 @@ initHookState pyInitLastState; -PyMODINIT_FUNC init_dbbase(void); - -void pyDBD_cleanup(void); +PyObject* pyDBD_setup(PyObject *unused); +PyObject* pyDBD_cleanup(PyObject *unused); int pyField_prepare(PyObject *module); -void pyField_cleanup(void); int pyRecord_prepare(PyObject *module); diff --git a/pyIocApp/Makefile b/pyIocApp/Makefile index fe2bec3..8e41d4d 100644 --- a/pyIocApp/Makefile +++ b/pyIocApp/Makefile @@ -10,6 +10,25 @@ include $(TOP)/configure/CONFIG_PY #============================= # Build the IOC application +USR_CPPFLAGS += -I$(TOP)/devsupApp/src + +LIBRARY = pyDevSup$(PY_LD_VER) + +SHRLIB_VERSION = 0 + +DBD += pyDevSup.dbd + +pyDevSup$(PY_LD_VER)_SYS_LIBS += python$(PY_LD_VER) + +pyDevSup$(PY_LD_VER)_LIBS += $(EPICS_BASE_IOC_LIBS) + +setup_CPPFLAGS += -DXEPICS_ARCH=\"$(T_A)\" +setup_CPPFLAGS += -DXPYDEV_BASE=\"$(abspath $(INSTALL_LOCATION))\" +setup_CPPFLAGS += -DXEPICS_BASE=\"$(EPICS_BASE)\" +setup_CPPFLAGS += -DPYDIR=\"python$(PY_VER)\" + +pyDevSup$(PY_LD_VER)_SRCS += setup.c + PROD_IOC = softIocPy$(PY_VER) PRODNAME = $(addsuffix $(EXE),$(PROD)) diff --git a/pyIocApp/pyDevSup.dbd b/pyIocApp/pyDevSup.dbd new file mode 100644 index 0000000..b09ebf3 --- /dev/null +++ b/pyIocApp/pyDevSup.dbd @@ -0,0 +1 @@ +registrar(pySetupReg) diff --git a/pyIocApp/setup.c b/pyIocApp/setup.c new file mode 100644 index 0000000..263baf3 --- /dev/null +++ b/pyIocApp/setup.c @@ -0,0 +1,133 @@ +/* Global interpreter setup + */ + +/* python has its own ideas about which version to support */ +#undef _POSIX_C_SOURCE +#undef _XOPEN_SOURCE + +#include +#ifdef HAVE_NUMPY +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pydevsup.h" + +static void cleanupPy(void *junk) +{ + PyThreadState *state = PyGILState_GetThisThreadState(); + + PyEval_RestoreThread(state); + + /* special "fake" hook for shutdown */ + //pyhook((initHookState)9999); + + Py_Finalize(); // calls python atexit hooks +} + +static void extendPath(PyObject *list, + const char *base, + const char *archdir) +{ + PyObject *mod, *ret; + + mod = PyImport_ImportModule("os.path"); + if(!mod) + return; + + ret = PyObject_CallMethod(mod, "join", "sss", base, PYDIR, archdir); + if(ret && !PySequence_Contains(list, ret)) { + PyList_Insert(list, 0, ret); + } + Py_XDECREF(ret); + Py_DECREF(mod); + if(PyErr_Occurred()) { + PyErr_Print(); + PyErr_Clear(); + } +} + +static void insertDefaultPath(PyObject *list) +{ + const char *basedir, *pydevdir, *top, *arch; + + basedir = getenv("EPICS_BASE"); + if(!basedir) + basedir = XEPICS_BASE; + pydevdir = getenv("PYDEV_BASE"); + if(!pydevdir) + pydevdir = XPYDEV_BASE; + top = getenv("TOP"); + arch = getenv("ARCH"); + if(!arch) + arch = XEPICS_ARCH; + + assert(PyList_Check(list)); + assert(PySequence_Check(list)); + extendPath(list, basedir, arch); + extendPath(list, pydevdir, arch); + if(top) + extendPath(list, top, arch); +} + +static void setupPyPath(void) +{ + PyObject *mod, *path = NULL; + + mod = PyImport_ImportModule("sys"); + if(mod) + path = PyObject_GetAttrString(mod, "path"); + Py_XDECREF(mod); + + if(path) { + PyObject *cur; + char cwd[PATH_MAX]; + + insertDefaultPath(path); + + /* prepend current directory */ + if(getcwd(cwd, sizeof(cwd)-1)) { + cwd[sizeof(cwd)-1] = '\0'; + cur = PyString_FromString(cwd); + if(cur) + PyList_Insert(path, 0, cur); + Py_XDECREF(cur); + } + } + Py_XDECREF(path); +} + +static void pySetupReg(void) +{ + PyGILState_STATE state; + + Py_Initialize(); + PyEval_InitThreads(); + + setupPyPath(); + + if(PyRun_SimpleString("import devsup\n" + "devsup._init(True)\n" + )) { + PyErr_Print(); + PyErr_Clear(); + } + + (void)PyEval_SaveThread(); + + epicsAtExit(&cleanupPy, NULL); +} + +#include +epicsExportRegistrar(pySetupReg); diff --git a/test.cmd b/test.cmd index 210dcdf..f1ad20e 100644 --- a/test.cmd +++ b/test.cmd @@ -3,7 +3,6 @@ py "import logging" py "logging.basicConfig(level=logging.DEBUG)" -py "import devsup; print devsup.HAVE_DBAPI" py "import sys; sys.path.insert(0,'${PWD}/testApp')" py "print sys.path" @@ -23,4 +22,4 @@ dbLoadRecords("db/test6.db","P=tst:,TNAME=tsum") iocInit() # Start Reference tracker -py "from devsup import disect; disect.periodic(10)" +#py "from devsup import disect; disect.periodic(10)"