From 897b80c5585f7c9031ff1aafb504c21a9d3b1606 Mon Sep 17 00:00:00 2001 From: Thomas Grill Date: Fri, 8 Jul 2005 14:30:31 +0000 Subject: better reload handling, but still far fom perfect fixed minor other issues cleaned up float vs. int pyext tags simplifications in py and pyext bumped version number python-like dotted module.function syntax send and receive wrapped PyObjects through inlets/outlets multiply inlets for py (hot and cold inlets) svn path=/trunk/; revision=3308 --- externals/grill/py/package.txt | 5 +- externals/grill/py/pd/simple-1.pd | 2 +- externals/grill/py/pd/simple-3.pd | 21 +- externals/grill/py/py.vcproj | 1177 ++++++++++++++++---------------- externals/grill/py/readme.txt | 7 + externals/grill/py/source/bound.cpp | 2 +- externals/grill/py/source/clmeth.cpp | 30 +- externals/grill/py/source/main.cpp | 621 +---------------- externals/grill/py/source/main.h | 197 +----- externals/grill/py/source/modmeth.cpp | 2 +- externals/grill/py/source/py.cpp | 168 +++-- externals/grill/py/source/pyargs.cpp | 146 ++-- externals/grill/py/source/pyatom.cpp | 61 ++ externals/grill/py/source/pyatom.h | 19 + externals/grill/py/source/pybase.cpp | 672 ++++++++++++++++++ externals/grill/py/source/pybase.h | 206 ++++++ externals/grill/py/source/pybuffer.cpp | 2 +- externals/grill/py/source/pyext.cpp | 141 ++-- externals/grill/py/source/pyext.h | 6 +- externals/grill/py/source/pyprefix.h | 12 + externals/grill/py/source/register.cpp | 2 +- 21 files changed, 1882 insertions(+), 1617 deletions(-) create mode 100644 externals/grill/py/source/pyatom.cpp create mode 100644 externals/grill/py/source/pyatom.h create mode 100644 externals/grill/py/source/pybase.cpp create mode 100644 externals/grill/py/source/pybase.h (limited to 'externals/grill/py') diff --git a/externals/grill/py/package.txt b/externals/grill/py/package.txt index cad479b6..31637831 100644 --- a/externals/grill/py/package.txt +++ b/externals/grill/py/package.txt @@ -10,6 +10,7 @@ SRCS= \ main.cpp \ py.cpp pyext.cpp modmeth.cpp clmeth.cpp \ register.cpp bound.cpp pyargs.cpp \ - pysymbol.cpp pybuffer.cpp pydsp.cpp + pysymbol.cpp pybuffer.cpp pydsp.cpp \ + pyatom.cpp pybase.cpp -HDRS= pyprefix.h main.h pyext.h pysymbol.h pybuffer.h +HDRS= pyprefix.h main.h pyext.h pysymbol.h pybuffer.h pyatom.h pybase.h diff --git a/externals/grill/py/pd/simple-1.pd b/externals/grill/py/pd/simple-1.pd index e3ef6754..bfe137de 100644 --- a/externals/grill/py/pd/simple-1.pd +++ b/externals/grill/py/pd/simple-1.pd @@ -1,4 +1,4 @@ -#N canvas 156 192 674 401 12; +#N canvas 156 192 682 409 12; #X obj 53 123 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 -1; #X floatatom 52 155 5 0 0 0 - - -; diff --git a/externals/grill/py/pd/simple-3.pd b/externals/grill/py/pd/simple-3.pd index ceebbcf3..5874991e 100644 --- a/externals/grill/py/pd/simple-3.pd +++ b/externals/grill/py/pd/simple-3.pd @@ -1,4 +1,4 @@ -#N canvas 136 275 653 365 12; +#N canvas 136 275 657 369 12; #X msg 73 266 help; #X text 17 80 This demonstrates message handling. See the simple.py file.; @@ -7,7 +7,6 @@ file.; #X floatatom 258 305 5 0 0 0 - - -; #X floatatom 316 119 5 0 0 0 - - -; #X floatatom 399 119 5 0 0 0 - - -; -#X obj 210 266 pyext simple ex3 1; #X msg 24 182 reload.; #X msg 24 210 reload -10; #X text 95 181 reload script and keep arguments; @@ -19,11 +18,13 @@ file.; -1 0; #X text 213 16 Python script objects \, (C)2003-2005 Thomas Grill; #X text 213 32 http://grrrr.org/ext; -#X connect 0 0 7 0; -#X connect 2 0 7 0; -#X connect 3 0 7 0; -#X connect 5 0 7 1; -#X connect 6 0 7 2; -#X connect 7 0 4 0; -#X connect 8 0 7 0; -#X connect 9 0 7 0; +#X obj 210 266 pyext simple.ex3 1; +#X text 369 258 the name can also be given in dotted Python style; +#X connect 0 0 17 0; +#X connect 2 0 17 0; +#X connect 3 0 17 0; +#X connect 5 0 17 1; +#X connect 6 0 17 2; +#X connect 7 0 17 0; +#X connect 8 0 17 0; +#X connect 17 0 4 0; diff --git a/externals/grill/py/py.vcproj b/externals/grill/py/py.vcproj index b2cafb10..a103c872 100644 --- a/externals/grill/py/py.vcproj +++ b/externals/grill/py/py.vcproj @@ -634,176 +634,616 @@ Name="make" Filter=""> + RelativePath=".\package.txt"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + RelativePath="source\clmeth.cpp"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + RelativePath="source\pyext.cpp"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + RelativePath="source\pyext.h"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/externals/grill/py/readme.txt b/externals/grill/py/readme.txt index 526b3299..309e99d5 100644 --- a/externals/grill/py/readme.txt +++ b/externals/grill/py/readme.txt @@ -85,6 +85,13 @@ It has been thoroughly tested with versions 2.2 to 2.4 Version history: +0.2.1: +- FIX: some simplifications in py and pyext +- ADD: Python objects can be sent/received through outlets/inlets +- ADD: py can have multiple inlets for multiple function arguments (right inlets are non-triggering) +- ADD: allow module.function syntax for py and pyext +- FIX: pyext: cleanup up float vs. int ... first decision is made by tag, afterwards a conversion is tried + 0.2.0: - ADD: handling of Python threads - FIX: output of single atoms instead of 1-element lists diff --git a/externals/grill/py/source/bound.cpp b/externals/grill/py/source/bound.cpp index a5ace4ee..4c57f65f 100644 --- a/externals/grill/py/source/bound.cpp +++ b/externals/grill/py/source/bound.cpp @@ -66,7 +66,7 @@ bool pyext::boundmeth(flext_base *th,t_symbol *sym,int argc,t_atom *argv,void *d PyThreadState *state = pyth->PyLock(); - PyObject *args = MakePyArgs(sym,argc,argv,-1,obj->self != NULL); + PyObject *args = MakePyArgs(sym,argc,argv); // call all functions bound by this symbol for(FuncSet::iterator it = obj->funcs.begin(); it != obj->funcs.end(); ++it) { diff --git a/externals/grill/py/source/clmeth.cpp b/externals/grill/py/source/clmeth.cpp index a8e1ceb7..303b5888 100644 --- a/externals/grill/py/source/clmeth.cpp +++ b/externals/grill/py/source/clmeth.cpp @@ -177,28 +177,18 @@ PyObject *pyext::pyext_outlet(PyObject *,PyObject *args) if(!tp) val = PySequence_GetSlice(args,2,sz); // new ref - flext::AtomListStatic<16> lst; - if(GetPyArgs(lst,val)) { - int o = PyInt_AsLong(outl); - if(o >= 1 && o <= ext->Outlets()) { - // offset outlet by signal outlets - o += ext->sigoutlets; - - // 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->ToOutAnything(o-1,GetSymbol(lst[0]),lst.Count()-1,lst.Atoms()+1); - else if(lst.Count() > 1) - ext->ToOutList(o-1,lst); - else - ext->ToOutAtom(o-1,*lst.Atoms()); + int o = PyInt_AsLong(outl); + if(o >= 1 && o <= ext->Outlets()) { + // offset outlet by signal outlets + o += ext->sigoutlets; + + if(ext->OutObject(ext,o,val)) ok = true; - } - else - PyErr_SetString(PyExc_ValueError,"pyext - _outlet: index out of range"); + else + PyErr_SetString(PyExc_ValueError,"pyext - _outlet: invalid arguments"); } - else - PyErr_SetString(PyExc_ValueError,"pyext - _outlet: invalid arguments"); + else + PyErr_SetString(PyExc_ValueError,"pyext - _outlet: index out of range"); if(!tp) Py_DECREF(val); } diff --git a/externals/grill/py/source/main.cpp b/externals/grill/py/source/main.cpp index 428afea7..399f35c1 100644 --- a/externals/grill/py/source/main.cpp +++ b/externals/grill/py/source/main.cpp @@ -8,625 +8,6 @@ WARRANTIES, see the file, "license.txt," in this distribution. */ -#include "main.h" -#include +#include "pybase.h" -static PyMethodDef StdOut_Methods[] = -{ - { "write", pybase::StdOut_Write, 1 }, - { NULL, NULL, } -}; -static PyObject *gcollect = NULL; - -#ifdef FLEXT_THREADS - -typedef std::map PyThrMap; - -static PyInterpreterState *pymain = NULL; -static PyThrMap pythrmap; -PyThreadState *pybase::pythrsys = NULL; - -int pybase::lockcount = 0; - -PyThreadState *pybase::FindThreadState() -{ - flext::thrid_t id = flext::GetThreadId(); - PyThrMap::iterator it = pythrmap.find(id); - if(it == pythrmap.end()) { - // Make new thread state - PyThreadState *st = PyThreadState_New(pymain); - pythrmap[id] = st; - return st; - } - else - return it->second; -} - -void pybase::FreeThreadState() -{ - flext::thrid_t id = flext::GetThreadId(); - PyThrMap::iterator it = pythrmap.find(id); - if(it != pythrmap.end()) { - // clear out any cruft from thread state object - PyThreadState_Clear(it->second); - // delete my thread state object - PyThreadState_Delete(it->second); - // delete from map - pythrmap.erase(it); - } -} -#endif - - -PyObject *pybase::module_obj = NULL; -PyObject *pybase::module_dict = NULL; - - -void initsymbol(); -void initsamplebuffer(); - -void pybase::lib_setup() -{ - post(""); - post("------------------------------------------------"); - post("py/pyext %s - python script objects",PY__VERSION); - post("(C)2002-2005 Thomas Grill - http://grrrr.org/ext"); - post(""); - post("using Python %s",Py_GetVersion()); - -#ifdef FLEXT_DEBUG - post(""); - post("DEBUG version compiled on %s %s",__DATE__,__TIME__); -#endif - - // ------------------------------------------------------------- - - Py_Initialize(); - -#ifdef FLEXT_DEBUG -// Py_VerboseFlag = 1; -#endif - -#ifdef FLEXT_THREADS - // enable thread support and acquire the global thread lock - PyEval_InitThreads(); - - // get thread state - pythrsys = PyThreadState_Get(); - // get main interpreter state - pymain = pythrsys->interp; - - // add thread state of main thread to map - pythrmap[GetThreadId()] = pythrsys; -#endif - - // sys.argv must be set to empty tuple - char *nothing = ""; - PySys_SetArgv(0,¬hing); - - // register/initialize pyext module only once! - module_obj = Py_InitModule(PYEXT_MODULE, func_tbl); - module_dict = PyModule_GetDict(module_obj); // borrowed reference - - PyModule_AddStringConstant(module_obj,"__doc__",(char *)py_doc); - - // redirect stdout - 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); - - // get garbage collector function - PyObject *gcobj = PyImport_ImportModule("gc"); - if(gcobj) { - gcollect = PyObject_GetAttrString(gcobj,"collect"); - Py_DECREF(gcobj); - } - - // add symbol type - initsymbol(); - PyModule_AddObject(module_obj,"Symbol",(PyObject *)&pySymbol_Type); - - // pre-defined symbols - PyModule_AddObject(module_obj,"_s_",(PyObject *)pySymbol__); - PyModule_AddObject(module_obj,"_s_bang",(PyObject *)pySymbol_bang); - PyModule_AddObject(module_obj,"_s_list",(PyObject *)pySymbol_list); - PyModule_AddObject(module_obj,"_s_symbol",(PyObject *)pySymbol_symbol); - PyModule_AddObject(module_obj,"_s_float",(PyObject *)pySymbol_float); - PyModule_AddObject(module_obj,"_s_int",(PyObject *)pySymbol_int); - - // add samplebuffer type - initsamplebuffer(); - PyModule_AddObject(module_obj,"Buffer",(PyObject *)&pySamplebuffer_Type); - - // ------------------------------------------------------------- - - FLEXT_SETUP(pyobj); - FLEXT_SETUP(pyext); - FLEXT_DSP_SETUP(pydsp); - -#ifdef FLEXT_THREADS - // release global lock - PyEval_ReleaseLock(); -#endif - - post("------------------------------------------------"); - post(""); -} - -FLEXT_LIB_SETUP(py,pybase::lib_setup) - - -pybase::pybase() - : module(NULL) -#ifdef FLEXT_THREADS - , shouldexit(false),thrcount(0),stoptick(0) -#endif - , detach(0) -{ - PyThreadState *state = PyLockSys(); - Py_INCREF(module_obj); - PyUnlock(state); -} - -pybase::~pybase() -{ - PyThreadState *state = PyLockSys(); - Py_XDECREF(module_obj); - PyUnlock(state); -} - -void pybase::Exit() -{ -#ifdef FLEXT_THREADS - 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(PY_STOP_TICK*0.001f); - if(thrcount) { - // Wait forever - post("py/pyext - Waiting for thread termination!"); - while(thrcount) Sleep(PY_STOP_TICK*0.001f); - post("py/pyext - Okay, all threads have terminated"); - } - } -#endif -} - -void pybase::GetDir(PyObject *obj,AtomList &lst) -{ - if(obj) { - PyThreadState *state = PyLock(); - - PyObject *pvar = PyObject_Dir(obj); - if(!pvar) - PyErr_Print(); // no method found - else { - if(!GetPyArgs(lst,pvar)) - post("py/pyext - Argument list could not be created"); - Py_DECREF(pvar); - } - - PyUnlock(state); - } -} - -void pybase::m__dir(PyObject *obj) -{ - AtomList lst; - GetDir(obj,lst); - // dump dir to attribute outlet - DumpOut(NULL,lst.Count(),lst.Atoms()); -} - -void pybase::m__doc(PyObject *obj) -{ - if(obj) { - PyThreadState *state = PyLock(); - - PyObject *docf = PyDict_GetItemString(obj,"__doc__"); // borrowed!!! - if(docf && PyString_Check(docf)) { - post(""); - const char *s = PyString_AS_STRING(docf); - - // FIX: Python doc strings can easily be larger than 1k characters - // -> split into separate lines - for(;;) { - char buf[1024]; - char *nl = strchr((char *)s,'\n'); // the cast is for Borland C++ - if(!nl) { - // no more newline found - post(s); - break; - } - else { - // copy string before newline to temp buffer and post - unsigned int l = nl-s; - if(l >= sizeof(buf)) l = sizeof buf-1; - strncpy(buf,s,l); // copy all but newline - buf[l] = 0; - post(buf); - s = nl+1; // set after newline - } - } - } - - PyUnlock(state); - } -} - -void pybase::OpenEditor() -{ - // this should once open the editor.... -} - -void pybase::SetArgs() -{ - // script arguments - int argc = args.Count(); - const t_atom *argv = args.Atoms(); - char **sargv = new char *[argc+1]; - for(int i = 0; i <= argc; ++i) { - sargv[i] = new char[256]; - if(!i) - strcpy(sargv[i],"py/pyext"); - 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; -} - -bool pybase::ImportModule(const char *name) -{ - if(!name) return false; - if(modname == name) return true; - modname = name; - return ReloadModule(); -} - -void pybase::UnimportModule() -{ - if(!module) return; - - FLEXT_ASSERT(dict && module_obj && module_dict); - - Py_DECREF(module); - - // reference count to module is not 0 here, altough probably the last instance was unloaded - // Python retains one reference to the module all the time - // we don't care - - module = NULL; - dict = NULL; -} - -bool pybase::ReloadModule() -{ - bool ok = false; - - SetArgs(); - PyObject *newmod = module - ?PyImport_ReloadModule(module) - :PyImport_ImportModule((char *)modname.c_str()); - - if(!newmod) { - // unload faulty module - if(module) UnimportModule(); - } - else { - Py_XDECREF(module); - module = newmod; - dict = PyModule_GetDict(module); // borrowed - ok = true; - } - return ok; -} - -void pybase::GetModulePath(const char *mod,char *dir,int len) -{ -#if FLEXT_SYS == FLEXT_SYS_PD - // uarghh... pd doesn't show its path for extra modules - - char *name; - int 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 FLEXT_SYS == FLEXT_SYS_MAX - // how do i get the path in Max/MSP? - short path; - long type; - char smod[1024]; - strcat(strcpy(smod,mod),".py"); - 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; -#else - *dir = 0; -#endif -} - -void pybase::AddToPath(const char *dir) -{ - if(dir && *dir) { - PyObject *pobj = PySys_GetObject("path"); - if(pobj && PyList_Check(pobj)) { - PyObject *ps = PyString_FromString(dir); - if(!PySequence_Contains(pobj,ps)) - PyList_Append(pobj,ps); // makes new reference - Py_DECREF(ps); - } - PySys_SetObject("path",pobj); // steals reference to pobj - } -} - -static const t_symbol *sym_response = flext::MakeSymbol("response"); - -void pybase::Respond(bool b) -{ - if(respond) { - t_atom a; - SetBool(a,b); - DumpOut(sym_response,1,&a); - } -} - -void pybase::Reload() -{ - PyThreadState *state = PyLockSys(); - - PyObject *reg = GetRegistry(REGNAME); - - if(reg) { - PyObject *key; - int pos = 0; - while(PyDict_Next(reg,&pos,&key,NULL)) { - pybase *th = (pybase *)PyLong_AsLong(key); - FLEXT_ASSERT(th); - th->Unload(); - } - - UnloadModule(); - } - - bool ok = ReloadModule(); - - if(ok) { - LoadModule(); - - if(reg) { - SetRegistry(REGNAME,reg); - - PyObject *key; - int pos = 0; - while(PyDict_Next(reg,&pos,&key,NULL)) { - pybase *th = (pybase *)PyLong_AsLong(key); - FLEXT_ASSERT(th); - th->Load(); - } - } - else - Load(); - } - - Report(); - PyUnlock(state); -} - - - -static PyObject *output = NULL; - -// post to the console -PyObject* pybase::StdOut_Write(PyObject* self, PyObject* args) -{ - // should always be a tuple - FLEXT_ASSERT(PyTuple_Check(args)); - - const int sz = PyTuple_GET_SIZE(args); - - for(int i = 0; i < sz; ++i) { - PyObject *val = PyTuple_GET_ITEM(args,i); // borrowed reference - PyObject *str = PyObject_Str(val); // new reference - char *cstr = PyString_AS_STRING(str); - char *lf = strchr(cstr,'\n'); - - // line feed in string - if(!lf) { - // no -> just append - if(output) - PyString_ConcatAndDel(&output,str); // str is decrefd - else - output = str; // take str reference - } - else { - // yes -> append up to line feed, reset output buffer to string remainder - PyObject *part = PyString_FromStringAndSize(cstr,lf-cstr); // new reference - if(output) - PyString_ConcatAndDel(&output,part); // str is decrefd - else - output = part; // take str reference - - // output concatenated string - post(PyString_AS_STRING(output)); - - Py_DECREF(output); - output = PyString_FromString(lf+1); // new reference - } - } - - 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 pybase::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; -#ifdef FLEXT_THREADS - case 1: - // put call into queue - ret = qucall(pmeth,pargs); - break; - case 2: - // each call a new thread - if(!shouldexit) { - ret = thrcall(new work_data(pmeth,pargs)); - if(!ret) post("py/pyext - Failed to launch thread!"); - } - break; -#endif - default: - post("py/pyext - Unknown detach mode"); - } - return ret; -} - -void pybase::work_wrapper(void *data) -{ - FLEXT_ASSERT(data); - -#ifdef FLEXT_THREADS - ++thrcount; -#endif - - PyThreadState *state = PyLock(); - - // call worker - work_data *w = (work_data *)data; - callpy(w->fun,w->args); - delete w; - - PyUnlock(state); - -#ifdef FLEXT_THREADS - --thrcount; -#endif -} - -#ifdef FLEXT_THREADS -bool pybase::qucall(PyObject *fun,PyObject *args) -{ - FifoEl *el = qufifo.New(); - el->Set(fun,args); - qufifo.Put(el); - qucond.Signal(); - return true; -} - -void pybase::threadworker() -{ - ++thrcount; - - FifoEl *el; - PyThreadState *my = FindThreadState(),*state; - - for(;;) { - while(el = qufifo.Get()) { - ++thrcount; - state = PyLock(my); - callpy(el->fun,el->args); - Py_XDECREF(el->fun); - Py_XDECREF(el->args); - PyUnlock(state); - qufifo.Free(el); - --thrcount; - } - if(shouldexit) - break; - else - qucond.Wait(); - } - - state = PyLock(my); - // unref remaining Python objects - while(el = qufifo.Get()) { - Py_XDECREF(el->fun); - Py_XDECREF(el->args); - qufifo.Free(el); - } - PyUnlock(state); - - --thrcount; -} -#endif - -#if FLEXT_SYS == FLEXT_SYS_MAX -short pybase::patcher_myvol(t_patcher *x) -{ - t_box *w; - if (x->p_vol) - return x->p_vol; - else if (w = (t_box *)x->p_vnewobj) - return patcher_myvol(w->b_patcher); - else - return 0; -} -#endif - -bool pybase::collect() -{ - if(gcollect) { - PyObject *ret = PyObject_CallObject(gcollect,NULL); - if(ret) { -#ifdef FLEXT_DEBUG - int refs = PyInt_AsLong(ret); - if(refs) post("py/pyext - Garbage collector reports %i unreachable objects",refs); -#endif - Py_DECREF(ret); - return false; - } - } - return true; -} - -/* -PY_EXPORT PyThreadState *py_Lock(PyThreadState *st = NULL) { return pybase::PyLock(st?st:pybase::FindThreadState()); } -PY_EXPORT PyThreadState *py_LockSys() { return pybase::PyLockSys(); } -PY_EXPORT void py_Unlock(PyThreadState *st) { pybase::PyUnlock(st); } -*/ diff --git a/externals/grill/py/source/main.h b/externals/grill/py/source/main.h index 0b94274e..479c082d 100644 --- a/externals/grill/py/source/main.h +++ b/externals/grill/py/source/main.h @@ -12,20 +12,8 @@ WARRANTIES, see the file, "license.txt," in this distribution. #define __MAIN_H #include "pyprefix.h" -#include "pysymbol.h" -#include "pybuffer.h" -#include -#include -#if FLEXT_OS == FLEXT_LINUX || FLEXT_OS == FLEXT_IRIX -#include -#endif - -#if FLEXT_SYS == FLEXT_SYS_PD && (!defined (PD_MINOR_VERSION) || PD_MINOR_VERSION < 37) -#error PD version >= 0.37 required, please upgrade! -#endif - -#define PY__VERSION "0.2.0" +#define PY__VERSION "0.2.1pre" #define PYEXT_MODULE "pyext" // name for module @@ -37,7 +25,6 @@ WARRANTIES, see the file, "license.txt," in this distribution. #define PY_STOP_TICK 1 // ms - class FifoEl : public Fifo::Cell { @@ -48,186 +35,4 @@ public: typedef PooledFifo PyFifo; - -class pybase - : public flext -{ -public: - pybase(); - virtual ~pybase(); - - void Exit(); - - static PyObject *MakePyArgs(const t_symbol *s,int argc,const t_atom *argv,int inlet = -1,bool withself = false); - static bool GetPyArgs(AtomList &lst,PyObject *pValue,int offs = 0,PyObject **self = NULL); - - static void lib_setup(); - -protected: - - virtual void DumpOut(const t_symbol *sym,int argc,const t_atom *argv) = 0; - - void m__dir(PyObject *obj); - void m__doc(PyObject *obj); - - void m_dir() { m__dir(module); } - void mg_dir(AtomList &lst) { m__dir(module); } - void m_doc() { m__doc(dict); } - - std::string modname; // module name - PyObject *module,*dict; // object module and associated dictionary - - static const char *py_doc; - - void GetDir(PyObject *obj,AtomList &lst); - - AtomListStatic<16> args; - - void GetModulePath(const char *mod,char *dir,int len); - void AddToPath(const char *dir); - void SetArgs(); - - // reload module and all connected objects - void Reload(); - - bool ImportModule(const char *name); - void UnimportModule(); - bool ReloadModule(); - - // Get module registry - PyObject *GetRegistry(const char *regname); - // Set module registry - void SetRegistry(const char *regname,PyObject *reg); - - // Register object - void Register(PyObject *reg); - // Unregister object - void Unregister(PyObject *reg); - - virtual void LoadModule() = 0; - virtual void UnloadModule() = 0; - - virtual void Load() = 0; - virtual void Unload() = 0; - - void OpenEditor(); - void Respond(bool b); - - void Report() { while(PyErr_Occurred()) PyErr_Print(); } - - static bool IsAnything(const t_symbol *s) { return s && s != sym_float && s != sym_int && s != sym_symbol && s != sym_list && s != sym_pointer; } - - enum retval { nothing,atom,sequ }; - - // --- 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_arraysupport(PyObject *,PyObject *args); - static PyObject *py_samplerate(PyObject *,PyObject *args); - static PyObject *py_blocksize(PyObject *,PyObject *args); - -#if FLEXT_SYS == FLEXT_SYS_PD - static PyObject *py_getvalue(PyObject *,PyObject *args); - static PyObject *py_setvalue(PyObject *,PyObject *args); -#endif - -#ifdef PY_NUMARRAY - static void setupNumarray(); - static PyObject *py_import(PyObject *,PyObject *args); - static PyObject *py_export(PyObject *,PyObject *args); -#endif - - // ----thread stuff ------------ - - virtual void m_stop(int argc,const t_atom *argv); - - bool respond; -#ifdef FLEXT_THREADS - bool shouldexit; - int thrcount; - int stoptick; - Timer stoptmr; - - void tick(void *); -#endif - - int detach; - - bool gencall(PyObject *fun,PyObject *args); - virtual bool thrcall(void *data) = 0; - virtual bool callpy(PyObject *fun,PyObject *args) = 0; - -#if FLEXT_SYS == FLEXT_SYS_MAX - static short patcher_myvol(t_patcher *x); -#endif - - static bool collect(); - -protected: - - void work_wrapper(void *data); - -#ifdef FLEXT_THREADS - bool qucall(PyObject *fun,PyObject *args); - void threadworker(); - PyFifo qufifo; - ThrCond qucond; - static PyThreadState *pythrsys; - - static PyThreadState *FindThreadState(); - static void FreeThreadState(); -#else - static PyThreadState *FindThreadState() { return NULL; } -#endif - -public: - -#ifdef FLEXT_THREADS - ThrMutex mutex; - inline void Lock() { mutex.Unlock(); } - inline void Unlock() { mutex.Unlock(); } - - // this is especially needed when one py/pyext object calls another one - // we don't want the message to be queued, but otoh we have to avoid deadlock - // (recursive calls can only happen in the system thread) - static int lockcount; - - static PyThreadState *PyLock(PyThreadState *st = FindThreadState()) - { - if(!IsSystemThread() || !lockcount++) PyEval_AcquireLock(); - return PyThreadState_Swap(st); - } - - static PyThreadState *PyLockSys() - { - if(!lockcount++) PyEval_AcquireLock(); - return PyThreadState_Swap(pythrsys); - } - - static void PyUnlock(PyThreadState *st) - { - PyThreadState *old = PyThreadState_Swap(st); - if(old != pythrsys || !--lockcount) PyEval_ReleaseLock(); - } - -#else - inline void Lock() {} - inline void Unlock() {} - - static PyThreadState *PyLock(PyThreadState * = NULL) { return NULL; } - static PyThreadState *PyLockSys() { return NULL; } - static void PyUnlock(PyThreadState *st) {} -#endif - - static PyObject* StdOut_Write(PyObject* Self, PyObject* Args); -}; - #endif diff --git a/externals/grill/py/source/modmeth.cpp b/externals/grill/py/source/modmeth.cpp index 33f0fc7b..d56441ac 100644 --- a/externals/grill/py/source/modmeth.cpp +++ b/externals/grill/py/source/modmeth.cpp @@ -8,7 +8,7 @@ WARRANTIES, see the file, "license.txt," in this distribution. */ -#include "main.h" +#include "pybase.h" // function table for module diff --git a/externals/grill/py/source/py.cpp b/externals/grill/py/source/py.cpp index f91d13b6..d007f0c3 100644 --- a/externals/grill/py/source/py.cpp +++ b/externals/grill/py/source/py.cpp @@ -8,8 +8,7 @@ WARRANTIES, see the file, "license.txt," in this distribution. */ -#include "main.h" - +#include "pybase.h" class pyobj : public pybase @@ -35,15 +34,6 @@ protected: void m_dir_() { m__dir(function); } void m_doc_() { m__doc(function); } - // methods for python arguments - void callwork(const t_symbol *s,int argc,const t_atom *argv); - - inline void m_bang() { callwork(NULL,0,NULL); } - inline void m_py_list(int argc,const t_atom *argv) { callwork(sym_list,argc,argv); } - inline void m_py_float(int argc,const t_atom *argv) { callwork(sym_float,argc,argv); } - inline void m_py_int(int argc,const t_atom *argv) { callwork(sym_int,argc,argv); } - inline void m_py_any(const t_symbol *s,int argc,const t_atom *argv) { callwork(s,argc,argv); } - const t_symbol *funname; PyObject *function; bool withfunction; @@ -60,6 +50,8 @@ protected: virtual bool thrcall(void *data); virtual void DumpOut(const t_symbol *sym,int argc,const t_atom *argv); + PyObject **objects; + private: virtual bool callpy(PyObject *fun,PyObject *args); @@ -67,21 +59,17 @@ private: static void Setup(t_classid c); FLEXT_CALLBACK(m_help) - FLEXT_CALLBACK(m_bang) FLEXT_CALLBACK(m_reload) FLEXT_CALLBACK_V(m_reload_) FLEXT_CALLBACK_V(m_set) FLEXT_CALLBACK(m_dir_) 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) - // callbacks FLEXT_ATTRVAR_I(detach) + FLEXT_ATTRVAR_B(xlate) FLEXT_ATTRVAR_B(respond) + FLEXT_CALLBACK_V(m_stop) FLEXT_CALLBACK(m_dir) FLEXT_CALLGET_V(mg_dir) @@ -114,56 +102,64 @@ void pyobj::Setup(t_classid c) FLEXT_CADDMETHOD_(c,0,"doc+",m_doc_); FLEXT_CADDMETHOD_(c,0,"dir+",m_dir_); - FLEXT_CADDBANG(c,0,m_bang); FLEXT_CADDMETHOD_(c,0,"set",m_set); - FLEXT_CADDMETHOD_(c,1,"float",m_py_float); - FLEXT_CADDMETHOD_(c,1,"int",m_py_int); - FLEXT_CADDMETHOD(c,1,m_py_list); - FLEXT_CADDMETHOD(c,1,m_py_any); - + FLEXT_CADDATTR_VAR1(c,"xlate",xlate); FLEXT_CADDATTR_VAR1(c,"respond",respond); } -pyobj::pyobj(int argc,const t_atom *argv): - funname(NULL),function(NULL),withfunction(false) +pyobj::pyobj(int argc,const t_atom *argv) + : funname(NULL) + , function(NULL) + , withfunction(false) + , objects(NULL) { - AddInAnything(2); - AddOutAnything(); - #ifdef FLEXT_THREADS FLEXT_ADDTIMER(stoptmr,tick); // launch thread worker FLEXT_CALLMETHOD(threadworker); #endif - if(argc > 2) args(argc-2,argv+2); - PyThreadState *state = PyLockSys(); + int inlets = -1; // -1 signals non-explicit definition + if(argc && CanbeInt(*argv)) { + inlets = GetAInt(*argv); + argv++,argc--; + } + + if(inlets >= 1) { + objects = new PyObject *[inlets]; + for(int i = 0; i < inlets; ++i) { objects[i] = Py_None; Py_INCREF(Py_None); } + } + + AddInAnything(1+(inlets < 0?1:inlets)); + AddOutAnything(); + + const char *funnm = NULL; + // init script module - if(argc >= 1) { - const char *sn = GetAString(argv[0]); + if(argc) { + AddCurrentPath(thisCanvas()); + + const char *sn = GetAString(*argv); + argv++,argc--; + if(sn) { - char dir[1024]; - GetModulePath(sn,dir,sizeof(dir)); - // set script path - AddToPath(dir); + char modnm[64]; + strcpy(modnm,sn); -#if FLEXT_SYS == FLEXT_SYS_PD - // add dir of current patch to path - AddToPath(GetString(canvas_getdir(thisCanvas()))); - // add current dir to path - AddToPath(GetString(canvas_getcurrentdir())); -#elif FLEXT_SYS == FLEXT_SYS_MAX - short path = patcher_myvol(thisCanvas()); - path_topathname(path,NULL,dir); - AddToPath(dir); -#else - #pragma message("Adding current dir to path is not implemented") -#endif + char *pt = strrchr(modnm,'.'); // search for last dot + if(pt && *pt) { + funnm = pt+1; + *pt = 0; + } + + char dir[1024]; + GetModulePath(modnm,dir,sizeof(dir)); + AddToPath(dir); - ImportModule(sn); + ImportModule(modnm); } else PyErr_SetString(PyExc_ValueError,"Invalid module name"); @@ -171,14 +167,20 @@ pyobj::pyobj(int argc,const t_atom *argv): Register(GetRegistry(REGNAME)); - if(argc >= 2) { - const char *fn = GetAString(argv[1]); - if(fn) - SetFunction(fn); + if(funnm || argc) { + if(!funnm) { + funnm = GetAString(*argv); + argv++,argc--; + } + + if(funnm) + SetFunction(funnm); else PyErr_SetString(PyExc_ValueError,"Invalid function name"); } + if(argc) args(argc,argv); + Report(); PyUnlock(state); @@ -186,7 +188,12 @@ pyobj::pyobj(int argc,const t_atom *argv): pyobj::~pyobj() { - PyThreadState *state = PyLockSys(); + if(objects) { + for(int i = 0; i < CntIn()-1; ++i) Py_DECREF(objects[i]); + delete[] objects; + } + + PyThreadState *state = PyLockSys(); Unregister(GetRegistry(REGNAME)); Report(); PyUnlock(state); @@ -198,13 +205,6 @@ void pyobj::Exit() flext_base::Exit(); } -bool pyobj::CbMethodResort(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; -} - void pyobj::m_set(int argc,const t_atom *argv) { PyThreadState *state = PyLockSys(); @@ -333,31 +333,52 @@ bool pyobj::callpy(PyObject *fun,PyObject *args) return false; } else { - flext::AtomListStatic<16> rargs; - if(GetPyArgs(rargs,ret)) { - // call to outlet _outside_ the Mutex lock! - // otherwise (if not detached) deadlock will occur - if(rargs.Count()) ToOutList(0,rargs); - } - else if(PyErr_Occurred()) + if(ret != Py_None && !OutObject(this,0,ret) && PyErr_Occurred()) PyErr_Print(); - Py_DECREF(ret); return true; } } -void pyobj::callwork(const t_symbol *s,int argc,const t_atom *argv) +bool pyobj::CbMethodResort(int n,const t_symbol *s,int argc,const t_atom *argv) { bool ret = false; + if(n == 0 && s != sym_bang) goto end; + + if(objects && n >= 1) { + // store args + PyObject *&obj = objects[n-1]; + Py_DECREF(obj); + obj = MakePyArg(s,argc,argv); // steal reference + + if(n > 1) { + ret = true; // just store, don't trigger + goto end; + } + } + PyThreadState *state = PyLock(); if(withfunction) { if(function) { - PyObject *pargs = MakePyArgs(s,argc,argv); Py_INCREF(function); - ret = gencall(function,pargs); + + PyObject *pargs; + + if(objects) { + int inlets = CntIn()-1; + pargs = PyTuple_New(inlets); + for(int i = 0; i < inlets; ++i) { + Py_INCREF(objects[i]); + PyTuple_SET_ITEM(pargs,i,objects[i]); + } + } + else + // construct tuple from args + pargs = MakePyArgs(s,argc,argv); + + ret = gencall(function,pargs); // references are stolen } else PyErr_SetString(PyExc_RuntimeError,"No function set"); @@ -380,6 +401,9 @@ void pyobj::callwork(const t_symbol *s,int argc,const t_atom *argv) PyUnlock(state); Respond(ret); + +end: + return ret || flext_base::CbMethodResort(n,s,argc,argv); } void pyobj::CbClick() { pybase::OpenEditor(); } diff --git a/externals/grill/py/source/pyargs.cpp b/externals/grill/py/source/pyargs.cpp index 5e328975..f08369a7 100644 --- a/externals/grill/py/source/pyargs.cpp +++ b/externals/grill/py/source/pyargs.cpp @@ -8,83 +8,124 @@ WARRANTIES, see the file, "license.txt," in this distribution. */ -#include "main.h" +#include "pybase.h" +#include "pyatom.h" + +static const t_symbol *symatom = flext::MakeSymbol(" py "); static PyObject *MakePyAtom(const t_atom &at) { if(flext::IsSymbol(at)) return pySymbol_FromSymbol(flext::GetSymbol(at)); +/* else if(flext::CanbeInt(at) || flext::CanbeFloat(at)) { - // if a number can be an integer... let at be an integer! + // if a number can be an integer... let it be an integer! int ival = flext::GetAInt(at); double fval = flext::GetAFloat(at); return (double)ival == fval?PyInt_FromLong(ival):PyFloat_FromDouble(fval); } -// else if(flext::IsPointer(at)) return NULL; // not handled -/* - // these following should never happen - else if(flext::IsFloat(at)) return PyFloat_FromDouble((double)flext::GetFloat(at)); - else if(flext::IsInt(at)) return PyInt_FromLong(flext::GetInt(at)); -*/ +*/ + else if(flext::IsFloat(at)) + return PyFloat_FromDouble(flext::GetFloat(at)); + else if(flext::IsInt(at)) + return PyInt_FromLong(flext::GetInt(at)); + return NULL; } -PyObject *pybase::MakePyArgs(const t_symbol *s,int argc,const t_atom *argv,int inlet,bool withself) +static PyObject *MakePyAtom(int argc,const t_atom *argv) { - PyObject *pArgs; + if(argc != sizeof(size_t)/2) return NULL; + + size_t atom = 0; + for(int i = sizeof(size_t)/2-1; i >= 0; --i) + if(!flext::CanbeInt(argv[i])) { + atom = 0; + break; + } + else + atom = (atom<<16)+flext::GetAInt(argv[i]); + + if(atom) { + PyObject *el = PyAtom::Retrieve(atom); + if(!el) el = Py_None; // object already gone.... + Py_INCREF(el); + return el; + } + else + return NULL; +} -/* - if(!s && args.Count() == 1) { - pArgs = MakePyAtom(args[0]); - if(!pArgs) - post("py/pyext: cannot convert argument(s)"); +PyObject *pybase::MakePyArgs(const t_symbol *s,int argc,const t_atom *argv,int inlet) +{ + PyObject *ret,*el; + + if(s == symatom && (el = MakePyAtom(argc,argv)) != NULL) { + ret = PyTuple_New(1); + PyTuple_SET_ITEM(ret,0,el); } - else -*/ - { + else { bool any = IsAnything(s); - pArgs = PyTuple_New(argc+(any?1:0)+(inlet >= 0?1:0)); + ret = PyTuple_New(argc+(any?1:0)+(inlet >= 0?1:0)); int pix = 0; if(inlet >= 0) - PyTuple_SetItem(pArgs, pix++, PyInt_FromLong(inlet)); - - int ix; - PyObject *tmp; -// if(!withself || argc < (any?1:2)) - tmp = pArgs,ix = pix; -// else -// tmp = PyTuple_New(argc+(any?1:0)),ix = 0; + PyTuple_SET_ITEM(ret,pix++,PyInt_FromLong(inlet)); if(any) - PyTuple_SET_ITEM(tmp, ix++, pySymbol_FromSymbol(s)); + PyTuple_SET_ITEM(ret,pix++,pySymbol_FromSymbol(s)); for(int i = 0; i < argc; ++i) { - PyObject *pValue = MakePyAtom(argv[i]); - if(!pValue) { + el = MakePyAtom(argv[i]); + if(!el) { post("py/pyext: cannot convert argument %i",any?i+1:i); - continue; - } - - /* pValue reference stolen here: */ - PyTuple_SET_ITEM(tmp, ix++, pValue); + + el = Py_None; + Py_INCREF(Py_None); + } + + PyTuple_SET_ITEM(ret,pix++,el); // reference stolen } + } + + return ret; +} - if(tmp != pArgs) { - PyTuple_SET_ITEM(pArgs, pix++, tmp); - #if PY_VERSION_HEX >= 0x02020000 - _PyTuple_Resize(&pArgs,pix); - #else - _PyTuple_Resize(&pArgs,pix,0); - #endif +PyObject *pybase::MakePyArg(const t_symbol *s,int argc,const t_atom *argv) +{ + PyObject *ret; + + if(s == symatom && (ret = MakePyAtom(argc,argv)) != NULL) { + // ok! + } + else if(argc == 1 && IsAtom(s)) + ret = MakePyAtom(*argv); + else { + bool any = s != sym_list; + ret = PyTuple_New(argc+(any?1:0)); + + int pix = 0; + if(any) + PyTuple_SET_ITEM(ret,pix++,pySymbol_FromSymbol(s)); + + for(int i = 0; i < argc; ++i) { + PyObject *el = MakePyAtom(argv[i]); + if(!el) { + post("py/pyext: cannot convert argument %i",any?i+1:i); + + el = Py_None; + Py_INCREF(Py_None); + } + + PyTuple_SET_ITEM(ret,pix++,el); // reference stolen } } - return pArgs; + return ret; } -bool pybase::GetPyArgs(AtomList &lst,PyObject *pValue,int offs,PyObject **self) +bool pybase::GetPyArgs(AtomList &lst,PyObject *pValue,int offs) { if(pValue == NULL) return false; @@ -124,11 +165,13 @@ bool pybase::GetPyArgs(AtomList &lst,PyObject *pValue,int offs,PyObject **self) else if(PyFloat_Check(arg)) SetFloat(at,(float)PyFloat_AsDouble(arg)); else if(pySymbol_Check(arg)) SetSymbol(at,pySymbol_AS_SYMBOL(arg)); else if(PyString_Check(arg)) SetString(at,PyString_AS_STRING(arg)); - else if(ix == 0 && self && PyInstance_Check(arg)) { +/* + else if(ix == 0 && self && PyInstance_Check(arg)) { // assumed to be self ... that should be checked _somehow_ !!! Py_INCREF(arg); *self = arg; } +*/ else { PyObject *tp = PyObject_Type(arg); PyObject *stp = tp?PyObject_Str(tp):NULL; @@ -146,3 +189,16 @@ bool pybase::GetPyArgs(AtomList &lst,PyObject *pValue,int offs,PyObject **self) return ok; } + + +bool pybase::GetPyAtom(AtomList &lst,PyObject *obj) +{ + size_t atom = PyAtom::Register(obj); + size_t szat = sizeof(atom)/2; + + lst(1+szat); + SetSymbol(lst[0],symatom); + for(size_t i = 0; i < szat; ++i,atom >>= 16) + flext::SetInt(lst[i+1],(int)(atom&((1<<16)-1))); + return true; +} diff --git a/externals/grill/py/source/pyatom.cpp b/externals/grill/py/source/pyatom.cpp new file mode 100644 index 00000000..07712f30 --- /dev/null +++ b/externals/grill/py/source/pyatom.cpp @@ -0,0 +1,61 @@ +/* + +py/pyext - python script object for PD and Max/MSP + +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. + +*/ + +#include "pyatom.h" +#include + +#define INTV 0.01 + +typedef std::map ObjMap; + +static ObjMap objmap; +static size_t collix = 0,curix = 0; +static double last = 0; + +size_t PyAtom::Register(PyObject *obj) +{ + Collect(); + + Py_INCREF(obj); + objmap[++curix] = obj; + +#ifdef _DEBUG +// post("REG %p (%i)\n",obj,objmap.size()); +#endif + return curix; +} + +PyObject *PyAtom::Retrieve(size_t id) +{ + ObjMap::iterator it = objmap.find(id); + PyObject *ret = it == objmap.end()?NULL:it->second; + Collect(); + return ret; +} + +void PyAtom::Collect() +{ + for(;;) { + ObjMap::iterator it = objmap.begin(); + if(it == objmap.end() || it->first > collix) break; + + PyObject *obj = it->second; + Py_DECREF(obj); + objmap.erase(it); + +#ifdef _DEBUG +// post("DEL %p\n",obj); +#endif + } + + // schedule next collect time + double tm = flext::GetTime(); + if(tm > last+INTV) last = tm,collix = curix; +} diff --git a/externals/grill/py/source/pyatom.h b/externals/grill/py/source/pyatom.h new file mode 100644 index 00000000..e96514c6 --- /dev/null +++ b/externals/grill/py/source/pyatom.h @@ -0,0 +1,19 @@ +/* + +py/pyext - python script object for PD and Max/MSP + +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. + +*/ + +#include "main.h" + +class PyAtom +{ +public: + static size_t Register(PyObject *obj); + static PyObject *Retrieve(size_t id); + static void Collect(); +}; diff --git a/externals/grill/py/source/pybase.cpp b/externals/grill/py/source/pybase.cpp new file mode 100644 index 00000000..cc51a59e --- /dev/null +++ b/externals/grill/py/source/pybase.cpp @@ -0,0 +1,672 @@ +/* + +py/pyext - python external object for PD and MaxMSP + +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. + +*/ + +#include "pybase.h" +#include + +static PyMethodDef StdOut_Methods[] = +{ + { "write", pybase::StdOut_Write, 1 }, + { NULL, NULL, } +}; + +static PyObject *gcollect = NULL; + +#ifdef FLEXT_THREADS + +typedef std::map PyThrMap; + +static PyInterpreterState *pymain = NULL; +static PyThrMap pythrmap; +PyThreadState *pybase::pythrsys = NULL; + +int pybase::lockcount = 0; + +PyThreadState *pybase::FindThreadState() +{ + flext::thrid_t id = flext::GetThreadId(); + PyThrMap::iterator it = pythrmap.find(id); + if(it == pythrmap.end()) { + // Make new thread state + PyThreadState *st = PyThreadState_New(pymain); + pythrmap[id] = st; + return st; + } + else + return it->second; +} + +void pybase::FreeThreadState() +{ + flext::thrid_t id = flext::GetThreadId(); + PyThrMap::iterator it = pythrmap.find(id); + if(it != pythrmap.end()) { + // clear out any cruft from thread state object + PyThreadState_Clear(it->second); + // delete my thread state object + PyThreadState_Delete(it->second); + // delete from map + pythrmap.erase(it); + } +} +#endif + + +PyObject *pybase::module_obj = NULL; +PyObject *pybase::module_dict = NULL; + + +// ----------------------------------------------------------------------------------------------------------- + + +void initsymbol(); +void initsamplebuffer(); + +void pybase::lib_setup() +{ + post(""); + post("------------------------------------------------"); + post("py/pyext %s - python script objects",PY__VERSION); + post("(C)2002-2005 Thomas Grill - http://grrrr.org/ext"); + post(""); + post("using Python %s",Py_GetVersion()); + +#ifdef FLEXT_DEBUG + post(""); + post("DEBUG version compiled on %s %s",__DATE__,__TIME__); +#endif + + // ------------------------------------------------------------- + + Py_Initialize(); + +#ifdef FLEXT_DEBUG +// Py_VerboseFlag = 1; +#endif + +#ifdef FLEXT_THREADS + // enable thread support and acquire the global thread lock + PyEval_InitThreads(); + + // get thread state + pythrsys = PyThreadState_Get(); + // get main interpreter state + pymain = pythrsys->interp; + + // add thread state of main thread to map + pythrmap[GetThreadId()] = pythrsys; +#endif + + // sys.argv must be set to empty tuple + char *nothing = ""; + PySys_SetArgv(0,¬hing); + + // register/initialize pyext module only once! + module_obj = Py_InitModule(PYEXT_MODULE, func_tbl); + module_dict = PyModule_GetDict(module_obj); // borrowed reference + + PyModule_AddStringConstant(module_obj,"__doc__",(char *)py_doc); + + // redirect stdout + 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); + + // get garbage collector function + PyObject *gcobj = PyImport_ImportModule("gc"); + if(gcobj) { + gcollect = PyObject_GetAttrString(gcobj,"collect"); + Py_DECREF(gcobj); + } + + // add symbol type + initsymbol(); + PyModule_AddObject(module_obj,"Symbol",(PyObject *)&pySymbol_Type); + + // pre-defined symbols + PyModule_AddObject(module_obj,"_s_",(PyObject *)pySymbol__); + PyModule_AddObject(module_obj,"_s_bang",(PyObject *)pySymbol_bang); + PyModule_AddObject(module_obj,"_s_list",(PyObject *)pySymbol_list); + PyModule_AddObject(module_obj,"_s_symbol",(PyObject *)pySymbol_symbol); + PyModule_AddObject(module_obj,"_s_float",(PyObject *)pySymbol_float); + PyModule_AddObject(module_obj,"_s_int",(PyObject *)pySymbol_int); + + // add samplebuffer type + initsamplebuffer(); + PyModule_AddObject(module_obj,"Buffer",(PyObject *)&pySamplebuffer_Type); + + // ------------------------------------------------------------- + + FLEXT_SETUP(pyobj); + FLEXT_SETUP(pyext); + FLEXT_DSP_SETUP(pydsp); + +#ifdef FLEXT_THREADS + // release global lock + PyEval_ReleaseLock(); +#endif + + post("------------------------------------------------"); + post(""); +} + +FLEXT_LIB_SETUP(py,pybase::lib_setup) + + +// ----------------------------------------------------------------------------------------------------------- + + +pybase::pybase() + : module(NULL) +#ifdef FLEXT_THREADS + , shouldexit(false),thrcount(0),stoptick(0) +#endif + , detach(0) + , xlate(true) +{ + PyThreadState *state = PyLockSys(); + Py_INCREF(module_obj); + PyUnlock(state); +} + +pybase::~pybase() +{ + PyThreadState *state = PyLockSys(); + Py_XDECREF(module_obj); + PyUnlock(state); +} + +void pybase::Exit() +{ +#ifdef FLEXT_THREADS + 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(PY_STOP_TICK*0.001f); + if(thrcount) { + // Wait forever + post("py/pyext - Waiting for thread termination!"); + while(thrcount) Sleep(PY_STOP_TICK*0.001f); + post("py/pyext - Okay, all threads have terminated"); + } + } +#endif +} + +void pybase::GetDir(PyObject *obj,AtomList &lst) +{ + if(obj) { + PyThreadState *state = PyLock(); + + PyObject *pvar = PyObject_Dir(obj); + if(!pvar) + PyErr_Print(); // no method found + else { + if(!GetPyArgs(lst,pvar)) + post("py/pyext - Argument list could not be created"); + Py_DECREF(pvar); + } + + PyUnlock(state); + } +} + +void pybase::m__dir(PyObject *obj) +{ + AtomList lst; + GetDir(obj,lst); + // dump dir to attribute outlet + DumpOut(NULL,lst.Count(),lst.Atoms()); +} + +void pybase::m__doc(PyObject *obj) +{ + if(obj) { + PyThreadState *state = PyLock(); + + PyObject *docf = PyDict_GetItemString(obj,"__doc__"); // borrowed!!! + if(docf && PyString_Check(docf)) { + post(""); + const char *s = PyString_AS_STRING(docf); + + // FIX: Python doc strings can easily be larger than 1k characters + // -> split into separate lines + for(;;) { + char buf[1024]; + char *nl = strchr((char *)s,'\n'); // the cast is for Borland C++ + if(!nl) { + // no more newline found + post(s); + break; + } + else { + // copy string before newline to temp buffer and post + unsigned int l = nl-s; + if(l >= sizeof(buf)) l = sizeof buf-1; + strncpy(buf,s,l); // copy all but newline + buf[l] = 0; + post(buf); + s = nl+1; // set after newline + } + } + } + + PyUnlock(state); + } +} + +void pybase::OpenEditor() +{ + // this should once open the editor.... +} + +void pybase::SetArgs() +{ + // script arguments + int argc = args.Count(); + const t_atom *argv = args.Atoms(); + char **sargv = new char *[argc+1]; + for(int i = 0; i <= argc; ++i) { + sargv[i] = new char[256]; + if(!i) + strcpy(sargv[i],"py/pyext"); + 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; +} + +bool pybase::ImportModule(const char *name) +{ + if(!name) return false; + if(modname == name) return true; + modname = name; + return ReloadModule(); +} + +void pybase::UnimportModule() +{ + if(!module) return; + + FLEXT_ASSERT(dict && module_obj && module_dict); + + Py_DECREF(module); + + // reference count to module is not 0 here, altough probably the last instance was unloaded + // Python retains one reference to the module all the time + // we don't care + + module = NULL; + dict = NULL; +} + +bool pybase::ReloadModule() +{ + bool ok = false; + + SetArgs(); + PyObject *newmod = module + ?PyImport_ReloadModule(module) + :PyImport_ImportModule((char *)modname.c_str()); + + if(!newmod) { + // unload faulty module + if(module) UnimportModule(); + } + else { + Py_XDECREF(module); + module = newmod; + dict = PyModule_GetDict(module); // borrowed + ok = true; + } + return ok; +} + +void pybase::GetModulePath(const char *mod,char *dir,int len) +{ +#if FLEXT_SYS == FLEXT_SYS_PD + // uarghh... pd doesn't show its path for extra modules + + char *name; + int 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 FLEXT_SYS == FLEXT_SYS_MAX + // how do i get the path in Max/MSP? + short path; + long type; + char smod[1024]; + strcat(strcpy(smod,mod),".py"); + 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; +#else + *dir = 0; +#endif +} + +void pybase::AddToPath(const char *dir) +{ + if(dir && *dir) { + PyObject *pobj = PySys_GetObject("path"); + if(pobj && PyList_Check(pobj)) { + PyObject *ps = PyString_FromString(dir); + if(!PySequence_Contains(pobj,ps)) + PyList_Append(pobj,ps); // makes new reference + Py_DECREF(ps); + } + PySys_SetObject("path",pobj); // steals reference to pobj + } +} + +void pybase::AddCurrentPath(t_canvas *cnv) +{ +#if FLEXT_SYS == FLEXT_SYS_PD + // add dir of current patch to path + AddToPath(GetString(canvas_getdir(cnv))); + // add current dir to path + AddToPath(GetString(canvas_getcurrentdir())); +#elif FLEXT_SYS == FLEXT_SYS_MAX + char dir[1024]; + short path = patcher_myvol(cnv); + path_topathname(path,NULL,dir); + AddToPath(dir); +#else + #pragma message("Adding current dir to path is not implemented") +#endif +} + +bool pybase::OutObject(flext_base *ext,int o,PyObject *obj) +{ + flext::AtomListStatic<16> lst; + if(xlate?GetPyArgs(lst,obj):GetPyAtom(lst,obj)) { + // call to outlet _outside_ the Mutex lock! + // otherwise (if not detached) deadlock will occur + if(lst.Count() && IsSymbol(lst[0])) + ext->ToOutAnything(o,GetSymbol(lst[0]),lst.Count()-1,lst.Atoms()+1); + else if(lst.Count() > 1) + ext->ToOutList(o,lst); + else + ext->ToOutAtom(o,lst[0]); + return true; + } + else + return false; +} + +static const t_symbol *sym_response = flext::MakeSymbol("response"); + +void pybase::Respond(bool b) +{ + if(respond) { + t_atom a; + SetBool(a,b); + DumpOut(sym_response,1,&a); + } +} + +void pybase::Reload() +{ + PyThreadState *state = PyLockSys(); + + PyObject *reg = GetRegistry(REGNAME); + + if(reg) { + PyObject *key; + int pos = 0; + while(PyDict_Next(reg,&pos,&key,NULL)) { + pybase *th = (pybase *)PyLong_AsLong(key); + FLEXT_ASSERT(th); + th->Unload(); + } + + UnloadModule(); + } + + bool ok = ReloadModule(); + + if(ok) { + LoadModule(); + + if(reg) { + SetRegistry(REGNAME,reg); + + PyObject *key; + int pos = 0; + while(PyDict_Next(reg,&pos,&key,NULL)) { + pybase *th = (pybase *)PyLong_AsLong(key); + FLEXT_ASSERT(th); + th->Load(); + } + } + else + Load(); + } + + Report(); + PyUnlock(state); +} + +static PyObject *output = NULL; + +// post to the console +PyObject* pybase::StdOut_Write(PyObject* self, PyObject* args) +{ + // should always be a tuple + FLEXT_ASSERT(PyTuple_Check(args)); + + const int sz = PyTuple_GET_SIZE(args); + + for(int i = 0; i < sz; ++i) { + PyObject *val = PyTuple_GET_ITEM(args,i); // borrowed reference + PyObject *str = PyObject_Str(val); // new reference + char *cstr = PyString_AS_STRING(str); + char *lf = strchr(cstr,'\n'); + + // line feed in string + if(!lf) { + // no -> just append + if(output) + PyString_ConcatAndDel(&output,str); // str is decrefd + else + output = str; // take str reference + } + else { + // yes -> append up to line feed, reset output buffer to string remainder + PyObject *part = PyString_FromStringAndSize(cstr,lf-cstr); // new reference + if(output) + PyString_ConcatAndDel(&output,part); // str is decrefd + else + output = part; // take str reference + + // output concatenated string + post(PyString_AS_STRING(output)); + + Py_DECREF(output); + output = PyString_FromString(lf+1); // new reference + } + } + + 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 pybase::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; +#ifdef FLEXT_THREADS + case 1: + // put call into queue + ret = qucall(pmeth,pargs); + break; + case 2: + // each call a new thread + if(!shouldexit) { + ret = thrcall(new work_data(pmeth,pargs)); + if(!ret) post("py/pyext - Failed to launch thread!"); + } + break; +#endif + default: + post("py/pyext - Unknown detach mode"); + } + return ret; +} + +void pybase::work_wrapper(void *data) +{ + FLEXT_ASSERT(data); + +#ifdef FLEXT_THREADS + ++thrcount; +#endif + + PyThreadState *state = PyLock(); + + // call worker + work_data *w = (work_data *)data; + callpy(w->fun,w->args); + delete w; + + PyUnlock(state); + +#ifdef FLEXT_THREADS + --thrcount; +#endif +} + +#ifdef FLEXT_THREADS +bool pybase::qucall(PyObject *fun,PyObject *args) +{ + FifoEl *el = qufifo.New(); + el->Set(fun,args); + qufifo.Put(el); + qucond.Signal(); + return true; +} + +void pybase::threadworker() +{ + ++thrcount; + + FifoEl *el; + PyThreadState *my = FindThreadState(),*state; + + for(;;) { + while(el = qufifo.Get()) { + ++thrcount; + state = PyLock(my); + callpy(el->fun,el->args); + Py_XDECREF(el->fun); + Py_XDECREF(el->args); + PyUnlock(state); + qufifo.Free(el); + --thrcount; + } + if(shouldexit) + break; + else + qucond.Wait(); + } + + state = PyLock(my); + // unref remaining Python objects + while(el = qufifo.Get()) { + Py_XDECREF(el->fun); + Py_XDECREF(el->args); + qufifo.Free(el); + } + PyUnlock(state); + + --thrcount; +} +#endif + +#if FLEXT_SYS == FLEXT_SYS_MAX +short pybase::patcher_myvol(t_patcher *x) +{ + t_box *w; + if (x->p_vol) + return x->p_vol; + else if (w = (t_box *)x->p_vnewobj) + return patcher_myvol(w->b_patcher); + else + return 0; +} +#endif + +bool pybase::collect() +{ + if(gcollect) { + PyObject *ret = PyObject_CallObject(gcollect,NULL); + if(ret) { +#ifdef FLEXT_DEBUG + int refs = PyInt_AsLong(ret); + if(refs) post("py/pyext - Garbage collector reports %i unreachable objects",refs); +#endif + Py_DECREF(ret); + return false; + } + } + return true; +} + +/* +PY_EXPORT PyThreadState *py_Lock(PyThreadState *st = NULL) { return pybase::PyLock(st?st:pybase::FindThreadState()); } +PY_EXPORT PyThreadState *py_LockSys() { return pybase::PyLockSys(); } +PY_EXPORT void py_Unlock(PyThreadState *st) { pybase::PyUnlock(st); } +*/ diff --git a/externals/grill/py/source/pybase.h b/externals/grill/py/source/pybase.h new file mode 100644 index 00000000..ea5f6b49 --- /dev/null +++ b/externals/grill/py/source/pybase.h @@ -0,0 +1,206 @@ +/* + +py/pyext - python script object for PD and MaxMSP + +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. + +*/ + +#ifndef __PYBASE_H +#define __PYBASE_H + +#include "main.h" +#include "pysymbol.h" +#include "pybuffer.h" + +class pybase + : public flext +{ +public: + pybase(); + virtual ~pybase(); + + void Exit(); + + static PyObject *MakePyArgs(const t_symbol *s,int argc,const t_atom *argv,int inlet = -1); + static PyObject *MakePyArg(const t_symbol *s,int argc,const t_atom *argv); + static bool GetPyArgs(AtomList &lst,PyObject *pValue,int offs = 0); + static bool GetPyAtom(AtomList &lst,PyObject *pValue); + + static void lib_setup(); + +protected: + + virtual void DumpOut(const t_symbol *sym,int argc,const t_atom *argv) = 0; + + void m__dir(PyObject *obj); + void m__doc(PyObject *obj); + + void m_dir() { m__dir(module); } + void mg_dir(AtomList &lst) { m__dir(module); } + void m_doc() { m__doc(dict); } + + std::string modname; // module name + PyObject *module,*dict; // object module and associated dictionary + + static const char *py_doc; + + void GetDir(PyObject *obj,AtomList &lst); + + AtomList args; + + void AddCurrentPath(t_canvas *cnv); + void GetModulePath(const char *mod,char *dir,int len); + void AddToPath(const char *dir); + void SetArgs(); + + bool OutObject(flext_base *ext,int o,PyObject *obj); + + // reload module and all connected objects + void Reload(); + + bool ImportModule(const char *name); + void UnimportModule(); + bool ReloadModule(); + + // Get module registry + PyObject *GetRegistry(const char *regname); + // Set module registry + void SetRegistry(const char *regname,PyObject *reg); + + // Register object + void Register(PyObject *reg); + // Unregister object + void Unregister(PyObject *reg); + + virtual void LoadModule() = 0; + virtual void UnloadModule() = 0; + + virtual void Load() = 0; + virtual void Unload() = 0; + + void OpenEditor(); + void Respond(bool b); + + void Report() { while(PyErr_Occurred()) PyErr_Print(); } + + static bool IsAnything(const t_symbol *s) { return s && s != sym_float && s != sym_int && s != sym_symbol && s != sym_list && s != sym_pointer; } + static bool IsAtom(const t_symbol *s) { return s == sym_float || s == sym_int || s == sym_symbol || s == sym_pointer; } + + enum retval { nothing,atom,sequ }; + + // --- 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_arraysupport(PyObject *,PyObject *args); + static PyObject *py_samplerate(PyObject *,PyObject *args); + static PyObject *py_blocksize(PyObject *,PyObject *args); + +#if FLEXT_SYS == FLEXT_SYS_PD + static PyObject *py_getvalue(PyObject *,PyObject *args); + static PyObject *py_setvalue(PyObject *,PyObject *args); +#endif + +#ifdef PY_NUMARRAY + static void setupNumarray(); + static PyObject *py_import(PyObject *,PyObject *args); + static PyObject *py_export(PyObject *,PyObject *args); +#endif + + // ----thread stuff ------------ + + virtual void m_stop(int argc,const t_atom *argv); + + bool respond; +#ifdef FLEXT_THREADS + bool shouldexit; + int thrcount; + int stoptick; + Timer stoptmr; + + void tick(void *); +#endif + + int detach; + bool xlate; + + bool gencall(PyObject *fun,PyObject *args); + virtual bool thrcall(void *data) = 0; + virtual bool callpy(PyObject *fun,PyObject *args) = 0; + +#if FLEXT_SYS == FLEXT_SYS_MAX + static short patcher_myvol(t_patcher *x); +#endif + + static bool collect(); + +protected: + + void work_wrapper(void *data); + +#ifdef FLEXT_THREADS + bool qucall(PyObject *fun,PyObject *args); + void threadworker(); + PyFifo qufifo; + ThrCond qucond; + static PyThreadState *pythrsys; + + static PyThreadState *FindThreadState(); + static void FreeThreadState(); +#else + static PyThreadState *FindThreadState() { return NULL; } +#endif + +public: + +#ifdef FLEXT_THREADS + ThrMutex mutex; + inline void Lock() { mutex.Unlock(); } + inline void Unlock() { mutex.Unlock(); } + + // this is especially needed when one py/pyext object calls another one + // we don't want the message to be queued, but otoh we have to avoid deadlock + // (recursive calls can only happen in the system thread) + static int lockcount; + + static PyThreadState *PyLock(PyThreadState *st = FindThreadState()) + { + if(!IsSystemThread() || !lockcount++) PyEval_AcquireLock(); + return PyThreadState_Swap(st); + } + + static PyThreadState *PyLockSys() + { + if(!lockcount++) PyEval_AcquireLock(); + return PyThreadState_Swap(pythrsys); + } + + static void PyUnlock(PyThreadState *st) + { + PyThreadState *old = PyThreadState_Swap(st); + if(old != pythrsys || !--lockcount) PyEval_ReleaseLock(); + } + +#else + inline void Lock() {} + inline void Unlock() {} + + static PyThreadState *PyLock(PyThreadState * = NULL) { return NULL; } + static PyThreadState *PyLockSys() { return NULL; } + static void PyUnlock(PyThreadState *st) {} +#endif + + static PyObject* StdOut_Write(PyObject* Self, PyObject* Args); +}; + +#endif diff --git a/externals/grill/py/source/pybuffer.cpp b/externals/grill/py/source/pybuffer.cpp index 0893b983..13d428c4 100644 --- a/externals/grill/py/source/pybuffer.cpp +++ b/externals/grill/py/source/pybuffer.cpp @@ -8,7 +8,7 @@ WARRANTIES, see the file, "license.txt," in this distribution. */ -#include "main.h" +#include "pybase.h" #undef PY_ARRAYS diff --git a/externals/grill/py/source/pyext.cpp b/externals/grill/py/source/pyext.cpp index e500e264..88b5de0c 100644 --- a/externals/grill/py/source/pyext.cpp +++ b/externals/grill/py/source/pyext.cpp @@ -41,6 +41,7 @@ void pyext::Setup(t_classid c) FLEXT_CADDMETHOD_(c,0,"get",m_get); FLEXT_CADDMETHOD_(c,0,"set",m_set); + FLEXT_CADDATTR_VAR1(c,"xlate",xlate); FLEXT_CADDATTR_VAR1(c,"respond",respond); // ---------------------------------------------------- @@ -123,71 +124,72 @@ pyext::pyext(int argc,const t_atom *argv,bool sig): FLEXT_CALLMETHOD(threadworker); #endif - int apre = 0; - if(argc >= apre+2 && CanbeInt(argv[apre]) && CanbeInt(argv[apre+1])) { - inlets = GetAInt(argv[apre]); - outlets = GetAInt(argv[apre+1]); - apre += 2; + if(argc >= 2 && CanbeInt(argv[0]) && CanbeInt(argv[1])) { + inlets = GetAInt(argv[0]); + outlets = GetAInt(argv[1]); + argv += 2,argc -= 2; } - if(sig && argc >= apre+2 && CanbeInt(argv[apre]) && CanbeInt(argv[apre+1])) { - siginlets = GetAInt(argv[apre]); - sigoutlets = GetAInt(argv[apre+1]); - apre += 2; + if(sig && argc >= 2 && CanbeInt(argv[0]) && CanbeInt(argv[1])) { + siginlets = GetAInt(argv[0]); + sigoutlets = GetAInt(argv[1]); + argv += 2,argc -= 2; } const t_symbol *clname = NULL; + // check if the object name is pyext. , pyx. or similar + bool dotted = strrchr(thisName(),'.') != NULL; + PyThreadState *state = PyLockSys(); // init script module - if(argc > apre) { - char dir[1024]; - -#if FLEXT_SYS == FLEXT_SYS_PD - // add dir of current patch to path - AddToPath(GetString(canvas_getdir(thisCanvas()))); - // add current dir to path - AddToPath(GetString(canvas_getcurrentdir())); -#elif FLEXT_SYS == FLEXT_SYS_MAX - short path = patcher_myvol(thisCanvas()); - path_topathname(path,NULL,dir); - AddToPath(dir); -#else - #pragma message("Adding current dir to path is not implemented") -#endif - const t_symbol *scr = GetASymbol(argv[apre]); + if(argc) { + AddCurrentPath(thisCanvas()); + + const t_symbol *scr = GetASymbol(*argv); + argv++,argc--; + if(scr) { - GetModulePath(GetString(scr),dir,sizeof(dir)); - // add to path - AddToPath(dir); + char modnm[64]; + strcpy(modnm,GetString(scr)); + + if(!dotted) { + char *pt = strrchr(modnm,'.'); // search for last dot + if(pt && *pt) { + clname = MakeSymbol(pt+1); + *pt = 0; + } + } -// SetArgs(0,NULL); + char dir[1024]; + GetModulePath(modnm,dir,sizeof(dir)); + AddToPath(dir); - ImportModule(GetString(scr)); + ImportModule(modnm); } else - post("%s - script name argument is invalid",thisName()); - - ++apre; + PyErr_SetString(PyExc_ValueError,"Invalid module name"); // check for alias creation names - if(strrchr(thisName(),'.')) clname = scr; + if(dotted) clname = scr; } Register(GetRegistry(REGNAME)); - if(argc > apre || clname) { - if(!clname) clname = GetASymbol(argv[apre++]); + if(argc || clname) { + if(!clname) { + clname = GetASymbol(*argv); + argv++,argc--; + } - // class name - if(!clname) - post("%s - class name argument is invalid",thisName()); - else + if(clname) methname = clname; + else + PyErr_SetString(PyExc_ValueError,"Invalid class name"); } - if(argc > apre) initargs(argc-apre,argv+apre); + if(argc) initargs(argc,argv); Report(); @@ -240,7 +242,7 @@ bool pyext::DoInit() { // call init now, after _this has been set, which is // important for eventual callbacks from __init__ to c - PyObject *pargs = MakePyArgs(NULL,initargs.Count(),initargs.Atoms(),-1,true); + PyObject *pargs = MakePyArgs(NULL,initargs.Count(),initargs.Atoms()); if(pargs) { bool ok = true; @@ -443,7 +445,7 @@ void pyext::m_set(int argc,const t_atom *argv) else { char *ch = const_cast(GetString(argv[0])); if(PyObject_HasAttrString(pyobj,ch)) { - PyObject *pval = MakePyArgs(NULL,argc-1,argv+1,-1,false); + PyObject *pval = MakePyArgs(NULL,argc-1,argv+1); if(pval) { if(PySequence_Size(pval) == 1) { // reduce lists of one element to element itself @@ -467,10 +469,7 @@ void pyext::m_set(int argc,const t_atom *argv) bool pyext::CbMethodResort(int n,const t_symbol *s,int argc,const t_atom *argv) { - if(pyobj && n >= 1) - return work(n,s,argc,argv); - else - return flext_dsp::CbMethodResort(n,s,argc,argv); + return (pyobj && n >= 1 && work(n,s,argc,argv)) || flext_dsp::CbMethodResort(n,s,argc,argv); } @@ -527,7 +526,7 @@ bool pyext::call(const char *meth,int inlet,const t_symbol *s,int argc,const t_a PyErr_Clear(); // no method found } else { - PyObject *pargs = MakePyArgs(s,argc,argv,inlet?inlet:-1,true); + PyObject *pargs = MakePyArgs(s,argc,argv,inlet?inlet:-1); //,true); if(!pargs) { PyErr_Print(); Py_DECREF(pmeth); @@ -547,29 +546,29 @@ bool pyext::work(int n,const t_symbol *s,int argc,const t_atom *argv) // should be enough... char str[256]; - bool isfloat = s == sym_float && argc == 1; - // offset inlet index by signal inlets // \note first one is shared with messages! if(siginlets) n += siginlets-1; - // if float equals an integer, try int_* method - if(isfloat && GetAFloat(argv[0]) == GetAInt(argv[0])) { - sprintf(str,"int_%i",n); - ret = call(str,0,NULL,1,argv); - } - // try tag/inlet if(!ret) { sprintf(str,"%s_%i",GetString(s),n); ret = call(str,0,NULL,argc,argv); } - // try truncated int - if(!ret && isfloat) { - t_atom at; SetInt(at,GetAInt(argv[0])); - sprintf(str,"int_%i",n); - ret = call(str,0,NULL,1,&at); + if(!ret && argc == 1) { + if(s == sym_float) { + // try truncated float + t_atom at; SetInt(at,GetAInt(argv[0])); + sprintf(str,"int_%i",n); + ret = call(str,0,NULL,1,&at); + } + else if(s == sym_int) { + // try floating int + t_atom at; SetFloat(at,GetAFloat(argv[0])); + sprintf(str,"float_%i",n); + ret = call(str,0,NULL,1,&at); + } } // try anything/inlet @@ -578,21 +577,23 @@ bool pyext::work(int n,const t_symbol *s,int argc,const t_atom *argv) ret = call(str,0,s,argc,argv); } - // try int at any inlet - if(!ret && isfloat && GetAFloat(argv[0]) == GetAInt(argv[0])) { - ret = call("int_",0,NULL,1,argv); - } - // try tag at any inlet if(!ret) { sprintf(str,"%s_",GetString(s)); ret = call(str,n,NULL,argc,argv); } - // try truncated int at any inlet - if(!ret && isfloat) { - t_atom at; SetInt(at,GetAInt(argv[0])); - ret = call("int_",0,NULL,1,&at); + if(!ret && argc == 1) { + if(s == sym_float) { + // try truncated float at any inlet + t_atom at; SetInt(at,GetAInt(argv[0])); + ret = call("int_",0,NULL,1,&at); + } + else if(s == sym_int) { + // try floating int at any inlet + t_atom at; SetFloat(at,GetAFloat(argv[0])); + ret = call("float_",0,NULL,1,&at); + } } if(!ret) { diff --git a/externals/grill/py/source/pyext.h b/externals/grill/py/source/pyext.h index cab0c6a2..7fe0cc68 100644 --- a/externals/grill/py/source/pyext.h +++ b/externals/grill/py/source/pyext.h @@ -11,7 +11,7 @@ WARRANTIES, see the file, "license.txt," in this distribution. #ifndef __PYEXT_H #define __PYEXT_H -#include "main.h" +#include "pybase.h" class pyext : public pybase @@ -76,7 +76,7 @@ protected: int inlets,outlets; int siginlets,sigoutlets; - flext::AtomListStatic<16> initargs; + flext::AtomList initargs; virtual void LoadModule(); virtual void UnloadModule(); @@ -138,7 +138,9 @@ private: // callbacks FLEXT_ATTRVAR_I(detach) + FLEXT_ATTRVAR_B(xlate) FLEXT_ATTRVAR_B(respond) + FLEXT_CALLBACK_V(m_stop) FLEXT_CALLBACK(m_dir) FLEXT_CALLGET_V(mg_dir) diff --git a/externals/grill/py/source/pyprefix.h b/externals/grill/py/source/pyprefix.h index 3f149b61..3841cdce 100644 --- a/externals/grill/py/source/pyprefix.h +++ b/externals/grill/py/source/pyprefix.h @@ -24,4 +24,16 @@ WARRANTIES, see the file, "license.txt," in this distribution. #error You need at least flext version 0.5.0 #endif +#if FLEXT_OS == FLEXT_LINUX || FLEXT_OS == FLEXT_IRIX +#include +#endif + +#if FLEXT_SYS == FLEXT_SYS_PD && (!defined (PD_MINOR_VERSION) || PD_MINOR_VERSION < 37) +#error PD version >= 0.37 required, please upgrade! +#endif + +#include +#include + + #endif diff --git a/externals/grill/py/source/register.cpp b/externals/grill/py/source/register.cpp index d26d5504..b34fa209 100644 --- a/externals/grill/py/source/register.cpp +++ b/externals/grill/py/source/register.cpp @@ -8,7 +8,7 @@ WARRANTIES, see the file, "license.txt," in this distribution. */ -#include "main.h" +#include "pybase.h" #if 1 -- cgit v1.2.1