From e728a5bc3db296b4b67c2d3e5b56558c42c566a8 Mon Sep 17 00:00:00 2001 From: Thomas Grill Date: Mon, 21 Jun 2004 14:08:57 +0000 Subject: "" svn path=/trunk/; revision=1826 --- externals/grill/py/source/bound.cpp | 10 ++- externals/grill/py/source/clmeth.cpp | 4 +- externals/grill/py/source/main.cpp | 159 +++++++++++++++------------------- externals/grill/py/source/main.h | 26 +++--- externals/grill/py/source/modmeth.cpp | 10 ++- externals/grill/py/source/py.cpp | 4 +- externals/grill/py/source/pyargs.cpp | 12 +-- externals/grill/py/source/pyext.cpp | 115 +++++++++++------------- externals/grill/py/source/pyext.h | 5 +- 9 files changed, 164 insertions(+), 181 deletions(-) (limited to 'externals/grill/py/source') diff --git a/externals/grill/py/source/bound.cpp b/externals/grill/py/source/bound.cpp index 928d7b67..9578e4c0 100644 --- a/externals/grill/py/source/bound.cpp +++ b/externals/grill/py/source/bound.cpp @@ -13,10 +13,12 @@ WARRANTIES, see the file, "license.txt," in this distribution. #include +typedef std::set FuncSet; + struct bounddata { PyObject *self; - std::set funcs; + FuncSet funcs; }; bool pyext::boundmeth(flext_base *,t_symbol *sym,int argc,t_atom *argv,void *data) @@ -25,10 +27,10 @@ bool pyext::boundmeth(flext_base *,t_symbol *sym,int argc,t_atom *argv,void *dat PY_LOCK - PyObject *args = MakePyArgs(sym,AtomList(argc,argv),-1,obj->self != NULL); + PyObject *args = MakePyArgs(sym,argc,argv,-1,obj->self != NULL); // call all functions bound by this symbol - for(std::set::iterator it = obj->funcs.begin(); it != obj->funcs.end(); ++it) { + for(FuncSet::iterator it = obj->funcs.begin(); it != obj->funcs.end(); ++it) { PyObject *ret = PyObject_CallObject(*it,args); if(!ret) { PyErr_Print(); @@ -135,7 +137,7 @@ V pyext::ClearBinding() while(GetThis(pyobj)->UnbindMethod(sym,NULL,&data)) { bounddata *bdt = (bounddata *)data; if(bdt) { - for(std::set::iterator it = bdt->funcs.begin(); it != bdt->funcs.end(); ++it) { + for(FuncSet::iterator it = bdt->funcs.begin(); it != bdt->funcs.end(); ++it) { PyObject *func = *it; if(PyMethod_Check(func)) Py_DECREF(func); } diff --git a/externals/grill/py/source/clmeth.cpp b/externals/grill/py/source/clmeth.cpp index a17c010a..d3b909ff 100644 --- a/externals/grill/py/source/clmeth.cpp +++ b/externals/grill/py/source/clmeth.cpp @@ -161,8 +161,10 @@ PyObject *pyext::pyext_outlet(PyObject *,PyObject *args) // deadlock would occur if this was another py/pyext object! if(lst->Count() && IsSymbol((*lst)[0])) ext->ToQueueAnything(o-1,GetSymbol((*lst)[0]),lst->Count()-1,lst->Atoms()+1); +// ext->ToOutAnything(o-1,GetSymbol((*lst)[0]),lst->Count()-1,lst->Atoms()+1); else ext->ToQueueList(o-1,*lst); +// ext->ToOutList(o-1,*lst); } else post("pyext: outlet index out of range"); @@ -230,7 +232,7 @@ PyObject *pyext::pyext_stop(PyObject *,PyObject *args) //! Query whether threading is enabled PyObject *pyext::pyext_isthreaded(PyObject *,PyObject *) { - return Py_BuildValue("i", + return PyInt_FromLong( #ifdef FLEXT_THREADED 1 #else diff --git a/externals/grill/py/source/main.cpp b/externals/grill/py/source/main.cpp index e552bc5a..0434f682 100644 --- a/externals/grill/py/source/main.cpp +++ b/externals/grill/py/source/main.cpp @@ -10,82 +10,83 @@ WARRANTIES, see the file, "license.txt," in this distribution. #include "main.h" + +static PyMethodDef StdOut_Methods[] = +{ + { "write", py::StdOut_Write, 1 }, + { NULL, NULL, } +}; + V py::lib_setup() { post(""); post("py/pyext %s - python script objects, (C)2002-2004 Thomas Grill",PY__VERSION); post(""); + // ------------------------------------------------------------- + + Py_Initialize(); + +#if 0 //def FLEXT_DEBUG + Py_DebugFlag = 1; + Py_VerboseFlag = 1; +#endif + +#ifdef FLEXT_THREADS + // enable thread support and acquire the global thread lock + PyEval_InitThreads(); + + // get thread state + pythrmain = PyThreadState_Get(); + // get main interpreter state + pystate = pythrmain->interp; + + // add thread state of main thread to map + pythrmap[GetThreadId()] = pythrmain; +#endif + + // register/initialize pyext module only once! + module_obj = Py_InitModule(PYEXT_MODULE, func_tbl); + module_dict = PyModule_GetDict(module_obj); // borrowed reference + + PyModule_AddStringConstant(module_obj,"__doc__",(C *)py_doc); + + // redirect stdout + PyObject* py_out = Py_InitModule("stdout", StdOut_Methods); + PySys_SetObject("stdout", py_out); + +#ifdef FLEXT_THREADS + // release global lock + PyEval_ReleaseLock(); +#endif + + // ------------------------------------------------------------- + FLEXT_SETUP(pyobj); FLEXT_SETUP(pyext); - - pyref = 0; } FLEXT_LIB_SETUP(py,py::lib_setup) -PyInterpreterState *py::pystate = NULL; #ifdef FLEXT_THREADS -std::map py::pythrmap; +PyInterpreterState *py::pystate = NULL; +PyThreadState *py::pythrmain = NULL; +PyThrMap py::pythrmap; #endif -I py::pyref = 0; PyObject *py::module_obj = NULL; PyObject *py::module_dict = NULL; -static PyMethodDef StdOut_Methods[] = -{ - { "write", py::StdOut_Write, 1 }, - { NULL, NULL, } -}; - - py::py(): module(NULL), detach(false),shouldexit(false),thrcount(0), stoptick(0) { - Lock(); - - if(!(pyref++)) { - Py_Initialize(); - - #ifdef FLEXT_THREADS - // enable thread support and acquire the global thread lock - PyEval_InitThreads(); - - // get thread state - PyThreadState *pythrmain = PyThreadState_Get(); - // get main interpreter state - pystate = pythrmain->interp; - - // release global lock - PyEval_ReleaseLock(); - - // add thread state of main thread to map - pythrmap[GetThreadId()] = pythrmain; - #endif - - // register/initialize pyext module only once! - module_obj = Py_InitModule(PYEXT_MODULE, func_tbl); - module_dict = PyModule_GetDict(module_obj); - - PyModule_AddStringConstant(module_obj,"__doc__",(C *)py_doc); - - // redirect stdout - PyObject* py_out = Py_InitModule("stdout", StdOut_Methods); - PySys_SetObject("stdout", py_out); - } - else { - PY_LOCK - Py_INCREF(module_obj); - Py_INCREF(module_dict); - PY_UNLOCK - } - - Unlock(); + PY_LOCK + Py_INCREF(module_obj); + PY_UNLOCK FLEXT_ADDTIMER(stoptmr,tick); } @@ -104,38 +105,7 @@ py::~py() post("%s - Okay, all threads have terminated",thisName()); } - Lock(); - - if(!(--pyref)) { - // no more py/pyext objects left... shut down Python - - module_obj = NULL; - module_dict = NULL; - - Py_XDECREF(module); - - PyEval_AcquireLock(); - -#ifdef FLEXT_THREADS - PyThreadState_Swap(pythrmap[GetThreadId()]); -#endif - -#if 0 //def FLEXT_DEBUG - // need not necessarily do that.... - Py_Finalize(); -#endif - -#ifdef FLEXT_THREADS - // reset thread state map - pythrmap.clear(); -#endif - } - else { - Py_DECREF(module_obj); - Py_DECREF(module_dict); - } - - Unlock(); + Py_XDECREF(module_obj); } @@ -229,16 +199,31 @@ V py::ImportModule(const C *name) { if(!name) return; - module = PyImport_ImportModule((C *)name); + module = PyImport_ImportModule((C *)name); // increases module_obj ref count by one if (!module) { + PyErr_Print(); dict = NULL; } else - dict = PyModule_GetDict(module); // borrowed - + dict = PyModule_GetDict(module); } +V py::UnimportModule() +{ + if(!module) return; + + assert(dict && module_obj && module_dict); + + Py_DECREF(module); + + // reference count to module is not 0 here, altough probably the last instance was unloaded + // Python retains one reference to the module all the time + // we don't care + + module = NULL; + dict = NULL; +} V py::ReloadModule() { @@ -262,7 +247,7 @@ V py::ReloadModule() V py::GetModulePath(const C *mod,C *dir,I len) { #if FLEXT_SYS == FLEXT_SYS_PD - // uarghh... pd doesn't show it's path for extra modules + // uarghh... pd doesn't show its path for extra modules C *name; I fd = open_via_path("",mod,".py",dir,&name,len,0); diff --git a/externals/grill/py/source/main.h b/externals/grill/py/source/main.h index d6ca52cb..2e8419d3 100644 --- a/externals/grill/py/source/main.h +++ b/externals/grill/py/source/main.h @@ -50,6 +50,8 @@ WARRANTIES, see the file, "license.txt," in this distribution. #include "main.h" +typedef std::map PyThrMap; + class py: public flext_base { @@ -60,7 +62,7 @@ public: ~py(); static V lib_setup(); - static PyObject *MakePyArgs(const t_symbol *s,const AtomList &args,I inlet = -1,BL withself = false); + static PyObject *MakePyArgs(const t_symbol *s,int argc,const t_atom *argv,I inlet = -1,BL withself = false); static AtomList *GetPyArgs(PyObject *pValue,PyObject **self = NULL); protected: @@ -74,7 +76,6 @@ protected: PyObject *module,*dict; // inherited user class module and associated dictionary - static I pyref; static const C *py_doc; V GetDir(PyObject *obj,AtomList &lst); @@ -83,6 +84,7 @@ protected: V AddToPath(const C *dir); V SetArgs(I argc,const t_atom *argv); V ImportModule(const C *name); + V UnimportModule(); V ReloadModule(); V Register(const C *reg); @@ -124,16 +126,17 @@ protected: V tick(V *); public: - static PyInterpreterState *pystate; #ifdef FLEXT_THREADS - static std::map pythrmap; + static PyInterpreterState *pystate; + static PyThreadState *pythrmain; + static PyThrMap pythrmap; ThrMutex mutex; - V Lock() { mutex.Unlock(); } - V Unlock() { mutex.Unlock(); } + inline V Lock() { mutex.Unlock(); } + inline V Unlock() { mutex.Unlock(); } #else - V Lock() {} - V Unlock() {} + inline V Lock() {} + inline V Unlock() {} #endif static PyObject* StdOut_Write(PyObject* Self, PyObject* Args); @@ -152,11 +155,14 @@ protected: #ifdef FLEXT_THREADS +// if thread is not found in the thread map, the state of the system thread is used +// we have yet to see if this has bad side-effects + #define PY_LOCK \ { \ PyEval_AcquireLock(); \ - PyThreadState *__st = pythrmap[GetThreadId()]; \ - FLEXT_ASSERT(__st != NULL); \ + PyThrMap::iterator it = pythrmap.find(GetThreadId()); \ + PyThreadState *__st = it != pythrmap.end()?it->second:pythrmain; \ PyThreadState *__oldst = PyThreadState_Swap(__st); #define PY_UNLOCK \ diff --git a/externals/grill/py/source/modmeth.cpp b/externals/grill/py/source/modmeth.cpp index f7dae309..b3a98c98 100644 --- a/externals/grill/py/source/modmeth.cpp +++ b/externals/grill/py/source/modmeth.cpp @@ -142,11 +142,15 @@ PyObject *py::py_send(PyObject *,PyObject *args) AtomList *lst = GetPyArgs(val); if(lst) { - if(!Forward(recv,*lst)) + bool ok; + if(lst->Count() && IsSymbol((*lst)[0])) + ok = Forward(recv,GetSymbol((*lst)[0]),lst->Count()-1,lst->Atoms()+1); + else + ok = Forward(recv,*lst); + #ifdef FLEXT_DEBUG + if(!ok) post("py/pyext - Receiver doesn't exist"); -#else - {} #endif } else diff --git a/externals/grill/py/source/py.cpp b/externals/grill/py/source/py.cpp index e7524ce1..8b14a485 100644 --- a/externals/grill/py/source/py.cpp +++ b/externals/grill/py/source/py.cpp @@ -2,7 +2,7 @@ py/pyext - python script object for PD and Max/MSP -Copyright (c) 2002-2004 Thomas Grill (xovo@gmx.net) +Copyright (c)2002-2004 Thomas Grill (xovo@gmx.net) For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "license.txt," in this distribution. @@ -297,7 +297,7 @@ BL pyobj::work(const t_symbol *s,I argc,const t_atom *argv) PY_LOCK if(function) { - PyObject *pArgs = MakePyArgs(s,AtomList(argc,argv)); + PyObject *pArgs = MakePyArgs(s,argc,argv); PyObject *pValue = PyObject_CallObject(function, pArgs); rargs = GetPyArgs(pValue); diff --git a/externals/grill/py/source/pyargs.cpp b/externals/grill/py/source/pyargs.cpp index 8fe68cbd..1b204273 100644 --- a/externals/grill/py/source/pyargs.cpp +++ b/externals/grill/py/source/pyargs.cpp @@ -27,7 +27,7 @@ static PyObject *MakePyAtom(const t_atom &at) return NULL; } -PyObject *py::MakePyArgs(const t_symbol *s,const AtomList &args,I inlet,BL withself) +PyObject *py::MakePyArgs(const t_symbol *s,int argc,const t_atom *argv,I inlet,BL withself) { PyObject *pArgs; @@ -41,7 +41,7 @@ PyObject *py::MakePyArgs(const t_symbol *s,const AtomList &args,I inlet,BL withs */ { BL any = IsAnything(s); - pArgs = PyTuple_New(args.Count()+(any?1:0)+(inlet >= 0?1:0)); + pArgs = PyTuple_New(argc+(any?1:0)+(inlet >= 0?1:0)); I pix = 0; @@ -54,8 +54,8 @@ PyObject *py::MakePyArgs(const t_symbol *s,const AtomList &args,I inlet,BL withs I ix; PyObject *tmp; - if(!withself || args.Count() < (any?1:2)) tmp = pArgs,ix = pix; - else tmp = PyTuple_New(args.Count()+(any?1:0)),ix = 0; + if(!withself || argc < (any?1:2)) tmp = pArgs,ix = pix; + else tmp = PyTuple_New(argc+(any?1:0)),ix = 0; if(any) { PyObject *pValue = PyString_FromString(GetString(s)); @@ -64,8 +64,8 @@ PyObject *py::MakePyArgs(const t_symbol *s,const AtomList &args,I inlet,BL withs PyTuple_SetItem(tmp, ix++, pValue); } - for(I i = 0; i < args.Count(); ++i) { - PyObject *pValue = MakePyAtom(args[i]); + for(I i = 0; i < argc; ++i) { + PyObject *pValue = MakePyAtom(argv[i]); if(!pValue) { post("py/pyext: cannot convert argument %i",any?i+1:i); continue; diff --git a/externals/grill/py/source/pyext.cpp b/externals/grill/py/source/pyext.cpp index 7dd4d805..66428054 100644 --- a/externals/grill/py/source/pyext.cpp +++ b/externals/grill/py/source/pyext.cpp @@ -34,6 +34,43 @@ V pyext::Setup(t_classid c) FLEXT_CADDMETHOD_(c,0,"set",m_set); FLEXT_CADDATTR_VAR1(c,"respond",respond); + + + // ---------------------------------------------------- + + // register/initialize pyext base class along with module + class_dict = PyDict_New(); + PyObject *className = PyString_FromString(PYEXT_CLASS); + PyMethodDef *def; + + // add setattr/getattr to class + for(def = attr_tbl; def->ml_name; def++) { + PyObject *func = PyCFunction_New(def, NULL); + PyDict_SetItemString(class_dict, def->ml_name, func); + Py_DECREF(func); + } + + class_obj = PyClass_New(NULL, class_dict, className); + Py_DECREF(className); + + // add methods to class + for (def = meth_tbl; def->ml_name != NULL; def++) { + PyObject *func = PyCFunction_New(def, NULL); + PyObject *method = PyMethod_New(func, NULL, class_obj); // increases class_obj ref count by 1 + PyDict_SetItemString(class_dict, def->ml_name, method); + Py_DECREF(func); + Py_DECREF(method); + } + +#if PY_VERSION_HEX >= 0x02020000 + // not absolutely necessary, existent in python 2.2 upwards + // make pyext functions available in class scope + PyDict_Merge(class_dict,module_dict,0); +#endif + // after merge so that it's not in class_dict as well... + PyDict_SetItemString(module_dict, PYEXT_CLASS,class_obj); // increases class_obj ref count by 1 + + PyDict_SetItemString(class_dict,"__doc__",PyString_FromString(pyext_doc)); } pyext *pyext::GetThis(PyObject *self) @@ -60,7 +97,6 @@ static short patcher_myvol(t_patcher *x) #endif -I pyext::pyextref = 0; PyObject *pyext::class_obj = NULL; PyObject *pyext::class_dict = NULL; @@ -71,53 +107,14 @@ pyext::pyext(I argc,const t_atom *argv): { int apre = 0; - PY_LOCK - - if(!pyextref++) { - // register/initialize pyext base class along with module - class_dict = PyDict_New(); - PyObject *className = PyString_FromString(PYEXT_CLASS); - PyMethodDef *def; - - // add setattr/getattr to class - for(def = attr_tbl; def->ml_name; def++) { - PyObject *func = PyCFunction_New(def, NULL); - PyDict_SetItemString(class_dict, def->ml_name, func); - Py_DECREF(func); - } - - class_obj = PyClass_New(NULL, class_dict, className); - PyDict_SetItemString(module_dict, PYEXT_CLASS,class_obj); - Py_DECREF(className); - - // add methods to class - for (def = meth_tbl; def->ml_name != NULL; def++) { - PyObject *func = PyCFunction_New(def, NULL); - PyObject *method = PyMethod_New(func, NULL, class_obj); - PyDict_SetItemString(class_dict, def->ml_name, method); - Py_DECREF(func); - Py_DECREF(method); - } - -#if PY_VERSION_HEX >= 0x02020000 - // not absolutely necessary, existent in python 2.2 upwards - // make pyext functions available in class scope - PyDict_Merge(class_dict,module_dict,0); -#endif - - PyDict_SetItemString(class_dict,"__doc__",PyString_FromString(pyext_doc)); - } - else { - Py_INCREF(class_obj); - Py_INCREF(class_dict); - } - if(argc >= apre+2 && CanbeInt(argv[apre]) && CanbeInt(argv[apre+1])) { inlets = GetAInt(argv[apre]); outlets = GetAInt(argv[apre+1]); apre += 2; } + PY_LOCK + // init script module if(argc > apre) { char dir[1024]; @@ -151,7 +148,7 @@ pyext::pyext(I argc,const t_atom *argv): ++apre; } - Register("_pyext"); + Register("_pyext"); // t_symbol *sobj = NULL; if(argc > apre) { @@ -168,7 +165,7 @@ pyext::pyext(I argc,const t_atom *argv): if(argc > apre) args(argc-apre,argv+apre); if(methname) { - SetClssMeth(); + MakeInstance(); if(inlets < 0 && outlets < 0) { // now get number of inlets and outlets @@ -222,22 +219,15 @@ pyext::~pyext() PY_LOCK ClearBinding(); - Unregister("_pyext"); + UnimportModule(); - Py_XDECREF(pyobj); - Py_XDECREF(class_obj); - Py_XDECREF(class_dict); - - if(!--pyextref) { - class_obj = NULL; - class_dict = NULL; - } + Py_XDECREF(pyobj); // opposite of SetClssMeth PY_UNLOCK } -BL pyext::SetClssMeth() //I argc,t_atom *argv) +BL pyext::MakeInstance() { // pyobj should already have been decref'd / cleared before getting here!! @@ -250,7 +240,6 @@ BL pyext::SetClssMeth() //I argc,t_atom *argv) // make instance, but don't call __init__ pyobj = PyInstance_NewRaw(pref,NULL); - Py_DECREF(pref); if(pyobj == NULL) PyErr_Print(); else { @@ -260,8 +249,9 @@ BL pyext::SetClssMeth() //I argc,t_atom *argv) // call init now, after _this has been set, which is // important for eventual callbacks from __init__ to c - PyObject *pargs = MakePyArgs(NULL,args,-1,true); - if (pargs == NULL) PyErr_Print(); + PyObject *pargs = MakePyArgs(NULL,args.Count(),args.Atoms(),-1,true); + if (pargs == NULL) + PyErr_Print(); PyObject *init; init = PyObject_GetAttrString(pyobj,"__init__"); // get ref @@ -300,7 +290,7 @@ V pyext::Reload() SetArgs(0,NULL); ReloadModule(); - SetClssMeth(); + MakeInstance(); } @@ -367,7 +357,7 @@ void pyext::m_set(int argc,const t_atom *argv) post("%s - set: Python variable %s not found",thisName(),ch); } else { - PyObject *pval = MakePyArgs(NULL,AtomList(argc-1,argv+1),-1,false); + PyObject *pval = MakePyArgs(NULL,argc-1,argv+1,-1,false); if(!pval) PyErr_Print(); @@ -438,17 +428,13 @@ PyObject *pyext::call(const C *meth,I inlet,const t_symbol *s,I argc,const t_ato PyErr_Clear(); // no method found } else { - PyObject *pargs = MakePyArgs(s,AtomList(argc,argv),inlet?inlet:-1,true); + PyObject *pargs = MakePyArgs(s,argc,argv,inlet?inlet:-1,true); if(!pargs) PyErr_Print(); else { ret = PyEval_CallObject(pmeth, pargs); if (ret == NULL) // function not found resp. arguments not matching -#ifdef FLEXT_DEBUG PyErr_Print(); -#else - PyErr_Clear(); -#endif Py_DECREF(pargs); } @@ -470,7 +456,6 @@ V pyext::work_wrapper(V *data) // --- make new Python thread --- // get the global lock PyEval_AcquireLock(); - // get a reference to the PyInterpreterState // create a thread state object for this thread PyThreadState *newthr = PyThreadState_New(pystate); // free the lock diff --git a/externals/grill/py/source/pyext.h b/externals/grill/py/source/pyext.h index 976ef0a6..d05ea7e5 100644 --- a/externals/grill/py/source/pyext.h +++ b/externals/grill/py/source/pyext.h @@ -51,7 +51,7 @@ protected: V ms_args(const AtomList &a) { m_reload_(a.Count(),a.Atoms()); } V m_dir_() { m__dir(pyobj); } V mg_dir_(AtomList &lst) { GetDir(pyobj,lst); } - V m_doc_() { m__doc(pyobj); } + V m_doc_() { m__doc(((PyInstanceObject *)pyobj)->in_class->cl_dict); } virtual V m_help(); V m_get(const t_symbol *s); @@ -66,13 +66,12 @@ private: static pyext *GetThis(PyObject *self); V ClearBinding(); - BL SetClssMeth(); //I argc,t_atom *argv); + BL MakeInstance(); AtomList args; virtual V Reload(); - static I pyextref; static PyObject *class_obj,*class_dict; static PyMethodDef attr_tbl[],meth_tbl[]; static const C *pyext_doc; -- cgit v1.2.1