diff options
Diffstat (limited to 'externals/grill/py/source/pyext.cpp')
-rw-r--r-- | externals/grill/py/source/pyext.cpp | 465 |
1 files changed, 465 insertions, 0 deletions
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 <flinternal.h> + + +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<C *>(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<char *>(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; +} + + + |