diff options
-rw-r--r-- | externals/grill/py/license.txt | 2 | ||||
-rw-r--r-- | externals/grill/py/package.txt | 4 | ||||
-rw-r--r-- | externals/grill/py/pd/thread-1.pd | 47 | ||||
-rw-r--r-- | externals/grill/py/py.vcproj | 2 | ||||
-rw-r--r-- | externals/grill/py/readme.txt | 53 | ||||
-rw-r--r-- | externals/grill/py/source/bound.cpp | 15 | ||||
-rw-r--r-- | externals/grill/py/source/clmeth.cpp | 24 | ||||
-rw-r--r-- | externals/grill/py/source/main.cpp | 232 | ||||
-rw-r--r-- | externals/grill/py/source/main.h | 157 | ||||
-rw-r--r-- | externals/grill/py/source/modmeth.cpp | 44 | ||||
-rw-r--r-- | externals/grill/py/source/py.cpp | 174 | ||||
-rw-r--r-- | externals/grill/py/source/pyargs.cpp | 24 | ||||
-rw-r--r-- | externals/grill/py/source/pyext.cpp | 186 | ||||
-rw-r--r-- | externals/grill/py/source/pyext.h | 64 | ||||
-rw-r--r-- | externals/grill/py/source/register.cpp | 37 |
15 files changed, 564 insertions, 501 deletions
diff --git a/externals/grill/py/license.txt b/externals/grill/py/license.txt index 7de52136..8d768d93 100644 --- a/externals/grill/py/license.txt +++ b/externals/grill/py/license.txt @@ -28,7 +28,7 @@ This package uses the flext C++ layer - See its license text below: --- flext ---------------------------------------------- flext - C++ layer for Max/MSP and pd (pure data) externals -Copyright (C) 2001-2003 Thomas Grill +Copyright (C) 2001-2005 Thomas Grill This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/externals/grill/py/package.txt b/externals/grill/py/package.txt index 5603e6eb..03c6fa9d 100644 --- a/externals/grill/py/package.txt +++ b/externals/grill/py/package.txt @@ -1,9 +1,7 @@ NAME=py
-BUILDTYPE=multi -
+BUILDTYPE=multi
BUILDDIR=build
- SRCDIR=source
diff --git a/externals/grill/py/pd/thread-1.pd b/externals/grill/py/pd/thread-1.pd index b2daa5f0..e46f6521 100644 --- a/externals/grill/py/pd/thread-1.pd +++ b/externals/grill/py/pd/thread-1.pd @@ -1,11 +1,11 @@ -#N canvas 135 178 644 401 12;
+#N canvas 135 178 648 405 12;
#X msg 35 292 help;
#X msg 34 317 doc;
#X msg 70 318 doc+;
#X floatatom 142 340 5 0 0 0 - - -;
#X text 17 66 This demonstrates threading. See the threads.py file.
;
-#X obj 137 243 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+#X obj 137 243 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1
1;
#X msg 137 263 detach \$1;
#X floatatom 250 341 5 0 0 0 - - -;
@@ -19,31 +19,42 @@ #X obj 157 208 0;
#X text 84 114 without threads;
#X text 248 112 with threads;
-#X text 249 129 non-blocking;
#X text 175 362 watch that!;
-#X msg 411 154 stop;
-#X text 381 133 you can even stop it;
+#X msg 421 264 stop;
+#X text 391 243 you can even stop it;
#X obj 142 306 pyext threads ex1;
#X text 90 128 - blocking!! -;
#X obj 16 13 cnv 15 600 40 empty empty py/pyext 10 22 0 24 -260818
-1 0;
-#X text 213 32 http://www.parasitaere-kapazitaeten.net;
-#X text 213 16 Python script objects \, (C)2003-2004 Thomas Grill;
-#X connect 0 0 20 0;
-#X connect 1 0 20 0;
-#X connect 2 0 20 0;
+#X obj 407 150 bng 15 250 50 0 empty empty empty 0 -6 0 8 -258699 -1
+-1;
+#X obj 407 177 t b b b;
+#X text 383 112 with threads;
+#X text 249 129 non-blocking;
+#X text 384 129 parallel;
+#X obj 445 205 2;
+#X text 213 32 http://grrrr.org/ext;
+#X text 213 16 Python script objects \, (C)2003-2005 Thomas Grill;
+#X connect 0 0 19 0;
+#X connect 1 0 19 0;
+#X connect 2 0 19 0;
#X connect 5 0 6 0;
-#X connect 6 0 20 0;
+#X connect 6 0 19 0;
#X connect 8 0 11 0;
#X connect 9 0 10 0;
-#X connect 10 0 20 1;
-#X connect 10 1 20 2;
+#X connect 10 0 19 1;
+#X connect 10 1 19 2;
#X connect 10 2 13 0;
-#X connect 11 0 20 1;
-#X connect 11 1 20 2;
+#X connect 11 0 19 1;
+#X connect 11 1 19 2;
#X connect 11 2 12 0;
#X connect 12 0 5 0;
#X connect 13 0 5 0;
-#X connect 18 0 20 0;
-#X connect 20 0 3 0;
-#X connect 20 1 7 0;
+#X connect 17 0 19 0;
+#X connect 19 0 3 0;
+#X connect 19 1 7 0;
+#X connect 22 0 23 0;
+#X connect 23 0 19 1;
+#X connect 23 1 19 2;
+#X connect 23 2 27 0;
+#X connect 27 0 5 0;
diff --git a/externals/grill/py/py.vcproj b/externals/grill/py/py.vcproj index 1b04f084..c108f9a7 100644 --- a/externals/grill/py/py.vcproj +++ b/externals/grill/py/py.vcproj @@ -85,7 +85,7 @@ <Tool Name="VCCLCompilerTool" Optimization="0" - AdditionalIncludeDirectories="c:\programme\audio\pd\src;c:\data\pdmax\flext\source;"C:\data\prog\packs\Python-2.4\include";"C:\data\prog\packs\Python-2.4\PC"" + AdditionalIncludeDirectories="c:\data\prog\pd\pd-cvs\src;c:\data\pdmax\flext\source;"C:\data\prog\packs\Python-2.4\include";"C:\data\prog\packs\Python-2.4\PC"" PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;FLEXT_SYS=2;FLEXT_THREADS" BasicRuntimeChecks="3" RuntimeLibrary="1" diff --git a/externals/grill/py/readme.txt b/externals/grill/py/readme.txt index 797a6b77..b9d818d8 100644 --- a/externals/grill/py/readme.txt +++ b/externals/grill/py/readme.txt @@ -1,24 +1,16 @@ -py/pyext - python script objects for PD (and MaxMSP... once, under MacOSX and Windows) +py/pyext - python script objects for PD and Max/MSP -Copyright (c)2002-2004 Thomas Grill (gr@grrrr.org) +Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "license.txt," in this distribution. Donations for further development of the package are highly appreciated. -Visit https://www.paypal.com/xclick/business=t.grill%40gmx.net&item_name=pyext&no_note=1&tax=0¤cy_code=EUR +Visit https://www.paypal.com/xclick/business=gr%40grrrr.org&item_name=pyext&no_note=1&tax=0¤cy_code=EUR ---------------------------------------------------------------------------- You will need the flext C++ layer for PD and Max/MSP externals to compile this. -see http://www.parasitaere-kapazitaeten.net/ext - - -Package files: -- readme.txt: this one -- gpl.txt,license.txt: GPL license stuff -- main.cpp, main.h, modmeth.cpp, pyargs.cpp, register.cpp: base class -- py.cpp: py object -- pyext.cpp, pyext.h, clmeth.cpp, bound.cpp: pyext object +see http://grrrr.org/ext/flext ---------------------------------------------------------------------------- @@ -29,7 +21,6 @@ Access the flexibility of the python language in PD and MaxMSP PD - Load it as i library with e.g. "pd -lib py -path scripts" -Max/MSP - Wait for Windows or a Mach-O MacOSX version. MacOS9 doesn't want it. Check out the sample patches and scripts @@ -51,32 +42,7 @@ Known bugs: ---------------------------------------------------------------------------- The py/pyext package should run with Python version >= 2.1. -It has been thoroughly tested with version 2.2 and 2.3 - - -The package should at least compile (and is tested) with the following compilers: - - -PD @ Windows: -------------- -o Borland C++ 5.5 (free): edit "config-pd-bcc.txt" & run "build-pd-bcc.bat" - -o Microsoft Visual C++ 6/7: edit "config-pd-msvc.txt" & run "build-pd-msvc.bat" - - -PD @ linux: ------------ -o GCC: edit "config-pd-linux.txt" & run "sh build-pd-linux.sh" - - -PD @ MacOSX: ---------------------- -You'll need to have Python installed as a framework. -This is the default with Panther - otherwise, all newer Python source distributions are buildable as a darwin framework -( ./configure --enable-framework=/System/Library/Frameworks && make && make installframework ) - -o GCC: edit "config-pd-darwin.txt" & run "sh build-pd-darwin.sh" - +It has been thoroughly tested with versions 2.2 to 2.4 ---------------------------------------------------------------------------- @@ -85,6 +51,9 @@ Version history: 0.2.0: - ADD: handling of Python threads - FIX: output of single atoms instead of 1-element lists +- ADD: new detach mechanism (call queue) +- ADD: support for Max/MSP @ OSX and Windows +- DEL: eliminated meaningless inchannels and outchannels methods 0.1.4: - ADD: better (and independent) handling of inlet and outlet count (as class variables or dynamically initialized in __init__) @@ -153,13 +122,9 @@ general: features: - enable multiple interpreters? -- make a pygui object where Tkinter draws to the PD canvas... - stop individual threads - Python type for symbols +- support named (keyword) arguments (like attributes for messages) tests: - check for python threading support - -bugs: -- named (keyword) arguments are not supported - diff --git a/externals/grill/py/source/bound.cpp b/externals/grill/py/source/bound.cpp index 2504dde0..ce17578c 100644 --- a/externals/grill/py/source/bound.cpp +++ b/externals/grill/py/source/bound.cpp @@ -2,7 +2,7 @@ py/pyext - python external object for PD and MaxMSP -Copyright (c) 2002-2004 Thomas Grill (gr@grrrr.org) +Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "license.txt," in this distribution. @@ -21,11 +21,12 @@ struct bounddata FuncSet funcs; }; -bool pyext::boundmeth(flext_base *,t_symbol *sym,int argc,t_atom *argv,void *data) +bool pyext::boundmeth(flext_base *th,t_symbol *sym,int argc,t_atom *argv,void *data) { bounddata *obj = (bounddata *)data; + py *pyth = static_cast<py *>(th); - PY_LOCK + PyThreadState *state = pyth->PyLock(); PyObject *args = MakePyArgs(sym,argc,argv,-1,obj->self != NULL); @@ -40,14 +41,14 @@ bool pyext::boundmeth(flext_base *,t_symbol *sym,int argc,t_atom *argv,void *dat Py_XDECREF(args); - PY_UNLOCK + pyth->PyUnlock(state); return true; } PyObject *pyext::pyext_bind(PyObject *,PyObject *args) { PyObject *self,*meth; - C *name; + char *name; if(!PyArg_ParseTuple(args, "OsO:pyext_bind", &self,&name,&meth)) post("py/pyext - Wrong arguments!"); else if(!PyInstance_Check(self) || !(PyMethod_Check(meth) || PyFunction_Check(meth))) { @@ -91,7 +92,7 @@ PyObject *pyext::pyext_bind(PyObject *,PyObject *args) PyObject *pyext::pyext_unbind(PyObject *,PyObject *args) { PyObject *self,*meth; - C *name; + char *name; if(!PyArg_ParseTuple(args, "OsO:pyext_bind", &self,&name,&meth)) post("py/pyext - Wrong arguments!"); else if(!PyInstance_Check(self) || !(PyMethod_Check(meth) || PyFunction_Check(meth))) { @@ -127,7 +128,7 @@ PyObject *pyext::pyext_unbind(PyObject *,PyObject *args) } -V pyext::ClearBinding() +void pyext::ClearBinding() { // in case the object couldn't be constructed... if(!pyobj) return; diff --git a/externals/grill/py/source/clmeth.cpp b/externals/grill/py/source/clmeth.cpp index d437f146..8423aa4b 100644 --- a/externals/grill/py/source/clmeth.cpp +++ b/externals/grill/py/source/clmeth.cpp @@ -2,7 +2,7 @@ py/pyext - python external object for PD and Max/MSP -Copyright (c) 2002-2004 Thomas Grill (gr@grrrr.org) +Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "license.txt," in this distribution. @@ -39,8 +39,8 @@ PyMethodDef pyext::attr_tbl[] = }; -const C *pyext::pyext_doc = - "py/pyext - python external object for PD and Max/MSP, (C)2002-2004 Thomas Grill\n" +const char *pyext::pyext_doc = + "py/pyext - python external object for PD and Max/MSP, (C)2002-2005 Thomas Grill\n" "\n" "This is the pyext base class. Available methods:\n" "_outlet(self,ix,args...): Send a message to an indexed outlet\n" @@ -81,7 +81,7 @@ PyObject* pyext::pyext_setattr(PyObject *,PyObject *args) return NULL; } - BL handled = false; + bool handled = false; if(PyString_Check(name)) { char* sname = PyString_AsString(name); if (sname) { @@ -136,7 +136,7 @@ PyObject* pyext::pyext_getattr(PyObject *,PyObject *args) //! Send message to outlet PyObject *pyext::pyext_outlet(PyObject *,PyObject *args) { - BL ok = false; + bool ok = false; // should always be a tuple! FLEXT_ASSERT(PyTuple_Check(args)); @@ -150,10 +150,10 @@ PyObject *pyext::pyext_outlet(PyObject *,PyObject *args) ) { pyext *ext = GetThis(self); - I sz = PyTuple_Size(args); + int sz = PyTuple_Size(args); PyObject *val; - BL tp = + bool tp = sz == 3 && PySequence_Check( val = PyTuple_GetItem(args,2) // borrow reference @@ -164,7 +164,7 @@ PyObject *pyext::pyext_outlet(PyObject *,PyObject *args) AtomList *lst = GetPyArgs(val); if(lst) { - I o = PyInt_AsLong(outl); + int o = PyInt_AsLong(outl); if(o >= 1 && o <= ext->Outlets()) { // by using the queue there is no immediate call of the next object // deadlock would occur if this was another py/pyext object! @@ -225,7 +225,7 @@ PyObject *pyext::pyext_stop(PyObject *,PyObject *args) } else { pyext *ext = GetThis(self); - I cnt = 0; + int cnt = 0; t_atom at; if(val >= 0) flext::SetInt(at,val); ext->m_stop(cnt,&at); @@ -255,15 +255,15 @@ PyObject *pyext::pyext_tocanvas(PyObject *,PyObject *args) { FLEXT_ASSERT(PyTuple_Check(args)); - BL ok = false; + bool ok = false; PyObject *self = PyTuple_GetItem(args,0); // borrowed ref if(self && PyInstance_Check(self)) { pyext *ext = GetThis(self); - I sz = PySequence_Size(args); + int sz = PySequence_Size(args); PyObject *val; - BL tp = + bool tp = sz == 2 && PySequence_Check( val = PyTuple_GetItem(args,1) // borrowed ref diff --git a/externals/grill/py/source/main.cpp b/externals/grill/py/source/main.cpp index 657349e7..86b5dd38 100644 --- a/externals/grill/py/source/main.cpp +++ b/externals/grill/py/source/main.cpp @@ -2,7 +2,7 @@ py/pyext - python external object for PD and MaxMSP -Copyright (c)2002-2004 Thomas Grill (gr@grrrr.org) +Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "license.txt," in this distribution. @@ -55,13 +55,18 @@ void FreeThreadState() #endif -V py::lib_setup() +void py::lib_setup() { post(""); - post("py/pyext %s - python script objects, (C)2002-2004 Thomas Grill",PY__VERSION); + post("--------------------------------------"); + post("py/pyext %s - python script objects",PY__VERSION); + post(" (C)2002-2005 Thomas Grill"); + post(" http://grrrr.org/ext"); #ifdef FLEXT_DEBUG + post(""); post("DEBUG version compiled on %s %s",__DATE__,__TIME__); #endif + post("--------------------------------------"); post(""); // ------------------------------------------------------------- @@ -90,11 +95,14 @@ V py::lib_setup() module_obj = Py_InitModule(PYEXT_MODULE, func_tbl); module_dict = PyModule_GetDict(module_obj); // borrowed reference - PyModule_AddStringConstant(module_obj,"__doc__",(C *)py_doc); + PyModule_AddStringConstant(module_obj,"__doc__",(char *)py_doc); // redirect stdout - PyObject* py_out = Py_InitModule("stdout", StdOut_Methods); + PyObject* py_out; + py_out = Py_InitModule("stdout", StdOut_Methods); PySys_SetObject("stdout", py_out); + py_out = Py_InitModule("stderr", StdOut_Methods); + PySys_SetObject("stderr", py_out); #ifdef FLEXT_THREADS // release global lock @@ -116,23 +124,29 @@ PyObject *py::module_dict = NULL; py::py(): module(NULL), - detach(false),shouldexit(false),thrcount(0), + detach(0),shouldexit(false),thrcount(0), stoptick(0) { - PY_LOCK +// interpreter = PyInterpreterState_New(); + + PyThreadState *state = PyLock(); Py_INCREF(module_obj); - PY_UNLOCK + PyUnlock(state); FLEXT_ADDTIMER(stoptmr,tick); + + // launch thread worker + FLEXT_CALLMETHOD(threadworker); } py::~py() { - if(thrcount) { - shouldexit = true; - + shouldexit = true; + qucond.Signal(); + + if(thrcount) { // Wait for a certain time - for(int i = 0; i < (PY_STOP_WAIT/PY_STOP_TICK) && thrcount; ++i) Sleep((F)(PY_STOP_TICK/1000.)); + for(int i = 0; i < (PY_STOP_WAIT/PY_STOP_TICK) && thrcount; ++i) Sleep((float)(PY_STOP_TICK/1000.)); // Wait forever post("%s - Waiting for thread termination!",thisName()); @@ -141,13 +155,19 @@ py::~py() } Py_XDECREF(module_obj); +/* + PyEval_AcquireLock(); + PyInterpreterState_Clear(interpreter); + PyInterpreterState_Delete(interpreter); + PyEval_ReleaseLock(); +*/ } void py::GetDir(PyObject *obj,AtomList &lst) { if(obj) { - PY_LOCK + PyThreadState *state = PyLock(); PyObject *pvar = PyObject_Dir(obj); if(!pvar) @@ -162,7 +182,7 @@ void py::GetDir(PyObject *obj,AtomList &lst) Py_DECREF(pvar); } - PY_UNLOCK + PyUnlock(state); } } @@ -174,10 +194,10 @@ void py::m__dir(PyObject *obj) ToOutAnything(GetOutAttr(),thisTag(),lst.Count(),lst.Atoms()); } -V py::m__doc(PyObject *obj) +void py::m__doc(PyObject *obj) { if(obj) { - PY_LOCK + PyThreadState *state = PyLock(); PyObject *docf = PyDict_GetItemString(obj,"__doc__"); // borrowed!!! if(docf && PyString_Check(docf)) { @@ -206,17 +226,17 @@ V py::m__doc(PyObject *obj) } } - PY_UNLOCK + PyUnlock(state); } } -V py::SetArgs(I argc,const t_atom *argv) +void py::SetArgs(int argc,const t_atom *argv) { // script arguments - C **sargv = new C *[argc+1]; + char **sargv = new char *[argc+1]; for(int i = 0; i <= argc; ++i) { - sargv[i] = new C[256]; + sargv[i] = new char[256]; if(!i) strcpy(sargv[i],thisName()); else @@ -230,11 +250,11 @@ V py::SetArgs(I argc,const t_atom *argv) delete[] sargv; } -V py::ImportModule(const C *name) +void py::ImportModule(const char *name) { if(!name) return; - module = PyImport_ImportModule((C *)name); // increases module_obj ref count by one + module = PyImport_ImportModule((char *)name); // increases module_obj ref count by one if (!module) { PyErr_Print(); @@ -244,7 +264,7 @@ V py::ImportModule(const C *name) dict = PyModule_GetDict(module); } -V py::UnimportModule() +void py::UnimportModule() { if(!module) return; @@ -260,7 +280,7 @@ V py::UnimportModule() dict = NULL; } -V py::ReloadModule() +void py::ReloadModule() { if(module) { PyObject *newmod = PyImport_ReloadModule(module); @@ -279,13 +299,13 @@ V py::ReloadModule() post("%s - No module to reload",thisName()); } -V py::GetModulePath(const C *mod,C *dir,I len) +void py::GetModulePath(const char *mod,char *dir,int len) { #if FLEXT_SYS == FLEXT_SYS_PD // uarghh... pd doesn't show its path for extra modules - C *name; - I fd = open_via_path("",mod,".py",dir,&name,len,0); + char *name; + int fd = open_via_path("",mod,".py",dir,&name,len,0); if(fd > 0) close(fd); else name = NULL; @@ -295,10 +315,25 @@ V py::GetModulePath(const C *mod,C *dir,I len) // how do i get the path in Max/MSP? short path; long type; - char smod[256]; + char smod[1024]; strcat(strcpy(smod,mod),".py"); - if(!locatefile_extended(smod,&path,&type,&type,-1)) + if(!locatefile_extended(smod,&path,&type,&type,-1)) { +#if FLEXT_OS == FLEXT_OS_WIN path_topathname(path,NULL,dir); +#else + // convert pathname to unix style + path_topathname(path,NULL,smod); + char *colon = strchr(smod,':'); + if(colon) { + *colon = 0; + strcpy(dir,"/Volumes/"); + strcat(dir,smod); + strcat(dir,colon+1); + } + else + strcpy(dir,smod); +#endif + } else // not found *dir = 0; @@ -307,7 +342,7 @@ V py::GetModulePath(const C *mod,C *dir,I len) #endif } -V py::AddToPath(const C *dir) +void py::AddToPath(const char *dir) { if(dir && *dir) { PyObject *pobj = PySys_GetObject("path"); @@ -326,6 +361,19 @@ V py::AddToPath(const C *dir) } } +static const t_symbol *sym_response = flext::MakeSymbol("response"); + +void py::Respond(bool b) +{ + if(respond) { + t_atom a; + SetBool(a,b); + ToOutAnything(GetOutAttr(),sym_response,1,&a); + } +} + + + static PyObject *output = NULL; // post to the console @@ -368,3 +416,127 @@ PyObject* py::StdOut_Write(PyObject* self, PyObject* args) Py_INCREF(Py_None); return Py_None; } + + +class work_data +{ +public: + work_data(PyObject *f,PyObject *a): fun(f),args(a) {} + ~work_data() { Py_DECREF(fun); Py_DECREF(args); } + + PyObject *fun,*args; +}; + +bool py::gencall(PyObject *pmeth,PyObject *pargs) +{ + bool ret = false; + + // Now call method + switch(detach) { + case 0: + ret = callpy(pmeth,pargs); + Py_DECREF(pargs); + Py_DECREF(pmeth); + break; + case 1: + // put call into queue + ret = qucall(pmeth,pargs); + break; + case 2: + // each call a new thread + if(!shouldexit) { + ret = FLEXT_CALLMETHOD_X(work_wrapper,new work_data(pmeth,pargs)); + if(!ret) post("%s - Failed to launch thread!",thisName()); + } + break; + default: + FLEXT_ASSERT(false); + } + return ret; +} + +void py::work_wrapper(void *data) +{ + FLEXT_ASSERT(data); + + ++thrcount; + + PyThreadState *state = PyLock(); + + // call worker + work_data *w = (work_data *)data; + callpy(w->fun,w->args); + delete w; + + PyUnlock(state); + + --thrcount; +} + +bool py::qucall(PyObject *fun,PyObject *args) +{ + if(qufifo.Push(fun,args)) { + qucond.Signal(); + return true; + } + else + return false; +} + +void py::threadworker() +{ + PyObject *fun,*args; + PyThreadState *state; + + while(!shouldexit) { + state = PyLock(); + while(qufifo.Pop(fun,args)) { + callpy(fun,args); + Py_XDECREF(fun); + Py_XDECREF(args); + } + PyUnlock(state); + qucond.Wait(); + } + + state = PyLock(); + // unref remaining Python objects + while(qufifo.Pop(fun,args)) { + Py_XDECREF(fun); + Py_XDECREF(args); + } + PyUnlock(state); +} + +Fifo::~Fifo() +{ + FifoEl *el = head; + while(el) { + FifoEl *n = el->nxt; + delete el; + el = n; + } +} + +bool Fifo::Push(PyObject *f,PyObject *a) +{ + FifoEl *el = new FifoEl; + el->fun = f; + el->args = a; + if(tail) tail->nxt = el; + else head = el; + tail = el; + return true; +} + +bool Fifo::Pop(PyObject *&f,PyObject *&a) +{ + if(!head) return false; + FifoEl *el = head; + head = el->nxt; + f = el->fun; + a = el->args; + if(tail == el) tail = NULL; + delete el; + return true; +} diff --git a/externals/grill/py/source/main.h b/externals/grill/py/source/main.h index 6dde3f54..e953b319 100644 --- a/externals/grill/py/source/main.h +++ b/externals/grill/py/source/main.h @@ -2,7 +2,7 @@ py/pyext - python script object for PD and MaxMSP -Copyright (c)2002-2004 Thomas Grill (gr@grrrr.org) +Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "license.txt," in this distribution. @@ -24,8 +24,8 @@ WARRANTIES, see the file, "license.txt," in this distribution. #include <unistd.h> #endif -#if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 408) -#error You need at least flext version 0.4.8 +#if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 500) +#error You need at least flext version 0.5.0 #endif #define PY__VERSION "0.2.0pre" @@ -38,16 +38,28 @@ WARRANTIES, see the file, "license.txt," in this distribution. #define PY_STOP_TICK 10 // ms +class Fifo +{ +protected: + struct FifoEl { + PyObject *fun; + PyObject *args; + FifoEl *nxt; + }; +public: + Fifo(): head(NULL),tail(NULL) {} + ~Fifo(); -#define I int -#define C char -#define V void -#define BL bool -#define F float -#define D double + bool Push(PyObject *f,PyObject *a); + bool Pop(PyObject *&f,PyObject *&a); + +protected: + FifoEl *head,*tail; +}; -#include "main.h" +PyThreadState *FindThreadState(); +void FreeThreadState(); class py: public flext_base @@ -57,43 +69,43 @@ class py: public: py(); ~py(); - static V lib_setup(); + static void lib_setup(); - static PyObject *MakePyArgs(const t_symbol *s,int argc,const t_atom *argv,I inlet = -1,BL withself = false); + static PyObject *MakePyArgs(const t_symbol *s,int argc,const t_atom *argv,int inlet = -1,bool withself = false); static AtomList *GetPyArgs(PyObject *pValue,PyObject **self = NULL); protected: - V m__dir(PyObject *obj); - V m__doc(PyObject *obj); + void m__dir(PyObject *obj); + void m__doc(PyObject *obj); - V m_dir() { m__dir(module); } - V mg_dir(AtomList &lst) { m__dir(module); } - V m_doc() { m__doc(dict); } + void m_dir() { m__dir(module); } + void mg_dir(AtomList &lst) { m__dir(module); } + void m_doc() { m__doc(dict); } PyObject *module,*dict; // inherited user class module and associated dictionary - static const C *py_doc; + static const char *py_doc; - V GetDir(PyObject *obj,AtomList &lst); + void GetDir(PyObject *obj,AtomList &lst); - V GetModulePath(const C *mod,C *dir,I len); - V AddToPath(const C *dir); - V SetArgs(I argc,const t_atom *argv); - V ImportModule(const C *name); - V UnimportModule(); - V ReloadModule(); + void GetModulePath(const char *mod,char *dir,int len); + void AddToPath(const char *dir); + void SetArgs(int argc,const t_atom *argv); + void ImportModule(const char *name); + void UnimportModule(); + void ReloadModule(); - V Register(const C *reg); - V Unregister(const C *reg); - V Reregister(const C *reg); - virtual V Reload() = 0; + void Register(const char *reg); + void Unregister(const char *reg); + void Reregister(const char *reg); + virtual void Reload() = 0; - V Respond(BL b) { if(respond) { t_atom a[1]; SetBool(a[0],b); ToOutAnything(GetOutAttr(),MakeSymbol("response"),1,a); } } + void Respond(bool b); - static BL IsAnything(const t_symbol *s) { return s && s != sym_bang && s != sym_float && s != sym_int && s != sym_symbol && s != sym_list && s != sym_pointer; } + static bool IsAnything(const t_symbol *s) { return s && s != sym_bang && s != sym_float && s != sym_int && s != sym_symbol && s != sym_list && s != sym_pointer; } - enum retval { nothing,atom,sequ /*,tuple,list*/ }; + enum retval { nothing,atom,sequ }; // --- module stuff ----- @@ -108,8 +120,7 @@ protected: static PyObject *py_samplerate(PyObject *,PyObject *args); static PyObject *py_blocksize(PyObject *,PyObject *args); - static PyObject *py_inchannels(PyObject *,PyObject *args); - static PyObject *py_outchannels(PyObject *,PyObject *args); + #if FLEXT_SYS == FLEXT_SYS_PD static PyObject *py_getvalue(PyObject *,PyObject *args); static PyObject *py_setvalue(PyObject *,PyObject *args); @@ -117,24 +128,59 @@ protected: // ----thread stuff ------------ - virtual V m_stop(int argc,const t_atom *argv); + virtual void m_stop(int argc,const t_atom *argv); - BL detach,shouldexit,respond; - I thrcount; - I stoptick; + bool shouldexit,respond; + int thrcount; + int stoptick; Timer stoptmr; + int detach; - V tick(V *); + void tick(void *); + + bool gencall(PyObject *fun,PyObject *args); + virtual bool callpy(PyObject *fun,PyObject *args) = 0; + +private: +// PyInterpreterState *interpreter; + + bool qucall(PyObject *fun,PyObject *args); + void threadworker(); + Fifo qufifo; + ThrCond qucond; + + void work_wrapper(void *data); + +#ifdef FLEXT_THREADS + FLEXT_THREAD_X(work_wrapper) +#else + FLEXT_CALLBACK_X(work_wrapper) +#endif public: #ifdef FLEXT_THREADS ThrMutex mutex; - inline V Lock() { mutex.Unlock(); } - inline V Unlock() { mutex.Unlock(); } + inline void Lock() { mutex.Unlock(); } + inline void Unlock() { mutex.Unlock(); } + + inline PyThreadState *PyLock() + { + PyEval_AcquireLock(); + return PyThreadState_Swap(FindThreadState()); + } + + inline void PyUnlock(PyThreadState *st) + { + PyThreadState_Swap(st); + PyEval_ReleaseLock(); + } #else - inline V Lock() {} - inline V Unlock() {} + inline void Lock() {} + inline void Unlock() {} + + inline PyThreadState *PyLock() { return NULL; } + inline void PyUnlock(PyThreadState *st) {} #endif static PyObject* StdOut_Write(PyObject* Self, PyObject* Args); @@ -142,34 +188,15 @@ public: protected: // callbacks - FLEXT_ATTRVAR_B(detach) + FLEXT_ATTRVAR_I(detach) FLEXT_ATTRVAR_B(respond) FLEXT_CALLBACK_V(m_stop) FLEXT_CALLBACK(m_dir) FLEXT_CALLGET_V(mg_dir) FLEXT_CALLBACK(m_doc) FLEXT_CALLBACK_T(tick) -}; - -#ifdef FLEXT_THREADS - -PyThreadState *FindThreadState(); -void FreeThreadState(); - -#define PY_LOCK \ - { \ - PyEval_AcquireLock(); \ - PyThreadState *__st = FindThreadState(); \ - PyThreadState *__oldst = PyThreadState_Swap(__st); - -#define PY_UNLOCK \ - PyThreadState_Swap(__oldst); \ - PyEval_ReleaseLock(); \ - } -#else -#define PY_LOCK -#define PY_UNLOCK -#endif + FLEXT_THREAD(threadworker) +}; #endif diff --git a/externals/grill/py/source/modmeth.cpp b/externals/grill/py/source/modmeth.cpp index 5a3efd10..88e3290a 100644 --- a/externals/grill/py/source/modmeth.cpp +++ b/externals/grill/py/source/modmeth.cpp @@ -2,7 +2,7 @@ py/pyext - python external object for PD and Max/MSP -Copyright (c) 2002-2004 Thomas Grill (gr@grrrr.org) +Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "license.txt," in this distribution. @@ -21,8 +21,6 @@ PyMethodDef py::func_tbl[] = { "_samplerate", py::py_samplerate, METH_NOARGS,"Get system sample rate" }, { "_blocksize", py::py_blocksize, METH_NOARGS,"Get system block size" }, - { "_inchannels", py::py_inchannels, METH_NOARGS,"Get number of audio in channels" }, - { "_outchannels", py::py_outchannels, METH_NOARGS,"Get number of audio out channels" }, #if FLEXT_SYS == FLEXT_SYS_PD { "_getvalue", py::py_getvalue, METH_VARARGS,"Get value of a 'value' object" }, @@ -32,8 +30,8 @@ PyMethodDef py::func_tbl[] = {NULL, NULL, 0, NULL} // sentinel }; -const C *py::py_doc = - "py/pyext - python external object for PD and Max/MSP, (C)2002-2004 Thomas Grill\n" +const char *py::py_doc = + "py/pyext - python external object for PD and Max/MSP, (C)2002-2005 Thomas Grill\n" "\n" "This is the pyext module. Available function:\n" "_send(args...): Send a message to a send symbol\n" @@ -42,15 +40,13 @@ const C *py::py_doc = #endif "_samplerate(): Get system sample rate\n" "_blocksize(): Get current blocksize\n" - "_inchannels(): Get number of audio in channels\n" - "_outchannels(): Get number of audio out channels\n" "_getvalue(name): Get value of a 'value' object\n" "_setvalue(name,float): Set value of a 'value' object\n" ; -V py::tick(V *) +void py::tick(void *) { Lock(); @@ -73,15 +69,15 @@ V py::tick(V *) Unlock(); } -V py::m_stop(int argc,const t_atom *argv) +void py::m_stop(int argc,const t_atom *argv) { if(thrcount) { Lock(); - I wait = PY_STOP_WAIT; + int wait = PY_STOP_WAIT; if(argc >= 1 && CanbeInt(argv[0])) wait = GetAInt(argv[0]); - I ticks = wait/PY_STOP_TICK; + int ticks = wait/PY_STOP_TICK; if(stoptick) { // already stopping if(ticks < stoptick) stoptick = ticks; @@ -106,32 +102,6 @@ PyObject *py::py_blocksize(PyObject *self,PyObject *args) return PyLong_FromLong(sys_getblksize()); } -PyObject *py::py_inchannels(PyObject *self,PyObject *args) -{ -#if FLEXT_SYS == FLEXT_SYS_PD - I ch = sys_get_inchannels(); -#elif FLEXT_SYS == FLEXT_SYS_MAX - I ch = sys_getch(); // not working -#else -#pragma message("Not implemented!") - ch = 0; -#endif - return PyLong_FromLong(ch); -} - -PyObject *py::py_outchannels(PyObject *self,PyObject *args) -{ -#if FLEXT_SYS == FLEXT_SYS_PD - I ch = sys_get_outchannels(); -#elif FLEXT_SYS == FLEXT_SYS_MAX - I ch = sys_getch(); // not working -#else -#pragma message("Not implemented!") - ch = 0; -#endif - return PyLong_FromLong(ch); -} - PyObject *py::py_send(PyObject *,PyObject *args) { // should always be a tuple diff --git a/externals/grill/py/source/py.cpp b/externals/grill/py/source/py.cpp index 021e710f..05aeea8d 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 (gr@grrrr.org) +Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "license.txt," in this distribution. @@ -17,40 +17,43 @@ class pyobj: FLEXT_HEADER_S(pyobj,py,Setup) public: - pyobj(I argc,const t_atom *argv); + pyobj(int argc,const t_atom *argv); ~pyobj(); protected: - BL m_method_(I n,const t_symbol *s,I argc,const t_atom *argv); + bool m_method_(int n,const t_symbol *s,int argc,const t_atom *argv); - BL work(const t_symbol *s,I argc,const t_atom *argv); + bool work(const t_symbol *s,int argc,const t_atom *argv); - V m_bang() { callwork(sym_bang,0,NULL); } - V m_reload(); - V m_reload_(I argc,const t_atom *argv); - V m_set(I argc,const t_atom *argv); - V m_dir_() { m__dir(function); } - V m_doc_() { m__doc(function); } + void m_bang() { callwork(sym_bang,0,NULL); } + void m_reload(); + void m_reload_(int argc,const t_atom *argv); + void m_set(int argc,const t_atom *argv); + void m_dir_() { m__dir(function); } + void m_doc_() { m__doc(function); } - virtual V m_help(); + virtual void m_help(); // methods for python arguments - V callwork(const t_symbol *s,I argc,const t_atom *argv); + void callwork(const t_symbol *s,int argc,const t_atom *argv); - V m_py_list(I argc,const t_atom *argv) { callwork(sym_list,argc,argv); } - V m_py_float(I argc,const t_atom *argv) { callwork(sym_float,argc,argv); } - V m_py_int(I argc,const t_atom *argv) { callwork(sym_int,argc,argv); } - V m_py_any(const t_symbol *s,I argc,const t_atom *argv) { callwork(s,argc,argv); } + void m_py_list(int argc,const t_atom *argv) { callwork(sym_list,argc,argv); } + void m_py_float(int argc,const t_atom *argv) { callwork(sym_float,argc,argv); } + void m_py_int(int argc,const t_atom *argv) { callwork(sym_int,argc,argv); } + void m_py_any(const t_symbol *s,int argc,const t_atom *argv) { callwork(s,argc,argv); } const t_symbol *funname; PyObject *function; - virtual V Reload(); + virtual void Reload(); - V SetFunction(const C *func); - V ResetFunction(); + void SetFunction(const char *func); + void ResetFunction(); private: + + virtual bool callpy(PyObject *fun,PyObject *args); + static void Setup(t_classid c); FLEXT_CALLBACK(m_bang) @@ -98,10 +101,10 @@ void pyobj::Setup(t_classid c) FLEXT_CADDATTR_VAR1(c,"respond",respond); } -pyobj::pyobj(I argc,const t_atom *argv): +pyobj::pyobj(int argc,const t_atom *argv): function(NULL),funname(NULL) { - PY_LOCK + PyThreadState *state = PyLock(); AddInAnything(2); AddOutAnything(); @@ -116,7 +119,7 @@ pyobj::pyobj(I argc,const t_atom *argv): if(!IsString(argv[0])) post("%s - script name argument is invalid",thisName()); else { - C dir[1024]; + char dir[1024]; GetModulePath(GetString(argv[0]),dir,sizeof(dir)); // set script path AddToPath(dir); @@ -127,12 +130,11 @@ pyobj::pyobj(I argc,const t_atom *argv): // add current dir to path AddToPath(GetString(canvas_getcurrentdir())); #elif FLEXT_SYS == FLEXT_SYS_MAX -#if FLEXT_OS == FLEXT_OS_WIN -#else +/* short path = patcher_myvol(thisCanvas()); path_topathname(path,NULL,dir); AddToPath(dir); -#endif +*/ #else #pragma message("Adding current dir to path is not implemented") #endif @@ -153,29 +155,29 @@ pyobj::pyobj(I argc,const t_atom *argv): } } - PY_UNLOCK + PyUnlock(state); } pyobj::~pyobj() { - PY_LOCK + PyThreadState *state = PyLock(); Unregister("_py"); - PY_UNLOCK + PyUnlock(state); } -BL pyobj::m_method_(I n,const t_symbol *s,I argc,const t_atom *argv) +bool pyobj::m_method_(int n,const t_symbol *s,int argc,const t_atom *argv) { if(n == 1) post("%s - no method for type %s",thisName(),GetString(s)); return false; } -V pyobj::m_reload() +void pyobj::m_reload() { - PY_LOCK + PyThreadState *state = PyLock(); Unregister("_py"); @@ -184,29 +186,29 @@ V pyobj::m_reload() Register("_py"); SetFunction(funname?GetString(funname):NULL); - PY_UNLOCK + PyUnlock(state); } -V pyobj::m_reload_(I argc,const t_atom *argv) +void pyobj::m_reload_(int argc,const t_atom *argv) { - PY_LOCK + PyThreadState *state = PyLock(); SetArgs(argc,argv); - PY_UNLOCK + PyUnlock(state); m_reload(); } -V pyobj::m_set(I argc,const t_atom *argv) +void pyobj::m_set(int argc,const t_atom *argv) { - PY_LOCK + PyThreadState *state = PyLock(); - I ix = 0; + int ix = 0; if(argc >= 2) { if(!IsString(argv[ix])) { post("%s - script name is not valid",thisName()); return; } - const C *sn = GetString(argv[ix]); + const char *sn = GetString(argv[ix]); if(!module || !strcmp(sn,PyModule_GetName(module))) { ImportModule(sn); @@ -221,13 +223,13 @@ V pyobj::m_set(I argc,const t_atom *argv) else SetFunction(GetString(argv[ix])); - PY_UNLOCK + PyUnlock(state); } -V pyobj::m_help() +void pyobj::m_help() { post(""); - post("%s %s - python script object, (C)2002-2004 Thomas Grill",thisName(),PY__VERSION); + post("%s %s - python script object, (C)2002-2005 Thomas Grill",thisName(),PY__VERSION); #ifdef FLEXT_DEBUG post("DEBUG VERSION, compiled on " __DATE__ " " __TIME__); #endif @@ -248,13 +250,13 @@ V pyobj::m_help() post("\tdir: dump module dictionary"); post("\tdir+: dump function dictionary"); #ifdef FLEXT_THREADS - post("\tdetach 0/1: detach threads"); + post("\tdetach 0/1/2: detach threads"); post("\tstop {wait time (ms)}: stop threads"); #endif post(""); } -V pyobj::ResetFunction() +void pyobj::ResetFunction() { if(!module || !dict) { @@ -263,7 +265,7 @@ V pyobj::ResetFunction() return; } - function = funname?PyDict_GetItemString(dict,(C *)GetString(funname)):NULL; // borrowed!!! + function = funname?PyDict_GetItemString(dict,(char *)GetString(funname)):NULL; // borrowed!!! if(!function) { PyErr_Clear(); if(funname) post("%s - Function %s could not be found",thisName(),GetString(funname)); @@ -274,7 +276,7 @@ V pyobj::ResetFunction() } } -V pyobj::SetFunction(const C *func) +void pyobj::SetFunction(const char *func) { if(func) { funname = MakeSymbol(func); @@ -285,63 +287,51 @@ V pyobj::SetFunction(const C *func) } -V pyobj::Reload() +void pyobj::Reload() { ResetFunction(); } - -BL pyobj::work(const t_symbol *s,I argc,const t_atom *argv) +bool pyobj::callpy(PyObject *fun,PyObject *args) { - AtomList *rargs = NULL; - BL ret; - - ++thrcount; - PY_LOCK + PyObject *ret = PyObject_Call(fun,args,NULL); + if(ret == NULL) { + // function not found resp. arguments not matching + PyErr_Print(); + return false; + } + else { + AtomList *rargs = GetPyArgs(ret); + if(!rargs) + PyErr_Print(); + else { + // call to outlet _outside_ the Mutex lock! + // otherwise (if not detached) deadlock will occur + if(rargs->Count()) ToOutList(0,*rargs); + delete rargs; + } + Py_DECREF(ret); + return true; + } +} +void pyobj::callwork(const t_symbol *s,int argc,const t_atom *argv) +{ + bool ret = false; + if(function) { - PyObject *pArgs = MakePyArgs(s,argc,argv); - PyObject *pValue = PyObject_CallObject(function, pArgs); - - rargs = GetPyArgs(pValue); - if(!rargs) PyErr_Print(); - - Py_XDECREF(pArgs); - Py_XDECREF(pValue); - ret = true; - } + PyThreadState *state = PyLock(); + + PyObject *pargs = MakePyArgs(s,argc,argv); + Py_INCREF(function); + ret = gencall(function,pargs); + + PyUnlock(state); + } else { post("%s: no function defined",thisName()); ret = false; } - PY_UNLOCK - --thrcount; - - if(rargs) { - // call to outlet _outside_ the Mutex lock! - // otherwise (if not detached) deadlock will occur - if(rargs->Count()) ToOutList(0,*rargs); - delete rargs; - } - - return ret; -} - -V pyobj::callwork(const t_symbol *s,I argc,const t_atom *argv) -{ - BL ret = false; - if(detach) { - if(shouldexit) - post("%s - New threads can't be launched now!",thisName()); - else { - ret = FLEXT_CALLMETHOD_A(work,s,argc,argv); - if(!ret) post("%s - Failed to launch thread!",thisName()); - } - } - else - ret = work(s,argc,argv); Respond(ret); } - - diff --git a/externals/grill/py/source/pyargs.cpp b/externals/grill/py/source/pyargs.cpp index 3c2d3c35..90686a90 100644 --- a/externals/grill/py/source/pyargs.cpp +++ b/externals/grill/py/source/pyargs.cpp @@ -2,7 +2,7 @@ py/pyext - python external object for PD and MaxMSP -Copyright (c)2002-2004 Thomas Grill (gr@grrrr.org) +Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "license.txt," in this distribution. @@ -21,13 +21,13 @@ static PyObject *MakePyAtom(const t_atom &at) return (double)ival == fval?PyInt_FromLong(ival):PyFloat_FromDouble(fval); } // these following should never happen - else if(flext::IsFloat(at)) return PyFloat_FromDouble((D)flext::GetFloat(at)); + else if(flext::IsFloat(at)) return PyFloat_FromDouble((double)flext::GetFloat(at)); else if(flext::IsInt(at)) return PyInt_FromLong(flext::GetInt(at)); return NULL; } -PyObject *py::MakePyArgs(const t_symbol *s,int argc,const t_atom *argv,I inlet,BL withself) +PyObject *py::MakePyArgs(const t_symbol *s,int argc,const t_atom *argv,int inlet,bool withself) { PyObject *pArgs; @@ -40,10 +40,10 @@ PyObject *py::MakePyArgs(const t_symbol *s,int argc,const t_atom *argv,I inlet,B else */ { - BL any = IsAnything(s); + bool any = IsAnything(s); pArgs = PyTuple_New(argc+(any?1:0)+(inlet >= 0?1:0)); - I pix = 0; + int pix = 0; if(inlet >= 0) { PyObject *pValue = PyInt_FromLong(inlet); @@ -52,7 +52,7 @@ PyObject *py::MakePyArgs(const t_symbol *s,int argc,const t_atom *argv,I inlet,B PyTuple_SetItem(pArgs, pix++, pValue); } - I ix; + int ix; PyObject *tmp; if(!withself || argc < (any?1:2)) tmp = pArgs,ix = pix; else tmp = PyTuple_New(argc+(any?1:0)),ix = 0; @@ -64,7 +64,7 @@ PyObject *py::MakePyArgs(const t_symbol *s,int argc,const t_atom *argv,I inlet,B PyTuple_SetItem(tmp, ix++, pValue); } - for(I i = 0; i < argc; ++i) { + for(int i = 0; i < argc; ++i) { PyObject *pValue = MakePyAtom(argv[i]); if(!pValue) { post("py/pyext: cannot convert argument %i",any?i+1:i); @@ -95,8 +95,8 @@ flext::AtomList *py::GetPyArgs(PyObject *pValue,PyObject **self) // analyze return value or tuple - I rargc = 0; - BL ok = true; + int rargc = 0; + bool ok = true; retval tp = nothing; if(PyString_Check(pValue)) { @@ -116,7 +116,7 @@ flext::AtomList *py::GetPyArgs(PyObject *pValue,PyObject **self) ret = new AtomList(rargc); - for(I ix = 0; ix < rargc; ++ix) { + for(int ix = 0; ix < rargc; ++ix) { PyObject *arg; if(tp == sequ) arg = PySequence_GetItem(pValue,ix); // new reference @@ -125,7 +125,7 @@ flext::AtomList *py::GetPyArgs(PyObject *pValue,PyObject **self) if(PyInt_Check(arg)) SetInt((*ret)[ix],PyInt_AsLong(arg)); else if(PyLong_Check(arg)) SetInt((*ret)[ix],PyLong_AsLong(arg)); - else if(PyFloat_Check(arg)) SetFloat((*ret)[ix],(F)PyFloat_AsDouble(arg)); + else if(PyFloat_Check(arg)) SetFloat((*ret)[ix],(float)PyFloat_AsDouble(arg)); else if(PyString_Check(arg)) SetString((*ret)[ix],PyString_AsString(arg)); else if(ix == 0 && self && PyInstance_Check(arg)) { // assumed to be self ... that should be checked _somehow_ !!! @@ -135,7 +135,7 @@ flext::AtomList *py::GetPyArgs(PyObject *pValue,PyObject **self) else { PyObject *tp = PyObject_Type(arg); PyObject *stp = tp?PyObject_Str(tp):NULL; - C *tmp = ""; + char *tmp = ""; if(stp) tmp = PyString_AsString(stp); post("py/pyext: Could not convert argument %s",tmp); Py_XDECREF(stp); diff --git a/externals/grill/py/source/pyext.cpp b/externals/grill/py/source/pyext.cpp index 9e1ff30b..f0fc25ba 100644 --- a/externals/grill/py/source/pyext.cpp +++ b/externals/grill/py/source/pyext.cpp @@ -2,7 +2,7 @@ py/pyext - python script object for PD and Max/MSP -Copyright (c)2002-2004 Thomas Grill (gr@grrrr.org) +Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "license.txt," in this distribution. @@ -13,9 +13,14 @@ WARRANTIES, see the file, "license.txt," in this distribution. FLEXT_LIB_V("pyext pyext. pyx pyx.",pyext) -V pyext::Setup(t_classid c) + +static const t_symbol *sym_get; + +void pyext::Setup(t_classid c) { - FLEXT_CADDMETHOD_(c,0,"reload.",m_reload); + sym_get = flext::MakeSymbol("get"); + + FLEXT_CADDMETHOD_(c,0,"reload.",m_reload); FLEXT_CADDMETHOD_(c,0,"reload",m_reload_); FLEXT_CADDMETHOD_(c,0,"dir",m_dir); FLEXT_CADDMETHOD_(c,0,"dir+",m_dir_); @@ -35,7 +40,6 @@ V pyext::Setup(t_classid c) FLEXT_CADDATTR_VAR1(c,"respond",respond); - // ---------------------------------------------------- // register/initialize pyext base class along with module @@ -112,7 +116,7 @@ static short patcher_myvol(t_patcher *x) PyObject *pyext::class_obj = NULL; PyObject *pyext::class_dict = NULL; -pyext::pyext(I argc,const t_atom *argv): +pyext::pyext(int argc,const t_atom *argv): pyobj(NULL),pythr(NULL), inlets(-1),outlets(-1), methname(NULL) @@ -127,7 +131,7 @@ pyext::pyext(I argc,const t_atom *argv): const t_atom *clname = NULL; - PY_LOCK + PyThreadState *state = PyLock(); // init script module if(argc > apre) { @@ -238,7 +242,7 @@ pyext::pyext(I argc,const t_atom *argv): else inlets = outlets = 0; - PY_UNLOCK + PyUnlock(state); if(inlets < 0 || outlets < 0) InitProblem(); @@ -253,7 +257,7 @@ pyext::pyext(I argc,const t_atom *argv): pyext::~pyext() { - PY_LOCK + PyThreadState *state = PyLock(); ClearBinding(); @@ -265,10 +269,10 @@ pyext::~pyext() Unregister("_pyext"); UnimportModule(); - PY_UNLOCK + PyUnlock(state); } -BL pyext::DoInit() +bool pyext::DoInit() { SetThis(); @@ -293,12 +297,12 @@ BL pyext::DoInit() return true; } -BL pyext::MakeInstance() +bool pyext::MakeInstance() { // pyobj should already have been decref'd / cleared before getting here!! if(module && methname) { - PyObject *pref = PyObject_GetAttrString(module,const_cast<C *>(GetString(methname))); + PyObject *pref = PyObject_GetAttrString(module,const_cast<char *>(GetString(methname))); if(!pref) PyErr_Print(); else { @@ -319,7 +323,7 @@ BL pyext::MakeInstance() return false; } -V pyext::Reload() +void pyext::Reload() { ClearBinding(); Py_XDECREF(pyobj); @@ -333,9 +337,9 @@ V pyext::Reload() } -V pyext::m_reload() +void pyext::m_reload() { - PY_LOCK + PyThreadState *state = PyLock(); Unregister("_pyext"); // self @@ -346,10 +350,10 @@ V pyext::m_reload() SetThis(); - PY_UNLOCK + PyUnlock(state); } -V pyext::m_reload_(I argc,const t_atom *argv) +void pyext::m_reload_(int argc,const t_atom *argv) { args(argc,argv); m_reload(); @@ -357,7 +361,7 @@ V pyext::m_reload_(I argc,const t_atom *argv) void pyext::m_get(const t_symbol *s) { - PY_LOCK + PyThreadState *state = PyLock(); PyObject *pvar = PyObject_GetAttrString(pyobj,const_cast<char *>(GetString(s))); /* fetch bound method */ if(!pvar) { @@ -368,7 +372,7 @@ void pyext::m_get(const t_symbol *s) AtomList *lst = GetPyArgs(pvar); if(lst) { // dump value to attribute outlet - AtomAnything out("get",lst->Count()+1); + AtomAnything out(sym_get,lst->Count()+1); SetSymbol(out[0],s); out.Set(lst->Count(),lst->Atoms(),1); delete lst; @@ -380,12 +384,12 @@ void pyext::m_get(const t_symbol *s) Py_DECREF(pvar); } - PY_UNLOCK + PyUnlock(state); } void pyext::m_set(int argc,const t_atom *argv) { - PY_LOCK + PyThreadState *state = PyLock(); if(argc < 2 || !IsString(argv[0])) post("%s - Syntax: set varname arguments...",thisName()); @@ -417,25 +421,25 @@ void pyext::m_set(int argc,const t_atom *argv) } } - PY_UNLOCK + PyUnlock(state); } -BL pyext::m_method_(I n,const t_symbol *s,I argc,const t_atom *argv) +bool pyext::m_method_(int n,const t_symbol *s,int argc,const t_atom *argv) { - BL ret = false; + bool ret = false; if(pyobj && n >= 1) - ret = callwork(n,s,argc,argv); + ret = work(n,s,argc,argv); else post("%s - no method for type '%s' into inlet %i",thisName(),GetString(s),n); return ret; } -V pyext::m_help() +void pyext::m_help() { post(""); - post("%s %s - python class object, (C)2002-2004 Thomas Grill",thisName(),PY__VERSION); + post("%s %s - python class object, (C)2002-2005 Thomas Grill",thisName(),PY__VERSION); #ifdef FLEXT_DEBUG post("DEBUG VERSION, compiled on " __DATE__ " " __TIME__); #endif @@ -460,97 +464,50 @@ V pyext::m_help() post(""); } -PyObject *pyext::call(const C *meth,I inlet,const t_symbol *s,I argc,const t_atom *argv) +bool pyext::callpy(PyObject *fun,PyObject *args) +{ + PyObject *ret = PyObject_Call(fun,args,NULL); + if(ret == NULL) { + // function not found resp. arguments not matching + PyErr_Print(); + return false; + } + else { + if(!PyObject_Not(ret)) post("pyext - returned value is ignored"); + Py_DECREF(ret); + return true; + } +} + + +bool pyext::call(const char *meth,int inlet,const t_symbol *s,int argc,const t_atom *argv) { - PyObject *ret = NULL; + bool ret = false; - PyObject *pmeth = PyObject_GetAttrString(pyobj,const_cast<char *>(meth)); /* fetch bound method */ + PyObject *pmeth = PyObject_GetAttrString(pyobj,const_cast<char *>(meth)); /* fetch bound method */ if(pmeth == NULL) { PyErr_Clear(); // no method found } else { PyObject *pargs = MakePyArgs(s,argc,argv,inlet?inlet:-1,true); - if(!pargs) + if(!pargs) { PyErr_Print(); - else { - ret = PyEval_CallObject(pmeth, pargs); - if (ret == NULL) // function not found resp. arguments not matching - PyErr_Print(); - - Py_DECREF(pargs); - } - Py_DECREF(pmeth); - } - - return ret; -} - -V pyext::work_wrapper(V *data) -{ - ++thrcount; -#ifdef FLEXT_DEBUG - if(!data) - post("%s - no data!",thisName()); - else -#endif - { -#ifdef FLEXT_THREADS - // --- make new Python thread --- - // get the global lock - PyEval_AcquireLock(); - // create a thread state object for this thread - PyThreadState *newthr = FindThreadState(); - // free the lock - PyEval_ReleaseLock(); -#endif - { - // call worker - work_data *w = (work_data *)data; - work(w->n,w->Header(),w->Count(),w->Atoms()); - delete w; + Py_DECREF(pmeth); } - -#ifdef FLEXT_THREADS - // --- delete Python thread --- - // grab the lock - PyEval_AcquireLock(); - // swap my thread state out of the interpreter - PyThreadState_Swap(NULL); - // delete mapped thread state - FreeThreadState(); - // release the lock - PyEval_ReleaseLock(); - // ----------------------------- -#endif - } - --thrcount; -} - -BL pyext::callwork(I n,const t_symbol *s,I argc,const t_atom *argv) -{ - BL ret = true,ok = false; - if(detach) { - if(shouldexit) - post("%s - Stopping.... new threads can't be launched now!",thisName()); - else { - ret = FLEXT_CALLMETHOD_X(work_wrapper,new work_data(n,s,argc,argv)); - if(!ret) post("%s - Failed to launch thread!",thisName()); - } + else + ret = gencall(pmeth,pargs); } - else - ret = ok = work(n,s,argc,argv); - Respond(ok); - return ret; + return ret; } -BL pyext::work(I n,const t_symbol *s,I argc,const t_atom *argv) +bool pyext::work(int n,const t_symbol *s,int argc,const t_atom *argv) { - BL retv = false; + bool ret = false; - PY_LOCK + PyThreadState *state = PyLock(); - PyObject *ret = NULL; - char *str = new char[strlen(GetString(s))+10]; + // should be enough... + char str[256]; { // try tag/inlet @@ -563,7 +520,7 @@ BL pyext::work(I n,const t_symbol *s,I argc,const t_atom *argv) sprintf(str,"_anything_%i",n); if(s == sym_bang && !argc) { t_atom argv; - SetString(argv,""); + SetSymbol(argv,sym__); ret = call(str,0,s,1,&argv); } else @@ -576,29 +533,22 @@ BL pyext::work(I n,const t_symbol *s,I argc,const t_atom *argv) } if(!ret) { // try anything at any inlet - strcpy(str,"_anything_"); + const char *str1 = "_anything_"; if(s == sym_bang && !argc) { t_atom argv; - SetString(argv,""); - ret = call(str,n,s,1,&argv); + SetSymbol(argv,sym__); + ret = call(str1,n,s,1,&argv); } else - ret = call(str,n,s,argc,argv); + ret = call(str1,n,s,argc,argv); } if(!ret) // no matching python method found post("%s - no matching method found for '%s' into inlet %i",thisName(),GetString(s),n); - if(str) delete[] str; + PyUnlock(state); - if(ret) { - if(!PyObject_Not(ret)) post("%s - returned value is ignored",thisName()); - Py_DECREF(ret); - retv = true; - } - - PY_UNLOCK - - return retv; + Respond(ret); + return ret; } diff --git a/externals/grill/py/source/pyext.h b/externals/grill/py/source/pyext.h index 9fd4c157..45ba866d 100644 --- a/externals/grill/py/source/pyext.h +++ b/externals/grill/py/source/pyext.h @@ -2,7 +2,7 @@ py/pyext - python external object for PD and MaxMSP -Copyright (c)2002-2004 Thomas Grill (gr@grrrr.org) +Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "license.txt," in this distribution. @@ -19,7 +19,7 @@ class pyext: FLEXT_HEADER_S(pyext,py,Setup) public: - pyext(I argc,const t_atom *argv); + pyext(int argc,const t_atom *argv); ~pyext(); static PyObject *pyext__doc__(PyObject *,PyObject *args); @@ -38,46 +38,46 @@ public: static PyObject *pyext_stop(PyObject *,PyObject *args); static PyObject *pyext_isthreaded(PyObject *,PyObject *); - I Inlets() const { return inlets; } - I Outlets() const { return outlets; } + int Inlets() const { return inlets; } + int Outlets() const { return outlets; } protected: - virtual BL m_method_(I n,const t_symbol *s,I argc,const t_atom *argv); + virtual bool m_method_(int n,const t_symbol *s,int argc,const t_atom *argv); - BL work(I n,const t_symbol *s,I argc,const t_atom *argv); + bool work(int n,const t_symbol *s,int argc,const t_atom *argv); - V m_reload(); - V m_reload_(I argc,const t_atom *argv); - 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(((PyInstanceObject *)pyobj)->in_class->cl_dict); } - virtual V m_help(); + void m_reload(); + void m_reload_(int argc,const t_atom *argv); + void ms_args(const AtomList &a) { m_reload_(a.Count(),a.Atoms()); } + void m_dir_() { m__dir(pyobj); } + void mg_dir_(AtomList &lst) { GetDir(pyobj,lst); } + void m_doc_() { m__doc(((PyInstanceObject *)pyobj)->in_class->cl_dict); } + virtual void m_help(); - V m_get(const t_symbol *s); - V m_set(I argc,const t_atom *argv); + void m_get(const t_symbol *s); + void m_set(int argc,const t_atom *argv); const t_symbol *methname; PyObject *pyobj; - I inlets,outlets; + int inlets,outlets; private: - static V Setup(t_classid); + static void Setup(t_classid); static pyext *GetThis(PyObject *self); void SetThis(); - V ClearBinding(); - BL MakeInstance(); - BL DoInit(); + void ClearBinding(); + bool MakeInstance(); + bool DoInit(); AtomList args; - virtual V Reload(); + virtual void Reload(); static PyObject *class_obj,*class_dict; static PyMethodDef attr_tbl[],meth_tbl[]; - static const C *pyext_doc; + static const char *pyext_doc; // -------- bind stuff ------------------ static PyObject *pyext_bind(PyObject *,PyObject *args); @@ -85,24 +85,10 @@ private: // --------------------------- - PyObject *call(const C *meth,I inlet,const t_symbol *s,I argc,const t_atom *argv); + bool call(const char *meth,int inlet,const t_symbol *s,int argc,const t_atom *argv); - V work_wrapper(void *data); - BL callwork(I n,const t_symbol *s,I argc,const t_atom *argv); - - class work_data: - public flext::AtomAnything - { - public: - work_data(I _n,const t_symbol *_s,I _argc,const t_atom *_argv): n(_n),AtomAnything(_s,_argc,_argv) {} - I n; - }; - -#ifdef FLEXT_THREADS - FLEXT_THREAD_X(work_wrapper) -#else - FLEXT_CALLBACK_X(work_wrapper) -#endif + virtual bool callpy(PyObject *fun,PyObject *args); + static bool stcallpy(PyObject *fun,PyObject *args); PyThreadState *pythr; diff --git a/externals/grill/py/source/register.cpp b/externals/grill/py/source/register.cpp index c0ca4675..ae273955 100644 --- a/externals/grill/py/source/register.cpp +++ b/externals/grill/py/source/register.cpp @@ -2,7 +2,7 @@ py/pyext - python external object for PD and MaxMSP -Copyright (c) 2002-2004 Thomas Grill (gr@grrrr.org) +Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "license.txt," in this distribution. @@ -11,39 +11,37 @@ WARRANTIES, see the file, "license.txt," in this distribution. #include "main.h" -V py::Register(const C *regnm) +void py::Register(const char *regnm) { if(module) { // add this to module registry - PyObject *reg = PyDict_GetItemString(dict,(C *)regnm); // borrowed!!! + PyObject *reg = PyDict_GetItemString(dict,(char *)regnm); // borrowed!!! PyObject *add = Py_BuildValue("[i]",(long)this); if(!reg || !PyList_Check(reg)) { - if(PyDict_SetItemString(dict,(C *)regnm,add)) { + if(PyDict_SetItemString(dict,(char *)regnm,add)) { post("%s - Could not set registry",thisName()); } - // Py_XDECREF(reg); } else { PySequence_InPlaceConcat(reg,add); } } - } -V py::Unregister(const C *regnm) +void py::Unregister(const char *regnm) { if(module) { // remove this from module registry - PyObject *reg = PyDict_GetItemString(dict,(C *)regnm); // borrowed!!! + PyObject *reg = PyDict_GetItemString(dict,(char *)regnm); // borrowed!!! PyObject *add = Py_BuildValue("i",(int)this); if(!reg || !PySequence_Check(reg)) - post("%s - Registry not found!?",thisName()); + post("%s - Internal error: Registry not found!?",thisName()); else { - I ix = PySequence_Index(reg,add); + int ix = PySequence_Index(reg,add); if(ix < 0) { - post("%s - This object not found in registry?!",thisName()); + post("%s - Internal error: object not found in registry?!",thisName()); } else { PySequence_DelItem(reg,ix); @@ -51,24 +49,23 @@ V py::Unregister(const C *regnm) } Py_DECREF(add); } - } -V py::Reregister(const C *regnm) +void py::Reregister(const char *regnm) { if(module) { // remove this from module registry - PyObject *reg = PyDict_GetItemString(dict,(C *)regnm); // borrowed!!! + PyObject *reg = PyDict_GetItemString(dict,(char *)regnm); // borrowed!!! if(!reg || !PySequence_Check(reg)) - post("%s - Registry not found!?",thisName()); + post("%s - Internal error: Registry not found!?",thisName()); else { - I cnt = PySequence_Size(reg); - for(I i = 0; i < cnt; ++i) { + int cnt = PySequence_Size(reg); + for(int i = 0; i < cnt; ++i) { PyObject *it = PySequence_GetItem(reg,i); // new reference if(!it || !PyInt_Check(it)) { - post("%s - Corrupt registry?!",thisName()); + post("%s - Internal error: Corrupt registry?!",thisName()); } else { py *th = (py *)PyInt_AsLong(it); @@ -81,8 +78,4 @@ V py::Reregister(const C *regnm) } } } - } - - - |