From c2645dc4003b1391aba9b387a79a66cff1e63d3e Mon Sep 17 00:00:00 2001 From: Thomas Grill Date: Tue, 22 Oct 2002 23:16:30 +0000 Subject: This commit was generated by cvs2svn to compensate for changes in r189, which included commits to RCS files with non-trunk default branches. svn path=/trunk/; revision=190 --- externals/grill/py/source/bound.cpp | 143 ++++++++++ externals/grill/py/source/clmeth.cpp | 278 ++++++++++++++++++++ externals/grill/py/source/main.cpp | 212 +++++++++++++++ externals/grill/py/source/main.h | 147 +++++++++++ externals/grill/py/source/modmeth.cpp | 183 +++++++++++++ externals/grill/py/source/py.cpp | 327 +++++++++++++++++++++++ externals/grill/py/source/pyargs.cpp | 131 ++++++++++ externals/grill/py/source/pyext.cpp | 465 +++++++++++++++++++++++++++++++++ externals/grill/py/source/pyext.h | 127 +++++++++ externals/grill/py/source/register.cpp | 86 ++++++ 10 files changed, 2099 insertions(+) create mode 100644 externals/grill/py/source/bound.cpp create mode 100644 externals/grill/py/source/clmeth.cpp create mode 100644 externals/grill/py/source/main.cpp create mode 100644 externals/grill/py/source/main.h create mode 100644 externals/grill/py/source/modmeth.cpp create mode 100644 externals/grill/py/source/py.cpp create mode 100644 externals/grill/py/source/pyargs.cpp create mode 100644 externals/grill/py/source/pyext.cpp create mode 100644 externals/grill/py/source/pyext.h create mode 100644 externals/grill/py/source/register.cpp (limited to 'externals/grill/py/source') diff --git a/externals/grill/py/source/bound.cpp b/externals/grill/py/source/bound.cpp new file mode 100644 index 00000000..651bb52b --- /dev/null +++ b/externals/grill/py/source/bound.cpp @@ -0,0 +1,143 @@ +/* + +py/pyext - python external object for PD and MaxMSP + +Copyright (c) 2002 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. + +*/ + +#include "pyext.h" +#include "flinternal.h" + +t_class *pyext::px_class; +pyext::py_proxy *pyext::px_head,*pyext::px_tail; + +void pyext::py_proxy::px_method(py_proxy *obj,const t_symbol *s,int argc,t_atom *argv) +{ + PY_LOCK + + PyObject *args = MakePyArgs(s,AtomList(argc,argv),-1,obj->self != NULL); + PyObject *ret = PyObject_CallObject(obj->func,args); + if(!ret) { + PyErr_Print(); + } + Py_XDECREF(ret); + + PY_UNLOCK +} + + +PyObject *pyext::pyext_bind(PyObject *,PyObject *args) +{ + PyObject *self,*meth; + C *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))) { + post("py/pyext - Wrong argument types!"); + } + else { + t_symbol *recv = gensym(name); +/* + if(GetBound(recv)) + post("py/pyext - Symbol \"%s\" is already hooked",GetString(recv)); +*/ + // make a proxy object + py_proxy *px = (py_proxy *)object_new(px_class); + if(PyMethod_Check(meth)) { + PyObject *no = PyObject_GetAttrString(meth,"__name__"); + meth = PyObject_GetAttr(self,no); + Py_DECREF(no); + } + px->init(recv,self,meth); + + // add it to the list + if(px_tail) px_tail->nxt = px; + else px_head = px; + px_tail = px; + + // Do bind + pd_bind(&px->obj.ob_pd,recv); + + Py_INCREF(self); // self is borrowed reference + } + + Py_INCREF(Py_None); + return Py_None; +} + +PyObject *pyext::pyext_unbind(PyObject *,PyObject *args) +{ + PyObject *self,*meth; + C *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))) { + post("py/pyext - Wrong argument types!"); + } + else { + t_symbol *recv = gensym(name); + if(PyMethod_Check(meth)) { + PyObject *no = PyObject_GetAttrString(meth,"__name__"); + meth = PyObject_GetAttr(self,no); // meth is given a new reference! + Py_DECREF(no); + } + + // search proxy object + py_proxy *pp = NULL,*px = px_head; + while(px) { + py_proxy *pn = px->nxt; + if(recv == px->name && self == px->self && meth == px->func) { + if(pp) + pp->nxt = pn; + else + px_head = pn; + if(!pn) px_tail = pp; + break; + } + else pp = px; + px = pn; + } + + // do unbind + if(px) { + pd_unbind(&px->obj.ob_pd,recv); + object_free(px->obj); + + Py_DECREF(self); + if(PyMethod_Check(meth)) Py_DECREF(meth); + } + } + + Py_INCREF(Py_None); + return Py_None; +} + + +V pyext::ClearBinding() +{ + // search proxy object + py_proxy *pp = NULL,*px = px_head; + while(px) { + py_proxy *pn = px->nxt; + if(px->self == pyobj) { + if(pp) + pp->nxt = pn; + else + px_head = pn; + if(!pn) px_tail = pp; + + Py_DECREF(px->self); + if(PyMethod_Check(px->func)) Py_DECREF(px->func); + + pd_unbind(&px->obj.ob_pd,px->name); + object_free(px->obj); + } + else pp = px; + px = pn; + } +} + + diff --git a/externals/grill/py/source/clmeth.cpp b/externals/grill/py/source/clmeth.cpp new file mode 100644 index 00000000..0a30d1f7 --- /dev/null +++ b/externals/grill/py/source/clmeth.cpp @@ -0,0 +1,278 @@ +/* + +py/pyext - python external object for PD and MaxMSP + +Copyright (c) 2002 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. + +*/ + +#include "pyext.h" + + +PyMethodDef pyext::meth_tbl[] = +{ + {"__init__", pyext::pyext__init__, METH_VARARGS, "Constructor"}, + {"__del__", pyext::pyext__del__, METH_VARARGS, "Destructor"}, + + {"_outlet", pyext::pyext_outlet, METH_VARARGS,"Send message to outlet"}, + {"_tocanvas", pyext::pyext_tocanvas, METH_VARARGS,"Send message to canvas" }, + + { "_bind", pyext::pyext_bind, METH_VARARGS,"Bind function to a receiving symbol" }, + { "_unbind", pyext::pyext_unbind, METH_VARARGS,"Unbind function from a receiving symbol" }, +#ifdef FLEXT_THREADS + { "_detach", pyext::pyext_detach, METH_VARARGS,"Set detach flag for called methods" }, + { "_stop", pyext::pyext_stop, METH_VARARGS,"Stop running threads" }, +#endif + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + +PyMethodDef pyext::attr_tbl[] = +{ + { "__setattr__", pyext::pyext_setattr, METH_VARARGS,"Set class attribute" }, + { "__getattr__", pyext::pyext_getattr, METH_VARARGS,"Get class attribute" }, + { NULL, NULL,0,NULL }, +}; + + +const C *pyext::pyext_doc = + "py/pyext - python external object for PD and MaxMSP, (C)2002 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" + "_tocanvas(self,args...): Send a message to the parent canvas\n" +#ifdef FLEXT_THREADS + "_detach(self,int): Define whether a called Python method has its own thread\n" +#endif + "_bind(self,name,func): Bind a python function to a symbol\n" + "_unbind(self,name,func): Unbind a python function from a symbol\n" +; + +PyObject* pyext::pyext__init__(PyObject *,PyObject *args) +{ +// post("pyext.__init__ called"); + + Py_INCREF(Py_None); + return Py_None; +} + +PyObject* pyext::pyext__del__(PyObject *,PyObject *args) +{ +// post("pyext.__del__ called"); + + Py_INCREF(Py_None); + return Py_None; +} + +PyObject* pyext::pyext_setattr(PyObject *,PyObject *args) +{ + PyObject *self,*name,*val,*ret = NULL; + if(!PyArg_ParseTuple(args, "OOO:test_foo", &self,&name,&val)) { + // handle error + ERRINTERNAL(); + return NULL; + } + + BL handled = false; + if(PyString_Check(name)) { + char* sname = PyString_AsString(name); + if (sname) { +// post("pyext::setattr %s",sname); + } + } + + if(!handled) { + if(PyInstance_Check(self)) + PyDict_SetItem(((PyInstanceObject *)self)->in_dict, name,val); + else + ERRINTERNAL(); + } + + Py_INCREF(Py_None); + return Py_None; +} + +PyObject* pyext::pyext_getattr(PyObject *,PyObject *args) +{ + PyObject *self,*name,*ret = NULL; + if(!PyArg_ParseTuple(args, "OO:test_foo", &self,&name)) { + // handle error + ERRINTERNAL(); + } + + if(PyString_Check(name)) { + char* sname = PyString_AsString(name); + if (sname) { + if(!strcmp(sname,"_shouldexit")) { + pyext *ext = GetThis(self); + if(ext) + ret = PyLong_FromLong(ext->shouldexit?1:0); + } +// post("pyext::getattr %s",sname); + } + } + + if(!ret) { +#if PY_VERSION_HEX >= 0x02020000 + ret = PyObject_GenericGetAttr(self,name); +#else + if(PyInstance_Check(self)) + ret = PyDict_GetItem(((PyInstanceObject *)self)->in_dict,name); +#endif + } + return ret; +} + + +//! Send message to outlet +PyObject *pyext::pyext_outlet(PyObject *,PyObject *args) +{ + BL ok = false; + if(PySequence_Check(args)) { + PyObject *self = PySequence_GetItem(args,0); + PyObject *outl = PySequence_GetItem(args,1); + if( + self && PyInstance_Check(self) && + outl && PyInt_Check(outl) + ) { + pyext *ext = GetThis(self); + + I sz = PySequence_Size(args); + PyObject *val; + BL tp = sz == 3 && PySequence_Check(PySequence_GetItem(args,2)); + + if(tp) + val = PySequence_GetItem(args,2); // borrowed + else + val = PySequence_GetSlice(args,2,sz); // new ref + + AtomList *lst = GetPyArgs(val); + if(lst) { + I 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! + if(lst->Count() && IsSymbol((*lst)[0])) + ext->ToQueueAnything(o-1,GetSymbol((*lst)[0]),lst->Count()-1,lst->Atoms()+1); + else + ext->ToQueueList(o-1,*lst); + } + else + post("pyext: outlet index out of range"); + + ok = true; + } + else + post("py/pyext - No data to send"); + if(lst) delete lst; + + if(!tp) Py_DECREF(val); + } + } + + if(!ok) post("pyext - Syntax: _outlet(self,outlet,args...)"); + + Py_INCREF(Py_None); + return Py_None; +} + + + +#ifdef FLEXT_THREADS +//! Detach threads +PyObject *pyext::pyext_detach(PyObject *,PyObject *args) +{ + PyObject *self; + int val; + if(!PyArg_ParseTuple(args, "Oi:pyext_detach",&self,&val)) { + // handle error + post("pyext - Syntax: _detach(self,[0/1])"); + } + else { + pyext *ext = GetThis(self); + ext->m_detach(val != 0); + } + + Py_INCREF(Py_None); + return Py_None; +} + +//! Stop running threads +PyObject *pyext::pyext_stop(PyObject *,PyObject *args) +{ + PyObject *self; + int val = -1; + if(!PyArg_ParseTuple(args, "O|i:pyext_stop",&self,&val)) { + // handle error + post("pyext - Syntax: _stop(self,{wait time}"); + } + else { + pyext *ext = GetThis(self); + I cnt = 0; + t_atom at; + if(val >= 0) flext::SetInt(at,val); + ext->m_stop(cnt,&at); + } + + Py_INCREF(Py_None); + return Py_None; +} + +#endif + +//! Send message to canvas +PyObject *pyext::pyext_tocanvas(PyObject *,PyObject *args) +{ + BL ok = false; + if(PySequence_Check(args)) { + PyObject *self = PySequence_GetItem(args,0); + if(self && PyInstance_Check(self)) { + pyext *ext = GetThis(self); + +#ifdef PD + I sz = PySequence_Size(args); + PyObject *val; + BL tp = sz == 2 && PySequence_Check(PyTuple_GetItem(args,1)); + + if(tp) + val = PySequence_GetItem(args,1); // borrowed + else + val = PySequence_GetSlice(args,1,sz); // new ref + + AtomList *lst = GetPyArgs(val); + if(lst) { + t_glist *gl = ext->thisCanvas(); //canvas_getcurrent(); + t_class **cl = (t_pd *)gl; + if(cl) { +#ifdef PD + pd_forwardmess(cl,lst->Count(),lst->Atoms()); +#else + #pragma message ("Send is not implemented") +#endif + } +#ifdef _DEBUG + else + post("pyext - no parent canvas?!"); +#endif + ok = true; + } + else + post("py/pyext - No data to send"); + if(lst) delete lst; + + if(!tp) Py_DECREF(val); +#else +#pragma message ("Not implemented for MaxMSP") +#endif + } + } + + if(!ok) post("pyext - Syntax: _tocanvas(self,args...)"); + + Py_INCREF(Py_None); + return Py_None; +} + + + diff --git a/externals/grill/py/source/main.cpp b/externals/grill/py/source/main.cpp new file mode 100644 index 00000000..65e1d274 --- /dev/null +++ b/externals/grill/py/source/main.cpp @@ -0,0 +1,212 @@ +/* + +py/pyext - python external object for PD and MaxMSP + +Copyright (c) 2002 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. + +*/ + +#include "main.h" + + +V py::lib_setup() +{ + post(""); + post("py/pyext %s - python script objects, (C)2002 Thomas Grill",PY__VERSION); + post(""); + + FLEXT_SETUP(pyobj); + FLEXT_SETUP(pyext); + + pyref = 0; +} + +FLEXT_LIB_SETUP(py,py::lib_setup) + +PyInterpreterState *py::pystate = NULL; + + +I py::pyref = 0; +PyObject *py::module_obj = NULL; +PyObject *py::module_dict = NULL; + + +py::py(): + module(NULL), + detach(false),shouldexit(false),thrcount(0), + clk(NULL),stoptick(0) +{ + Lock(); + // under Max/MSP: doesn't survive next line..... + + if(!(pyref++)) { + Py_Initialize(); + + #ifdef FLEXT_THREADS + PyEval_InitThreads(); + + pystate = PyThreadState_Get()->interp; + #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); + + #ifdef FLEXT_THREADS + pythrmain = PyEval_SaveThread(); + #endif + } + else { + PY_LOCK + Py_INCREF(module_obj); + Py_INCREF(module_dict); + PY_UNLOCK + } + + Unlock(); + + clk = clock_new(this,(t_method)tick); +} + +py::~py() +{ + if(thrcount) { + shouldexit = true; + + // Wait for a certain time + for(int i = 0; i < (PY_STOP_WAIT/PY_STOP_TICK) && thrcount; ++i) Sleep((F)(PY_STOP_TICK/1000.)); + + // Wait forever + post("%s - Waiting for thread termination!",thisName()); + while(thrcount) Sleep(0.2f); + post("%s - Okay, all threads have terminated",thisName()); + } + +/* + // don't unregister + + Lock(); + + if(!(--pyref)) { + Py_DECREF(module_obj); + module_obj = NULL; + Py_DECREF(module_dict); + module_dict = NULL; + + Py_XDECREF(module); + +// delete modules; modules = NULL; + + PyEval_AcquireThread(pythrmain); + PyThreadState *new_state = PyThreadState_New(pystate); // must have lock + PyThreadState *prev_state = PyThreadState_Swap(new_state); + + Py_Finalize(); + } + + Unlock(); +*/ + if(clk) clock_free(clk); +} + + +V py::m_doc() +{ + if(dict) { + PyObject *docf = PyDict_GetItemString(dict,"__doc__"); // borrowed!!! + if(docf && PyString_Check(docf)) { + post(""); + post(PyString_AsString(docf)); + } + } +} + + + + +V py::SetArgs(I argc,t_atom *argv) +{ + // script arguments + C **sargv = new C *[argc+1]; + for(int i = 0; i <= argc; ++i) { + sargv[i] = new C[256]; + if(!i) + strcpy(sargv[i],thisName()); + else + GetAString(argv[i-1],sargv[i],255); + } + + // the arguments to the module are only recognized once! (at first use in a patcher) + PySys_SetArgv(argc+1,sargv); + + for(int j = 0; j <= argc; ++j) delete[] sargv[j]; + delete[] sargv; +} + +V py::ImportModule(const C *name) +{ + if(!name) return; + + module = PyImport_ImportModule((C *)name); + if (!module) { + PyErr_Print(); + dict = NULL; + } + else + dict = PyModule_GetDict(module); // borrowed + +} + + +V py::ReloadModule() +{ + if(module) { + PyObject *newmod = PyImport_ReloadModule(module); + if(!newmod) { + PyErr_Print(); + // old module still exists?! +// dict = NULL; + } + else { + Py_XDECREF(module); + module = newmod; + dict = PyModule_GetDict(module); // borrowed + } + } + else + post("%s - No module to reload",thisName()); +} + +V py::GetModulePath(const C *mod,C *dir,I len) +{ +#ifdef PD + // uarghh... pd doesn't show it's path for extra modules + + C *name; + I fd = open_via_path("",mod,".py",dir,&name,len,0); + if(fd > 0) close(fd); + else name = NULL; + + // if dir is current working directory... name points to dir + if(dir == name) strcpy(dir,"."); +#elif defined(MAXMSP) + *dir = 0; +#endif +} + +V py::AddToPath(const C *dir) +{ + if(dir && *dir) { + PyObject *pobj = PySys_GetObject("path"); + if(pobj && PyList_Check(pobj)) { + PyObject *ps = PyString_FromString(dir); + PyList_Append(pobj,ps); + } + PySys_SetObject("path",pobj); + } +} + + diff --git a/externals/grill/py/source/main.h b/externals/grill/py/source/main.h new file mode 100644 index 00000000..66b332cf --- /dev/null +++ b/externals/grill/py/source/main.h @@ -0,0 +1,147 @@ +/* + +py/pyext - python script object for PD and MaxMSP + +Copyright (c) 2002 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. + +*/ + +#ifndef __MAIN_H +#define __MAIN_H + +#include +#include +#ifndef NT +#include +#endif + +#if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 400) +#error You need at least flext version 0.4.0 +#endif + +#define PY__VERSION "0.1.1pre3" + + +#define PYEXT_MODULE "pyext" // name for module +#define PYEXT_CLASS "_class" // name for base class + +#define PY_STOP_WAIT 1000 // ms +#define PY_STOP_TICK 10 // ms + + + +#define I int +#define C char +#define V void +#define BL bool +#define F float +#define D double + + +#include "main.h" + +class py: + public flext_base +{ + FLEXT_HEADER(py,flext_base) + +public: + py(); + ~py(); + static V lib_setup(); + + static PyObject *MakePyArgs(const t_symbol *s,const AtomList &args,I inlet = -1,BL withself = false); + static AtomList *GetPyArgs(PyObject *pValue,PyObject **self = NULL); + +protected: + + V m_doc(); + + PyObject *module,*dict; // inherited user class module and associated dictionary + + static I pyref; + static const C *py_doc; + + V GetModulePath(const C *mod,C *dir,I len); + V AddToPath(const C *dir); + V SetArgs(I argc,t_atom *argv); + V ImportModule(const C *name); + V ReloadModule(); + + V Register(const C *reg); + V Unregister(const C *reg); + V Reregister(const C *reg); + virtual V Reload() = 0; + + 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; } + + enum retval { nothing,atom,sequ /*,tuple,list*/ }; + + // --- module stuff ----- + + static PyObject *module_obj,*module_dict; + static PyMethodDef func_tbl[]; + + static PyObject *py__doc__(PyObject *,PyObject *args); + static PyObject *py_send(PyObject *,PyObject *args); +#ifdef FLEXT_THREADS + static PyObject *py_priority(PyObject *,PyObject *args); +#endif + + 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); + + // ----thread stuff ------------ + + V m_detach(BL det) { detach = det; } + virtual V m_stop(int argc,t_atom *argv); + + BL detach,shouldexit; + I thrcount; + t_clock *clk; + I stoptick; + + static V tick(py *obj); + +public: + static PyInterpreterState *pystate; + PyThreadState *pythrmain; + +#ifdef FLEXT_THREADS + ThrMutex mutex; + V Lock() { mutex.Unlock(); } + V Unlock() { mutex.Unlock(); } +#else + V Lock() {} + V Unlock() {} +#endif + +protected: + // callbacks + + FLEXT_CALLBACK_B(m_detach) + FLEXT_CALLBACK_V(m_stop) + FLEXT_CALLBACK(m_doc) +}; + +#ifdef FLEXT_THREADS +#define PY_LOCK \ + { \ + PyThreadState *thrst = PyThreadState_New(pystate); \ + PyEval_AcquireThread(thrst); + +#define PY_UNLOCK \ + PyThreadState_Clear(thrst); /* must have lock */ \ + PyEval_ReleaseThread(thrst); \ + PyThreadState_Delete(thrst); /* needn't have lock */ \ + } +#else +#define PY_LOCK +#define PY_UNLOCK +#endif + +#endif diff --git a/externals/grill/py/source/modmeth.cpp b/externals/grill/py/source/modmeth.cpp new file mode 100644 index 00000000..51e103ed --- /dev/null +++ b/externals/grill/py/source/modmeth.cpp @@ -0,0 +1,183 @@ +/* + +py/pyext - python external object for PD and MaxMSP + +Copyright (c) 2002 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. + +*/ + +#include "main.h" + + +// function table for module +PyMethodDef py::func_tbl[] = +{ + { "_send", py::py_send, METH_VARARGS,"Send message to a named object" }, +#ifdef FLEXT_THREADS + { "_priority", py::py_priority, METH_VARARGS,"Set priority of current thread" }, +#endif + + { "_samplerate", py::py_samplerate, 0,"Get system sample rate" }, + { "_blocksize", py::py_blocksize, 0,"Get system block size" }, + { "_inchannels", py::py_inchannels, 0,"Get number of audio in channels" }, + { "_outchannels", py::py_outchannels, 0,"Get number of audio out channels" }, + + {NULL, NULL, 0, NULL} // sentinel +}; + +const C *py::py_doc = + "py/pyext - python external object for PD and MaxMSP, (C)2002 Thomas Grill\n" + "\n" + "This is the pyext module. Available function:\n" + "_send(args...): Send a message to a send symbol\n" +#ifdef FLEXT_THREADS + "_priority(int): Raise/lower thread priority\n" +#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" +; + + + +V py::tick(py *th) +{ + th->Lock(); + + if(!th->thrcount) { + // all threads have stopped + th->shouldexit = false; + th->stoptick = 0; + } + else { + // still active threads + if(!--th->stoptick) { + post("%s - Threads couldn't be stopped entirely - %i remaining",th->thisName(),th->thrcount); + th->shouldexit = false; + } + else + // continue waiting + clock_delay(th->clk,PY_STOP_TICK); + } + + th->Unlock(); +} + +V py::m_stop(int argc,t_atom *argv) +{ + if(thrcount) { + Lock(); + + I wait = PY_STOP_WAIT; + if(argc >= 1 && CanbeInt(argv[0])) wait = GetAInt(argv[0]); + + I ticks = wait/PY_STOP_TICK; + if(stoptick) { + // already stopping + if(ticks < stoptick) stoptick = ticks; + } + else + stoptick = ticks; + shouldexit = true; + clock_delay(clk,PY_STOP_TICK); + + Unlock(); + } + +} + +PyObject *py::py_samplerate(PyObject *self,PyObject *args) +{ + return PyFloat_FromDouble(sys_getsr()); +} + +PyObject *py::py_blocksize(PyObject *self,PyObject *args) +{ + return PyLong_FromLong(sys_getblksize()); +} + +PyObject *py::py_inchannels(PyObject *self,PyObject *args) +{ +#ifdef PD + I ch = sys_get_inchannels(); +#else // MAXMSP + I ch = sys_getch(); // not functioning +#endif + return PyLong_FromLong(ch); +} + +PyObject *py::py_outchannels(PyObject *self,PyObject *args) +{ +#ifdef PD + I ch = sys_get_outchannels(); +#else // MAXMSP + I ch = sys_getch(); // not functioning +#endif + return PyLong_FromLong(ch); +} + +PyObject *py::py_send(PyObject *,PyObject *args) +{ + if(PySequence_Check(args)) { + PyObject *name = PySequence_GetItem(args,0); // borrowed + if(name && PyString_Check(name)) { + const t_symbol *recv = MakeSymbol(PyString_AsString(name)); + I sz = PySequence_Size(args); + PyObject *val; + BL tp = sz == 2 && PySequence_Check(PySequence_GetItem(args,1)); + + if(tp) + val = PySequence_GetItem(args,1); // borrowed + else + val = PySequence_GetSlice(args,1,sz); // new ref + + AtomList *lst = GetPyArgs(val); + if(lst) { +// t_class **cl = (t_class **)GetBound(recv); + t_class **cl = (t_class **)recv->s_thing; + if(cl) { +#ifdef PD + pd_forwardmess(cl,lst->Count(),lst->Atoms()); +#else + #pragma message ("Send is not implemented") +#endif + } +#ifdef _DEBUG + else + post("py/pyext - Receiver doesn't exist"); +#endif + } + else + post("py/pyext - No data to send"); + if(lst) delete lst; + + if(!tp) Py_DECREF(val); + } + else + post("py/pyext - Send name is invalid"); + } + + Py_INCREF(Py_None); + return Py_None; +} + +#ifdef FLEXT_THREADS +PyObject *py::py_priority(PyObject *self,PyObject *args) +{ + int val; + if(!PyArg_ParseTuple(args, "i:py_priority", &val)) { + post("py/pyext - Syntax: _priority [int]"); + } + else + ChangePriority(val); + + Py_INCREF(Py_None); + return Py_None; +} +#endif + + + diff --git a/externals/grill/py/source/py.cpp b/externals/grill/py/source/py.cpp new file mode 100644 index 00000000..83c5d9b5 --- /dev/null +++ b/externals/grill/py/source/py.cpp @@ -0,0 +1,327 @@ +/* + +py/pyext - python script object for PD and MaxMSP + +Copyright (c) 2002 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. + +*/ + +#include "main.h" + + +class pyobj: + public py +{ + FLEXT_HEADER(pyobj,py) + +public: + pyobj(I argc,t_atom *argv); + ~pyobj(); + +protected: + BL m_method_(I n,const t_symbol *s,I argc,t_atom *argv); + + V work(const t_symbol *s,I argc,t_atom *argv); + + V m_bang() { work(sym_bang,0,NULL); } + V m_reload(); + V m_reload_(I argc,t_atom *argv); + V m_set(I argc,t_atom *argv); + V m_doc_(); + + virtual V m_help(); + + // methods for python arguments + V callwork(const t_symbol *s,I argc,t_atom *argv); + + V m_py_list(I argc,t_atom *argv) { callwork(sym_list,argc,argv); } + V m_py_float(I argc,t_atom *argv) { callwork(sym_float,argc,argv); } + V m_py_int(I argc,t_atom *argv) { callwork(sym_int,argc,argv); } + V m_py_any(const t_symbol *s,I argc,t_atom *argv) { callwork(s,argc,argv); } + + const t_symbol *funname; + PyObject *function; + + virtual V Reload(); + + V SetFunction(const C *func); + V ResetFunction(); + +private: + + FLEXT_CALLBACK(m_bang) + FLEXT_CALLBACK(m_reload) + FLEXT_CALLBACK_V(m_reload_) + FLEXT_CALLBACK_V(m_set) + FLEXT_CALLBACK(m_doc_) + + FLEXT_CALLBACK_V(m_py_float) + FLEXT_CALLBACK_V(m_py_list) + FLEXT_CALLBACK_V(m_py_int) + FLEXT_CALLBACK_A(m_py_any) + +#ifdef FLEXT_THREADS + FLEXT_THREAD_A(work) +#else + FLEXT_CALLBACK_A(work) +#endif +}; + +FLEXT_LIB_V("py",pyobj) + + +pyobj::pyobj(I argc,t_atom *argv): + function(NULL),funname(NULL) +{ + PY_LOCK + + AddInAnything(2); + AddOutAnything(); + + FLEXT_ADDBANG(0,m_bang); + FLEXT_ADDMETHOD_(0,"reload",m_reload_); + FLEXT_ADDMETHOD_(0,"reload.",m_reload); + FLEXT_ADDMETHOD_(0,"set",m_set); + FLEXT_ADDMETHOD_(0,"doc",m_doc); + FLEXT_ADDMETHOD_(0,"doc+",m_doc_); +#ifdef FLEXT_THREADS + FLEXT_ADDMETHOD_(0,"detach",m_detach); + FLEXT_ADDMETHOD_(0,"stop",m_stop); +#endif + + FLEXT_ADDMETHOD_(1,"float",m_py_float); + FLEXT_ADDMETHOD_(1,"int",m_py_int); + FLEXT_ADDMETHOD(1,m_py_list); + FLEXT_ADDMETHOD(1,m_py_any); + + if(argc > 2) + SetArgs(argc-2,argv+2); + else + SetArgs(0,NULL); + + // init script module + if(argc >= 1) { + C dir[1024]; + GetModulePath(GetString(argv[0]),dir,sizeof(dir)); + // set script path + AddToPath(dir); + + if(!IsString(argv[0])) + post("%s - script name argument is invalid",thisName()); + else + ImportModule(GetString(argv[0])); + } + + Register("_py"); + + if(argc >= 2) { + // set function name + if(!IsString(argv[1])) + post("%s - function name argument is invalid",thisName()); + else { + // Set function + SetFunction(GetString(argv[1])); + } + } + + PY_UNLOCK +} + +pyobj::~pyobj() +{ + PY_LOCK + Unregister("_py"); + PY_UNLOCK +} + + + + +BL pyobj::m_method_(I n,const t_symbol *s,I argc,t_atom *argv) +{ + if(n == 1) + post("%s - no method for type %s",thisName(),GetString(s)); + return false; +} + +V pyobj::m_reload() +{ + PY_LOCK + + Unregister("_py"); + + ReloadModule(); + Reregister("_py"); + Register("_py"); + SetFunction(funname?GetString(funname):NULL); + + PY_UNLOCK +} + +V pyobj::m_reload_(I argc,t_atom *argv) +{ + PY_LOCK + SetArgs(argc,argv); + PY_UNLOCK + + m_reload(); +} + +V pyobj::m_set(I argc,t_atom *argv) +{ + PY_LOCK + + I ix = 0; + if(argc >= 2) { + if(!IsString(argv[ix])) { + post("%s - script name is not valid",thisName()); + return; + } + const C *sn = GetString(argv[ix]); + + if(!module || !strcmp(sn,PyModule_GetName(module))) { + ImportModule(sn); + Register("_py"); + } + + ++ix; + } + + if(!IsString(argv[ix])) + post("%s - function name is not valid",thisName()); + else + SetFunction(GetString(argv[ix])); + + PY_UNLOCK +} + + +V pyobj::m_doc_() +{ + PY_LOCK + + if(function) { + PyObject *docf = PyObject_GetAttrString(function,"__doc__"); // borrowed!!! + if(docf && PyString_Check(docf)) { + post(""); + post(PyString_AsString(docf)); + } + } + + PY_UNLOCK +} + + +V pyobj::m_help() +{ + post(""); + post("py %s - python script object, (C)2002 Thomas Grill",PY__VERSION); +#ifdef _DEBUG + post("compiled on " __DATE__ " " __TIME__); +#endif + + post("Arguments: %s [script name] [function name] {args...}",thisName()); + + post("Inlet 1:messages to control the py object"); + post(" 2:call python function with message as argument(s)"); + post("Outlet: 1:return values from python function"); + post("Methods:"); + post("\thelp: shows this help"); + post("\tbang: call script without arguments"); + post("\tset [script name] [function name]: set (script and) function name"); + post("\treload {args...}: reload python script"); + post("\treload. : reload with former arguments"); + post("\tdoc: display module doc string"); + post("\tdoc+: display function doc string"); +#ifdef FLEXT_THREADS + post("\tdetach 0/1: detach threads"); + post("\tstop {wait time (ms)}: stop threads"); +#endif + post(""); +} + +V pyobj::ResetFunction() +{ + if(!module || !dict) + { + post("%s - No module loaded",thisName()); + function = NULL; + return; + } + + function = funname?PyDict_GetItemString(dict,(C *)GetString(funname)):NULL; // borrowed!!! + if(!function) { + PyErr_Clear(); + if(funname) post("%s - Function %s could not be found",thisName(),GetString(funname)); + } + else if(!PyFunction_Check(function)) { + post("%s - Object %s is not a function",thisName(),GetString(funname)); + function = NULL; + } +} + +V pyobj::SetFunction(const C *func) +{ + if(func) { + funname = MakeSymbol(func); + ResetFunction(); + } + else + function = NULL,funname = NULL; +} + + +V pyobj::Reload() +{ + ResetFunction(); +} + + +V pyobj::work(const t_symbol *s,I argc,t_atom *argv) +{ + AtomList *rargs = NULL; + + ++thrcount; + PY_LOCK + + if(function) { + PyObject *pArgs = MakePyArgs(s,AtomList(argc,argv)); + PyObject *pValue = PyObject_CallObject(function, pArgs); + + rargs = GetPyArgs(pValue); + if(!rargs) PyErr_Print(); + + Py_XDECREF(pArgs); + Py_XDECREF(pValue); + } + else { + post("%s: no function defined",thisName()); + } + + PY_UNLOCK + --thrcount; + + if(rargs) { + // call to outlet _outside_ the Mutex lock! + // otherwise (if not detached) deadlock will occur + ToOutList(0,*rargs); + delete rargs; + } +} + +V pyobj::callwork(const t_symbol *s,I argc,t_atom *argv) +{ + if(detach) { + if(shouldexit) + post("%s - New threads can't be launched now!",thisName()); + else + if(!FLEXT_CALLMETHOD_A(work,s,argc,argv)) + post("%s - Failed to launch thread!",thisName()); + } + else + work(s,argc,argv); +} + + diff --git a/externals/grill/py/source/pyargs.cpp b/externals/grill/py/source/pyargs.cpp new file mode 100644 index 00000000..be4adaa1 --- /dev/null +++ b/externals/grill/py/source/pyargs.cpp @@ -0,0 +1,131 @@ +/* + +py/pyext - python external object for PD and MaxMSP + +Copyright (c) 2002 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. + +*/ + +#include "main.h" + + +PyObject *py::MakePyArgs(const t_symbol *s,const AtomList &args,I inlet,BL withself) +{ + PyObject *pArgs; + + BL any = IsAnything(s); + pArgs = PyTuple_New(args.Count()+(any?1:0)+(inlet >= 0?1:0)); + + I pix = 0; + + if(inlet >= 0) { + PyObject *pValue = PyInt_FromLong(inlet); + + // reference stolen: + PyTuple_SetItem(pArgs, pix++, pValue); + } + + 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(any) { + PyObject *pValue = PyString_FromString(GetString(s)); + + // reference stolen here: + PyTuple_SetItem(tmp, ix++, pValue); + } + + for(I i = 0; i < args.Count(); ++i) { + PyObject *pValue = NULL; + + if(IsFloat(args[i])) pValue = PyFloat_FromDouble((D)GetFloat(args[i])); + else if(IsInt(args[i])) pValue = PyInt_FromLong(GetInt(args[i])); + else if(IsSymbol(args[i])) pValue = PyString_FromString(GetString(args[i])); + else if(IsPointer(args[i])) pValue = NULL; // not handled + + if(!pValue) { + post("py/pyext: cannot convert argument %i",any?i+1:i); + continue; + } + + /* pValue reference stolen here: */ + PyTuple_SetItem(tmp, ix++, pValue); + } + + if(tmp != pArgs) { + PyTuple_SetItem(pArgs, pix++, tmp); +#if PY_VERSION_HEX >= 0x02020000 + _PyTuple_Resize(&pArgs,pix); +#else + _PyTuple_Resize(&pArgs,pix,0); +#endif + } + + return pArgs; +} + +flext::AtomList *py::GetPyArgs(PyObject *pValue,PyObject **self) +{ + if(pValue == NULL) return NULL; + AtomList *ret = NULL; + + // analyze return value or tuple + + I rargc = 0; + BL ok = true; + retval tp = nothing; + + if(PyString_Check(pValue)) { + rargc = 1; + tp = atom; + } + else if(PySequence_Check(pValue)) { + rargc = PySequence_Size(pValue); + tp = sequ; + } + else { + rargc = 1; + tp = atom; + } + + ret = new AtomList(rargc); + + for(I ix = 0; ix < rargc; ++ix) { + PyObject *arg; + switch(tp) { + case sequ: arg = PySequence_GetItem(pValue,ix); break; + default: arg = pValue; + } + + 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(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_ !!! + *self = arg; + } + else { + PyObject *tp = PyObject_Type(arg); + PyObject *stp = tp?PyObject_Str(tp):NULL; + C *tmp = ""; + if(stp) tmp = PyString_AsString(stp); + post("py/pyext: Could not convert argument %s",tmp); + Py_XDECREF(stp); + Py_XDECREF(tp); + ok = false; + } + // No DECREF for arg -> borrowed from pValue! + } + + if(!ok) { + delete ret; + ret = NULL; + } + return ret; +} + diff --git a/externals/grill/py/source/pyext.cpp b/externals/grill/py/source/pyext.cpp new file mode 100644 index 00000000..8e971873 --- /dev/null +++ b/externals/grill/py/source/pyext.cpp @@ -0,0 +1,465 @@ +/* + +py/pyext - python script object for PD and MaxMSP + +Copyright (c) 2002 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. + +*/ + +#include "pyext.h" +#include + + +FLEXT_LIB_V("pyext",pyext) + +V pyext::setup(t_class *) +{ + px_head = px_tail = NULL; + + px_class = class_new(gensym("pyext proxy"),NULL,NULL,sizeof(py_proxy),CLASS_PD|CLASS_NOINLET, A_NULL); + ::add_anything(px_class,py_proxy::px_method); // for other inlets +} + +pyext *pyext::GetThis(PyObject *self) +{ + PyObject *th = PyObject_GetAttrString(self,"_this"); + pyext *ret = th?(pyext *)PyLong_AsVoidPtr(th):NULL; + PyErr_Clear(); + Py_XDECREF(th); + return ret; +} + + +I pyext::pyextref = 0; +PyObject *pyext::class_obj = NULL; +PyObject *pyext::class_dict = NULL; + +pyext::pyext(I argc,t_atom *argv): + pyobj(NULL),pythr(NULL), + inlets(0),outlets(0), + methname(NULL) +{ + 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); + } + + // init script module + if(argc >= 1) { + C dir[1024]; +#ifdef PD + // add dir of current patch to path + strcpy(dir,GetString(canvas_getdir(thisCanvas()))); + AddToPath(dir); + // add current dir to path + strcpy(dir,GetString(canvas_getcurrentdir())); + AddToPath(dir); +#else + #pragma message("Adding current dir to path is not implemented") +#endif + + GetModulePath(GetString(argv[0]),dir,sizeof(dir)); + // add to path + AddToPath(dir); + + if(!IsString(argv[0])) + post("%s - script name argument is invalid",thisName()); + else { + SetArgs(0,NULL); + ImportModule(GetString(argv[0])); + } + } + + Register("_pyext"); + +// t_symbol *sobj = NULL; + if(argc >= 2) { + // object name + if(!IsString(argv[1])) + post("%s - object name argument is invalid",thisName()); + else { + methname = GetSymbol(argv[1]); + } + + args(argc-2,argv+2); + } + + if(methname) { + SetClssMeth(); + + // now get number of inlets and outlets + inlets = 1,outlets = 1; + + if(pyobj) { + PyObject *res; + res = PyObject_GetAttrString(pyobj,"_inlets"); // get ref + if(res) { + if(PyCallable_Check(res)) { + PyObject *fres = PyEval_CallObject(res,NULL); + Py_DECREF(res); + res = fres; + } + if(PyInt_Check(res)) + inlets = PyInt_AsLong(res); + Py_DECREF(res); + } + else + PyErr_Clear(); + + res = PyObject_GetAttrString(pyobj,"_outlets"); // get ref + if(res) { + if(PyCallable_Check(res)) { + PyObject *fres = PyEval_CallObject(res,NULL); + Py_DECREF(res); + res = fres; + } + if(PyInt_Check(res)) + outlets = PyInt_AsLong(res); + Py_DECREF(res); + } + else + PyErr_Clear(); + } + } + + PY_UNLOCK + + AddInAnything(1+inlets); + AddOutAnything(outlets); + + FLEXT_ADDMETHOD_(0,"reload.",m_reload); + FLEXT_ADDMETHOD_(0,"reload",m_reload_); + FLEXT_ADDMETHOD_(0,"doc",m_doc); + FLEXT_ADDMETHOD_(0,"doc+",m_doc_); + +#ifdef FLEXT_THREADS + FLEXT_ADDMETHOD_(0,"detach",m_detach); + FLEXT_ADDMETHOD_(0,"stop",m_stop); +#endif + + if(!pyobj) + InitProblem(); +} + +pyext::~pyext() +{ + PY_LOCK + + ClearBinding(); + Unregister("_pyext"); + + Py_XDECREF(pyobj); + + Py_XDECREF(class_obj); + Py_XDECREF(class_dict); +/* + // Don't unregister + + if(!--pyextref) { + class_obj = NULL; + class_dict = NULL; + } +*/ + PY_UNLOCK +} + +BL pyext::SetClssMeth() //I argc,t_atom *argv) +{ + // pyobj should already have been decref'd / cleared before getting here!! + + if(module && methname) { + PyObject *pref = PyObject_GetAttrString(module,const_cast(GetString(methname))); + if(!pref) + PyErr_Print(); + else if(PyClass_Check(pref)) { + // make instance, but don't call __init__ + pyobj = PyInstance_NewRaw(pref,NULL); + + Py_DECREF(pref); + if(pyobj == NULL) + PyErr_Print(); + else { + // remember the this pointer + PyObject *th = PyLong_FromVoidPtr(this); + int ret = PyObject_SetAttrString(pyobj,"_this",th); // ref is taken + + // 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 *init; + init = PyObject_GetAttrString(pyobj,"__init__"); // get ref + if(init && PyCallable_Check(init)) { + PyObject *res = PyEval_CallObject(init,pargs); + if(!res) + PyErr_Print(); + else + Py_DECREF(res); + } + + Py_XDECREF(pargs); + } + } + else + post("%s - Type of \"%s\" is unhandled!",thisName(),GetString(methname)); + return true; + } + else + return false; +} + +V pyext::Reload() +{ + ClearBinding(); + Py_XDECREF(pyobj); + // by here, the Python class destructor should have been called! + + SetArgs(0,NULL); + ReloadModule(); + + SetClssMeth(); +} + + +V pyext::m_reload() +{ + PY_LOCK + + Unregister("_pyext"); // self + + Reload(); + + Reregister("_pyext"); // the others + Register("_pyext"); // self + + PY_UNLOCK +} + +V pyext::m_reload_(I argc,t_atom *argv) +{ + args(argc,argv); + m_reload(); +} + +V pyext::m_doc_() +{ + if(pyobj) { + PY_LOCK + + PyObject *docf = PyObject_GetAttrString(pyobj,"__doc__"); // borrowed!!! + if(docf && PyString_Check(docf)) { + post(""); + post(PyString_AsString(docf)); + } + + PY_UNLOCK + } +} + + + + +BL pyext::m_method_(I n,const t_symbol *s,I argc,t_atom *argv) +{ + if(pyobj && n >= 1) { + return callwork(n,s,argc,argv); + } + else { + post("%s - no method for type '%s' into inlet %i",thisName(),GetString(s),n); + return false; + } +} + + +V pyext::m_help() +{ + post(""); + post("pyext %s - python script object, (C)2002 Thomas Grill",PY__VERSION); +#ifdef _DEBUG + post("compiled on " __DATE__ " " __TIME__); +#endif + + post("Arguments: %s [script name] [class name] {args...}",thisName()); + + post("Inlet 1: messages to control the pyext object"); + post(" 2...: python inlets"); + post("Outlets: python outlets"); + post("Methods:"); + post("\thelp: shows this help"); + post("\treload {args...}: reload python script"); + post("\treload. : reload with former arguments"); + post("\tdoc: display module doc string"); + post("\tdoc+: display class doc string"); +#ifdef FLEXT_THREADS + post("\tdetach 0/1: detach threads"); + post("\tstop {wait time (ms)}: stop threads"); +#endif + post(""); +} + +PyObject *pyext::call(const C *meth,I inlet,const t_symbol *s,I argc,t_atom *argv) +{ + PyObject *ret = NULL; + + PyObject *pmeth = PyObject_GetAttrString(pyobj,const_cast(meth)); /* fetch bound method */ + if(pmeth == NULL) { + PyErr_Clear(); // no method found + } + else { + PyObject *pargs = MakePyArgs(s,AtomList(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 _DEBUG + PyErr_Print(); +#else + PyErr_Clear(); +#endif + else { +// Py_DECREF(pres); + } + + Py_DECREF(pargs); + } + Py_DECREF(pmeth); + } + + return ret; +} + +V pyext::work_wrapper(V *data) +{ + ++thrcount; +#ifdef _DEBUG + if(!data) + post("%s - no data!",thisName()); + else +#endif + { + work_data *w = (work_data *)data; + work(w->n,w->Header(),w->Count(),w->Atoms()); +// delete w; + } + --thrcount; +} + +BL pyext::callwork(I n,const t_symbol *s,I argc,t_atom *argv) +{ + if(detach) { + if(shouldexit) { + post("%s - Stopping.... new threads can't be launched now!",thisName()); + return true; + } + else { + BL ret = FLEXT_CALLMETHOD_X(work_wrapper,new work_data(n,s,argc,argv)); + if(!ret) post("%s - Failed to launch thread!",thisName()); + return true; + } + } + else + return work(n,s,argc,argv); +} + +BL pyext::work(I n,const t_symbol *s,I argc,t_atom *argv) +{ + BL retv = false; + + PY_LOCK + + PyObject *ret = NULL; + char *str = new char[strlen(GetString(s))+10]; + + { + // try tag/inlet + sprintf(str,"%s_%i",GetString(s),n); + ret = call(str,0,NULL,argc,argv); + } + + if(!ret) { + // try anything/inlet + sprintf(str,"_anything_%i",n); + if(s == sym_bang && !argc) { + t_atom argv; + SetString(argv,""); + ret = call(str,0,s,1,&argv); + } + else + ret = call(str,0,s,argc,argv); + } + if(!ret) { + // try tag at any inlet + sprintf(str,"%s_",GetString(s)); + ret = call(str,n,NULL,argc,argv); + } + if(!ret) { + // try anything at any inlet + strcpy(str,"_anything_"); + if(s == sym_bang && !argc) { + t_atom argv; + SetString(argv,""); + ret = call(str,n,s,1,&argv); + } + else + ret = call(str,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; + + if(ret) { + if(!PyObject_Not(ret)) post("%s - returned value is ignored",thisName()); + Py_DECREF(ret); + retv = true; + } + + PY_UNLOCK + + return retv; +} + + + diff --git a/externals/grill/py/source/pyext.h b/externals/grill/py/source/pyext.h new file mode 100644 index 00000000..2aba0e8e --- /dev/null +++ b/externals/grill/py/source/pyext.h @@ -0,0 +1,127 @@ +/* + +py/pyext - python external object for PD and MaxMSP + +Copyright (c) 2002 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. + +*/ + +#ifndef __PYEXT_H +#define __PYEXT_H + +#include "main.h" + +class pyext: + public py +{ + FLEXT_HEADER_S(pyext,py,setup) + +public: + pyext(I argc,t_atom *argv); + ~pyext(); + + static PyObject *pyext__doc__(PyObject *,PyObject *args); + static PyObject *pyext__init__(PyObject *,PyObject *args); + static PyObject *pyext__del__(PyObject *,PyObject *args); + + static PyObject *pyext_outlet(PyObject *,PyObject *args); + static PyObject *pyext_tocanvas(PyObject *,PyObject *args); + + static PyObject *pyext_setattr(PyObject *,PyObject *args); + static PyObject *pyext_getattr(PyObject *,PyObject *args); + + static PyObject *pyext_detach(PyObject *,PyObject *args); + static PyObject *pyext_stop(PyObject *,PyObject *args); + + I Inlets() const { return inlets; } + I Outlets() const { return outlets; } + +protected: + BL m_method_(I n,const t_symbol *s,I argc,t_atom *argv); + + BL work(I n,const t_symbol *s,I argc,t_atom *argv); + + V m_reload(); + V m_reload_(I argc,t_atom *argv); + V m_doc_(); + virtual V m_help(); + + const t_symbol *methname; + PyObject *pyobj; + I inlets,outlets; + +private: + static V setup(t_class *); + + static pyext *GetThis(PyObject *self); + V ClearBinding(); + BL SetClssMeth(); //I argc,t_atom *argv); + + 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; + + // -------- bound stuff ------------------ + + static t_class *px_class; + + friend class py_proxy; + + class py_proxy // no virtual table! + { + public: + t_object obj; // MUST reside at memory offset 0 + PyObject *self,*func; + t_symbol *name; + + py_proxy *nxt; + + void init(t_symbol *n,PyObject *s,PyObject *f) { name = n,self = s,func = f,nxt = NULL; } +// bool cmp(PyObject *s,PyObject *f) const { return self == s && func == f; } +// void init(PyObject *s,char *f) { self = s,func = f,nxt = NULL; } +// bool cmp(PyObject *s,char *f) const { return self == s && func == f; } + static void px_method(py_proxy *c,const t_symbol *s,int argc,t_atom *argv); + }; + static py_proxy *px_head,*px_tail; + + static PyObject *pyext_bind(PyObject *,PyObject *args); + static PyObject *pyext_unbind(PyObject *,PyObject *args); + + // --------------------------- + + PyObject *call(const C *meth,I inlet,const t_symbol *s,I argc,t_atom *argv); + + V work_wrapper(void *data); + BL callwork(I n,const t_symbol *s,I argc,t_atom *argv); + + class work_data: + public flext::AtomAnything + { + public: + work_data(I _n,const t_symbol *_s,I _argc,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 + + PyThreadState *pythr; + +private: + FLEXT_CALLBACK(m_reload) + FLEXT_CALLBACK_V(m_reload_) + FLEXT_CALLBACK(m_doc_) +}; + + +#endif \ No newline at end of file diff --git a/externals/grill/py/source/register.cpp b/externals/grill/py/source/register.cpp new file mode 100644 index 00000000..63a9f952 --- /dev/null +++ b/externals/grill/py/source/register.cpp @@ -0,0 +1,86 @@ +/* + +py/pyext - python external object for PD and MaxMSP + +Copyright (c) 2002 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. + +*/ + +#include "main.h" + + +V py::Register(const C *regnm) +{ + if(module) { + // add this to module registry + + PyObject *reg = PyDict_GetItemString(dict,(C *)regnm); // borrowed!!! + PyObject *add = Py_BuildValue("[i]",(long)this); + if(!reg || !PyList_Check(reg)) { + if(PyDict_SetItemString(dict,(C *)regnm,add)) { + post("%s - Could not set registry",thisName()); + } + // Py_XDECREF(reg); + } + else { + PySequence_InPlaceConcat(reg,add); + } + } + +} + +V py::Unregister(const C *regnm) +{ + if(module) { + // remove this from module registry + + PyObject *reg = PyDict_GetItemString(dict,(C *)regnm); // borrowed!!! + PyObject *add = Py_BuildValue("i",(int)this); + if(!reg || !PySequence_Check(reg)) + post("%s - Registry not found!?",thisName()); + else { + I ix = PySequence_Index(reg,add); + if(ix < 0) { + post("%s - This object not found in registry?!",thisName()); + } + else { + PySequence_DelItem(reg,ix); + } + } + Py_DECREF(add); + } + +} + +V py::Reregister(const C *regnm) +{ + if(module) { + // remove this from module registry + + PyObject *reg = PyDict_GetItemString(dict,(C *)regnm); // borrowed!!! + + if(!reg || !PySequence_Check(reg)) + post("%s - Registry not found!?",thisName()); + else { + I cnt = PySequence_Size(reg); + for(I i = 0; i < cnt; ++i) { + PyObject *it = PySequence_GetItem(reg,i); // borrowed!! + if(!it || !PyInt_Check(it)) { + post("%s - Corrupt registry?!",thisName()); + } + else { + py *th = (py *)PyInt_AsLong(it); + th->module = module; + th->dict = dict; + th->Reload(); + } + } + } + } + +} + + + -- cgit v1.2.1