From f1558e3a9297c6a4fefa9d399a7c9d067d859aa3 Mon Sep 17 00:00:00 2001 From: Thomas Grill Date: Tue, 11 Jan 2005 04:59:27 +0000 Subject: added symbol type fixed incorrect unbinding of instance methods fixes for symbol type little restructuring svn path=/trunk/; revision=2489 --- externals/grill/py/maxmsp/thread-1.mxb | Bin 3172 -> 3080 bytes externals/grill/py/package.txt | 5 +- externals/grill/py/pd/sendrecv-1.pd | 10 +- externals/grill/py/py.vcproj | 11 ++- externals/grill/py/readme.txt | 8 +- externals/grill/py/scripts/sendrecv.py | 15 +-- externals/grill/py/source/bound.cpp | 121 ++++++++++++++++-------- externals/grill/py/source/clmeth.cpp | 42 +++++---- externals/grill/py/source/main.cpp | 60 ++++++++---- externals/grill/py/source/main.h | 22 ++--- externals/grill/py/source/modmeth.cpp | 36 ++++--- externals/grill/py/source/py.cpp | 13 +-- externals/grill/py/source/pyargs.cpp | 28 +++--- externals/grill/py/source/pyext.cpp | 167 ++++++++++++++++++-------------- externals/grill/py/source/pyext.h | 2 + externals/grill/py/source/pyprefix.h | 27 ++++++ externals/grill/py/source/pysymbol.cpp | 168 +++++++++++++++++++++++++++++++++ externals/grill/py/source/pysymbol.h | 57 +++++++++++ 18 files changed, 576 insertions(+), 216 deletions(-) create mode 100644 externals/grill/py/source/pyprefix.h create mode 100644 externals/grill/py/source/pysymbol.cpp create mode 100644 externals/grill/py/source/pysymbol.h diff --git a/externals/grill/py/maxmsp/thread-1.mxb b/externals/grill/py/maxmsp/thread-1.mxb index 6a0b9bbe..de2d4716 100644 Binary files a/externals/grill/py/maxmsp/thread-1.mxb and b/externals/grill/py/maxmsp/thread-1.mxb differ diff --git a/externals/grill/py/package.txt b/externals/grill/py/package.txt index 03c6fa9d..8cbd70d6 100644 --- a/externals/grill/py/package.txt +++ b/externals/grill/py/package.txt @@ -4,7 +4,8 @@ BUILDTYPE=multi BUILDDIR=build SRCDIR=source +PRECOMPILE=pyprefix.h -SRCS= main.cpp py.cpp pyext.cpp modmeth.cpp clmeth.cpp register.cpp pyargs.cpp bound.cpp +SRCS= main.cpp py.cpp pyext.cpp modmeth.cpp clmeth.cpp register.cpp pyargs.cpp bound.cpp pysymbol.cpp -HDRS= main.h pyext.h +HDRS= pyprefix.h main.h pyext.h pysymbol.h diff --git a/externals/grill/py/pd/sendrecv-1.pd b/externals/grill/py/pd/sendrecv-1.pd index 56810c74..ca399705 100644 --- a/externals/grill/py/pd/sendrecv-1.pd +++ b/externals/grill/py/pd/sendrecv-1.pd @@ -1,5 +1,5 @@ -#N canvas 145 126 642 317 12; -#X msg 125 117 reload mi ma; +#N canvas 145 126 650 325 12; +#X msg 125 81 reload mi ma; #X floatatom 48 238 5 0 0 0 - - -; #X floatatom 297 239 5 0 0 0 - - -; #X obj 297 263 s mi; @@ -8,7 +8,7 @@ #X obj 382 240 r ma; #X obj 48 262 s he; #X obj 143 238 r hu; -#X text 233 116 reload with different args; +#X text 233 80 reload with different args; #X msg 20 82 help; #X msg 19 114 doc; #X msg 58 114 doc+; @@ -19,6 +19,8 @@ -1 0; #X text 213 32 http://www.parasitaere-kapazitaeten.net; #X text 213 16 Python script objects \, (C)2003-2004 Thomas Grill; +#X msg 202 128 bind; +#X msg 249 129 unbind; #X connect 0 0 13 0; #X connect 1 0 7 0; #X connect 2 0 3 0; @@ -27,3 +29,5 @@ #X connect 10 0 13 0; #X connect 11 0 13 0; #X connect 12 0 13 0; +#X connect 19 0 13 1; +#X connect 20 0 13 1; diff --git a/externals/grill/py/py.vcproj b/externals/grill/py/py.vcproj index ed43729f..38bfb17d 100644 --- a/externals/grill/py/py.vcproj +++ b/externals/grill/py/py.vcproj @@ -91,7 +91,7 @@ RuntimeLibrary="1" RuntimeTypeInfo="TRUE" UsePrecompiledHeader="2" - PrecompiledHeaderThrough="" + PrecompiledHeaderThrough="pyprefix.h" BrowseInformation="0" WarningLevel="3" SuppressStartupBanner="TRUE" @@ -1207,6 +1207,15 @@ + + + + + + not possible within one thread) - stop individual threads -- Python type for symbols - support named (keyword) arguments (like attributes for messages) +- shutdown hook for threaded Python apps + tests: - check for python threading support diff --git a/externals/grill/py/scripts/sendrecv.py b/externals/grill/py/scripts/sendrecv.py index 0488359b..e3295ab6 100644 --- a/externals/grill/py/scripts/sendrecv.py +++ b/externals/grill/py/scripts/sendrecv.py @@ -43,7 +43,7 @@ class ex1(pyext._class): # no inlets and outlets - _inlets=0 + _inlets=1 _outlets=0 recvname="" @@ -66,21 +66,22 @@ class ex1(pyext._class): if len(args) >= 1: self.recvname = args[0] if len(args) >= 2: self.sendname = args[1] + self.bind_1() + + def bind_1(self): # bind functions to receiver names # both are called upon message self._bind(self.recvname,self.recv) self._bind(self.recvname,recv_gl) - + + def unbind_1(self): + self._unbind(self.recvname,self.recv) + self._unbind(self.recvname,recv_gl) def __del__(self): """Class destructor""" - # you can but you don't need to # unbinding is automatically done at destruction - # you can also comment out the _unbind lines - self._unbind(self.recvname,self.recv) - self._unbind(self.recvname,recv_gl) - pass diff --git a/externals/grill/py/source/bound.cpp b/externals/grill/py/source/bound.cpp index ce17578c..5690e0b1 100644 --- a/externals/grill/py/source/bound.cpp +++ b/externals/grill/py/source/bound.cpp @@ -13,7 +13,45 @@ WARRANTIES, see the file, "license.txt," in this distribution. #include -typedef std::set FuncSet; +class MethodCompare: + public std::less +{ +public: + bool operator()(PyObject *a,PyObject *b) const + { + if(PyMethod_Check(a)) + if(PyMethod_Check(b)) { + // both are methods + PyObject *sa = PyMethod_GET_SELF(a); + PyObject *sb = PyMethod_GET_SELF(b); + if(sa) + if(sb) { + // both have self + if(sa == sb) + return PyMethod_GET_FUNCTION(a) < PyMethod_GET_FUNCTION(b); + else + return sa < sb; + } + else + return false; + else + if(sb) + return true; + else + return PyMethod_GET_FUNCTION(a) < PyMethod_GET_FUNCTION(b); + } + else + return false; + else + if(PyMethod_Check(b)) + return true; + else + // both are functions + return a < b; + } +}; + +typedef std::set FuncSet; struct bounddata { @@ -33,10 +71,10 @@ bool pyext::boundmeth(flext_base *th,t_symbol *sym,int argc,t_atom *argv,void *d // call all functions bound by this symbol for(FuncSet::iterator it = obj->funcs.begin(); it != obj->funcs.end(); ++it) { PyObject *ret = PyObject_CallObject(*it,args); - if(!ret) { + if(!ret) PyErr_Print(); - } - Py_XDECREF(ret); + else + Py_DECREF(ret); } Py_XDECREF(args); @@ -47,41 +85,39 @@ bool pyext::boundmeth(flext_base *th,t_symbol *sym,int argc,t_atom *argv,void *d PyObject *pyext::pyext_bind(PyObject *,PyObject *args) { - PyObject *self,*meth; - char *name; - if(!PyArg_ParseTuple(args, "OsO:pyext_bind", &self,&name,&meth)) + PyObject *self,*meth,*name; + if(!PyArg_ParseTuple(args, "OOO:pyext_bind", &self,&name,&meth)) // borrowed references post("py/pyext - Wrong arguments!"); else if(!PyInstance_Check(self) || !(PyMethod_Check(meth) || PyFunction_Check(meth))) { post("py/pyext - Wrong argument types!"); } else { - const t_symbol *recv = MakeSymbol(name); -/* - if(GetBound(recv)) - post("py/pyext - Symbol \"%s\" is already hooked",GetString(recv)); -*/ - // make a proxy object - - if(PyMethod_Check(meth)) { - PyObject *no = PyObject_GetAttrString(meth,"__name__"); - meth = PyObject_GetAttr(self,no); - Py_DECREF(no); - } + py *th = GetThis(self); + FLEXT_ASSERT(th); + + const t_symbol *recv = pyObject_AsSymbol(name); void *data = NULL; - if(GetThis(self)->GetBoundMethod(recv,boundmeth,data)) { + if(recv && th->GetBoundMethod(recv,boundmeth,data)) { // already bound to that symbol and function bounddata *bdt = (bounddata *)data; FLEXT_ASSERT(bdt != NULL && bdt->self == self); - bdt->funcs.insert(meth); + + FuncSet::iterator it = bdt->funcs.find(meth); + if(it == bdt->funcs.end()) { + bdt->funcs.insert(meth); + Py_INCREF(meth); + } } else { + Py_INCREF(self); // self is borrowed reference + Py_INCREF(meth); + bounddata *data = new bounddata; data->self = self; data->funcs.insert(meth); - GetThis(self)->BindMethod(recv,boundmeth,data); - Py_INCREF(self); // self is borrowed reference + th->BindMethod(recv,boundmeth,data); } } @@ -91,34 +127,39 @@ PyObject *pyext::pyext_bind(PyObject *,PyObject *args) PyObject *pyext::pyext_unbind(PyObject *,PyObject *args) { - PyObject *self,*meth; - char *name; - if(!PyArg_ParseTuple(args, "OsO:pyext_bind", &self,&name,&meth)) + PyObject *self,*meth,*name; + if(!PyArg_ParseTuple(args, "OOO:pyext_bind", &self,&name,&meth)) // borrowed references post("py/pyext - Wrong arguments!"); else if(!PyInstance_Check(self) || !(PyMethod_Check(meth) || PyFunction_Check(meth))) { post("py/pyext - Wrong argument types!"); } else { - const t_symbol *recv = MakeSymbol(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); - } + py *th = GetThis(self); + FLEXT_ASSERT(th); + + const t_symbol *recv = pyObject_AsSymbol(name); void *data = NULL; - if(GetThis(self)->UnbindMethod(recv,boundmeth,&data)) { + if(recv && th->GetBoundMethod(recv,boundmeth,data)) { bounddata *bdt = (bounddata *)data; FLEXT_ASSERT(bdt != NULL); - if(PyMethod_Check(meth)) Py_DECREF(meth); - // erase from map - bdt->funcs.erase(meth); + // ATTENTION: meth is different from the element found in the map + // it just points to the same instance method + FuncSet::iterator it = bdt->funcs.find(meth); + if(it != bdt->funcs.end()) { + Py_DECREF(*it); + bdt->funcs.erase(it); + } + else + post("py/pyext - Function to unbind couldn't be found"); if(bdt->funcs.empty()) { Py_DECREF(bdt->self); delete bdt; + + th->UnbindMethod(recv,boundmeth,NULL); } } } @@ -142,13 +183,11 @@ void pyext::ClearBinding() while(GetThis(pyobj)->UnbindMethod(sym,NULL,&data)) { bounddata *bdt = (bounddata *)data; if(bdt) { - for(FuncSet::iterator it = bdt->funcs.begin(); it != bdt->funcs.end(); ++it) { - PyObject *func = *it; - if(PyMethod_Check(func)) Py_DECREF(func); - } + for(FuncSet::iterator it = bdt->funcs.begin(); it != bdt->funcs.end(); ++it) + Py_DECREF(*it); Py_DECREF(bdt->self); - if(data) delete bdt; + delete bdt; } } } diff --git a/externals/grill/py/source/clmeth.cpp b/externals/grill/py/source/clmeth.cpp index 8423aa4b..92c6b4d3 100644 --- a/externals/grill/py/source/clmeth.cpp +++ b/externals/grill/py/source/clmeth.cpp @@ -13,9 +13,10 @@ WARRANTIES, see the file, "license.txt," in this distribution. 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"}, #if FLEXT_SYS == FLEXT_SYS_PD {"_tocanvas", pyext::pyext_tocanvas, METH_VARARGS,"Send message to canvas" }, @@ -56,6 +57,7 @@ const char *pyext::pyext_doc = "_isthreaded(self): Query whether threading is enabled\n" ; +/* PyObject* pyext::pyext__init__(PyObject *,PyObject *args) { // post("pyext.__init__ called"); @@ -71,6 +73,7 @@ PyObject* pyext::pyext__del__(PyObject *,PyObject *args) Py_INCREF(Py_None); return Py_None; } +*/ PyObject* pyext::pyext_setattr(PyObject *,PyObject *args) { @@ -82,13 +85,15 @@ PyObject* pyext::pyext_setattr(PyObject *,PyObject *args) } bool 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); @@ -109,8 +114,8 @@ PyObject* pyext::pyext_getattr(PyObject *,PyObject *args) } if(PyString_Check(name)) { - char* sname = PyString_AsString(name); - if (sname) { + char* sname = PyString_AS_STRING(name); + if(sname) { if(!strcmp(sname,"_shouldexit")) { pyext *ext = GetThis(self); if(ext) @@ -122,8 +127,7 @@ PyObject* pyext::pyext_getattr(PyObject *,PyObject *args) if(!ret) { #if PY_VERSION_HEX >= 0x02020000 - // \todo borrowed or new??? - ret = PyObject_GenericGetAttr(self,name); + ret = PyObject_GenericGetAttr(self,name); // new reference (?) #else if(PyInstance_Check(self)) // borrowed reference @@ -141,22 +145,24 @@ PyObject *pyext::pyext_outlet(PyObject *,PyObject *args) // should always be a tuple! FLEXT_ASSERT(PyTuple_Check(args)); + int sz = PyTuple_GET_SIZE(args); + // borrowed references! - PyObject *self = PyTuple_GetItem(args,0); - PyObject *outl = PyTuple_GetItem(args,1); + PyObject *self,*outl; + if( - self && PyInstance_Check(self) && - outl && PyInt_Check(outl) + sz >= 2 && + (self = PyTuple_GET_ITEM(args,0)) != NULL && PyInstance_Check(self) && + (outl = PyTuple_GET_ITEM(args,1)) != NULL && PyInt_Check(outl) ) { pyext *ext = GetThis(self); - int sz = PyTuple_Size(args); PyObject *val; bool tp = sz == 3 && PySequence_Check( - val = PyTuple_GetItem(args,2) // borrow reference + val = PyTuple_GET_ITEM(args,2) // borrow reference ); if(!tp) @@ -255,18 +261,22 @@ PyObject *pyext::pyext_tocanvas(PyObject *,PyObject *args) { FLEXT_ASSERT(PyTuple_Check(args)); + int sz = PyTuple_GET_SIZE(args); + bool ok = false; - PyObject *self = PyTuple_GetItem(args,0); // borrowed ref - if(self && PyInstance_Check(self)) { + PyObject *self; // borrowed ref + if( + sz >= 1 && + (self = PyTuple_GET_ITEM(args,0)) != NULL && PyInstance_Check(self) + ) { pyext *ext = GetThis(self); - int sz = PySequence_Size(args); PyObject *val; bool tp = sz == 2 && PySequence_Check( - val = PyTuple_GetItem(args,1) // borrowed ref + val = PyTuple_GET_ITEM(args,1) // borrowed ref ); if(!tp) diff --git a/externals/grill/py/source/main.cpp b/externals/grill/py/source/main.cpp index 636435b3..33200c8b 100644 --- a/externals/grill/py/source/main.cpp +++ b/externals/grill/py/source/main.cpp @@ -56,6 +56,8 @@ void py::FreeThreadState() #endif +void initsymbol(); + void py::lib_setup() { post(""); @@ -76,8 +78,7 @@ void py::lib_setup() Py_Initialize(); #ifdef FLEXT_DEBUG -// Py_DebugFlag = 1; - Py_VerboseFlag = 1; +// Py_VerboseFlag = 1; #endif #ifdef FLEXT_THREADS @@ -99,6 +100,18 @@ void py::lib_setup() PyModule_AddStringConstant(module_obj,"__doc__",(char *)py_doc); + // 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); + // redirect stdout PyObject* py_out; py_out = Py_InitModule("stdout", StdOut_Methods); @@ -124,6 +137,16 @@ PyObject *py::module_obj = NULL; PyObject *py::module_dict = NULL; +void py::Setup(t_classid c) +{ + FLEXT_CADDMETHOD_(c,0,"doc",m_doc); + FLEXT_CADDMETHOD_(c,0,"dir",m_dir); +#ifdef FLEXT_THREADS + FLEXT_CADDATTR_VAR1(c,"detach",detach); + FLEXT_CADDMETHOD_(c,0,"stop",m_stop); +#endif +} + py::py(): module(NULL), detach(0),shouldexit(false),thrcount(0), @@ -150,7 +173,7 @@ py::~py() // Wait forever post("%s - Waiting for thread termination!",thisName()); - while(thrcount) Sleep(0.2f); + while(thrcount) Sleep(0.01f); post("%s - Okay, all threads have terminated",thisName()); } @@ -198,7 +221,7 @@ void py::m__doc(PyObject *obj) PyObject *docf = PyDict_GetItemString(obj,"__doc__"); // borrowed!!! if(docf && PyString_Check(docf)) { post(""); - const char *s = PyString_AsString(docf); + const char *s = PyString_AS_STRING(docf); // FIX: Python doc strings can easily be larger than 1k characters // -> split into separate lines @@ -226,6 +249,10 @@ void py::m__doc(PyObject *obj) } } +void py::m_click() +{ + // this should once open the editor.... +} void py::SetArgs(int argc,const t_atom *argv) { @@ -251,8 +278,7 @@ void py::ImportModule(const char *name) if(!name) return; module = PyImport_ImportModule((char *)name); // increases module_obj ref count by one - if (!module) { - + if(!module) { PyErr_Print(); dict = NULL; } @@ -264,7 +290,7 @@ void py::UnimportModule() { if(!module) return; - assert(dict && module_obj && module_dict); + FLEXT_ASSERT(dict && module_obj && module_dict); Py_DECREF(module); @@ -343,17 +369,12 @@ void py::AddToPath(const char *dir) if(dir && *dir) { PyObject *pobj = PySys_GetObject("path"); if(pobj && PyList_Check(pobj)) { - int i,n = PyList_Size(pobj); - for(i = 0; i < n; ++i) { - PyObject *pt = PyList_GetItem(pobj,i); // borrowed reference - if(PyString_Check(pt) && !strcmp(dir,PyString_AS_STRING(pt))) break; - } - if(i == n) { // string is not yet existent in path - PyObject *ps = PyString_FromString(dir); - PyList_Append(pobj,ps); - } + PyObject *ps = PyString_FromString(dir); + if(!PySequence_Contains(pobj,ps)) + PyList_Append(pobj,ps); // makes new reference + Py_DECREF(ps); } - PySys_SetObject("path",pobj); + PySys_SetObject("path",pobj); // steals reference to pobj } } @@ -378,9 +399,10 @@ PyObject* py::StdOut_Write(PyObject* self, PyObject* args) // should always be a tuple FLEXT_ASSERT(PyTuple_Check(args)); - int sz = PyTuple_Size(args); + const int sz = PyTuple_GET_SIZE(args); + for(int i = 0; i < sz; ++i) { - PyObject *val = PyTuple_GetItem(args,i); // borrowed reference + 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'); diff --git a/externals/grill/py/source/main.h b/externals/grill/py/source/main.h index 03b67e1c..ef9a16c9 100644 --- a/externals/grill/py/source/main.h +++ b/externals/grill/py/source/main.h @@ -11,23 +11,13 @@ WARRANTIES, see the file, "license.txt," in this distribution. #ifndef __MAIN_H #define __MAIN_H -#define FLEXT_ATTRIBUTES 1 - -#include -#if FLEXT_OS == FLEXT_OS_MAC -#include -#else -#include -#endif +#include "pyprefix.h" +#include "pysymbol.h" #if FLEXT_OS == FLEXT_LINUX || FLEXT_OS == FLEXT_IRIX #include #endif -#if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 500) -#error You need at least flext version 0.5.0 -#endif - #define PY__VERSION "0.2.0pre" @@ -61,7 +51,7 @@ protected: class py: public flext_base { - FLEXT_HEADER(py,flext_base) + FLEXT_HEADER_S(py,flext_base,Setup) public: py(); @@ -190,8 +180,12 @@ public: static PyObject* StdOut_Write(PyObject* Self, PyObject* Args); protected: - // callbacks + virtual void m_click(); + + static void Setup(t_classid c); + + // callbacks FLEXT_ATTRVAR_I(detach) FLEXT_ATTRVAR_B(respond) FLEXT_CALLBACK_V(m_stop) diff --git a/externals/grill/py/source/modmeth.cpp b/externals/grill/py/source/modmeth.cpp index 88e3290a..cf4dca88 100644 --- a/externals/grill/py/source/modmeth.cpp +++ b/externals/grill/py/source/modmeth.cpp @@ -107,16 +107,19 @@ PyObject *py::py_send(PyObject *,PyObject *args) // should always be a tuple FLEXT_ASSERT(PyTuple_Check(args)); - PyObject *name = PyTuple_GetItem(args,0); // borrowed reference - if(name && PyString_Check(name)) { - const t_symbol *recv = MakeSymbol(PyString_AsString(name)); - int sz = PySequence_Size(args); + const int sz = PyTuple_GET_SIZE(args); + + const t_symbol *recv; + if( + sz >= 1 && + (recv = pyObject_AsSymbol(PyTuple_GET_ITEM(args,0))) != NULL + ) { PyObject *val; bool tp = sz == 2 && PySequence_Check( - val = PyTuple_GetItem(args,1) // borrowed ref + val = PyTuple_GET_ITEM(args,1) // borrowed ref ); if(!tp) @@ -168,12 +171,14 @@ PyObject *py::py_getvalue(PyObject *self,PyObject *args) { FLEXT_ASSERT(PyTuple_Check(args)); + const int sz = PyTuple_GET_SIZE(args); + const t_symbol *sym; PyObject *ret; - PyObject *name = PyTuple_GetItem(args,0); // borrowed reference - - if(name && PyString_Check(name)) { - const t_symbol *sym = MakeSymbol(PyString_AsString(name)); + if( + sz == 1 && + (sym = pyObject_AsSymbol(PyTuple_GET_ITEM(args,0))) != NULL + ) { float f; if(value_getfloat(const_cast(sym),&f)) { post("py/pyext - Could not get value '%s'",GetString(sym)); @@ -193,10 +198,15 @@ PyObject *py::py_setvalue(PyObject *self,PyObject *args) { FLEXT_ASSERT(PyTuple_Check(args)); - PyObject *name = PyTuple_GetItem(args,0); // borrowed reference - PyObject *val = PyTuple_GetItem(args,1); // borrowed reference - if(name && val && PyString_Check(name) && PyNumber_Check(val)) { - const t_symbol *sym = MakeSymbol(PyString_AsString(name)); + const int sz = PyTuple_GET_SIZE(args); + const t_symbol *sym; + PyObject *val; // borrowed reference + + if( + sz == 2 && + (sym = pyObject_AsSymbol(PyTuple_GET_ITEM(args,0))) != NULL && + PyNumber_Check(val = PyTuple_GET_ITEM(args,1)) + ) { float f = (float)PyFloat_AsDouble(val); if(value_setfloat(const_cast(sym),f)) diff --git a/externals/grill/py/source/py.cpp b/externals/grill/py/source/py.cpp index 3bad1890..68f805b8 100644 --- a/externals/grill/py/source/py.cpp +++ b/externals/grill/py/source/py.cpp @@ -81,19 +81,14 @@ FLEXT_LIB_V("py",pyobj) void pyobj::Setup(t_classid c) { - FLEXT_CADDBANG(c,0,m_bang); FLEXT_CADDMETHOD_(c,0,"reload",m_reload_); - FLEXT_CADDMETHOD_(c,0,"reload.",m_reload); - FLEXT_CADDMETHOD_(c,0,"set",m_set); - FLEXT_CADDMETHOD_(c,0,"doc",m_doc); + FLEXT_CADDMETHOD_(c,0,"reload.",m_reload); FLEXT_CADDMETHOD_(c,0,"doc+",m_doc_); -#ifdef FLEXT_THREADS - FLEXT_CADDATTR_VAR1(c,"detach",detach); - FLEXT_CADDMETHOD_(c,0,"stop",m_stop); -#endif - FLEXT_CADDMETHOD_(c,0,"dir",m_dir); 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); diff --git a/externals/grill/py/source/pyargs.cpp b/externals/grill/py/source/pyargs.cpp index 90686a90..1b549160 100644 --- a/externals/grill/py/source/pyargs.cpp +++ b/externals/grill/py/source/pyargs.cpp @@ -12,7 +12,8 @@ WARRANTIES, see the file, "license.txt," in this distribution. static PyObject *MakePyAtom(const t_atom &at) { - if(flext::IsSymbol(at)) return PyString_FromString(flext::GetString(at)); + if(flext::IsSymbol(at)) + return pySymbol_FromSymbol(flext::GetSymbol(at)); // else if(flext::IsPointer(at)) return NULL; // not handled else if(flext::CanbeInt(at) && flext::CanbeFloat(at)) { // if a number can be an integer... let at be an integer! @@ -45,24 +46,16 @@ PyObject *py::MakePyArgs(const t_symbol *s,int argc,const t_atom *argv,int inlet int pix = 0; - if(inlet >= 0) { - PyObject *pValue = PyInt_FromLong(inlet); - - // reference stolen: - PyTuple_SetItem(pArgs, pix++, pValue); - } + 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; - if(any) { - PyObject *pValue = PyString_FromString(GetString(s)); - - // reference stolen here: - PyTuple_SetItem(tmp, ix++, pValue); - } + if(any) + PyTuple_SET_ITEM(tmp, ix++, pySymbol_FromSymbol(s)); for(int i = 0; i < argc; ++i) { PyObject *pValue = MakePyAtom(argv[i]); @@ -72,11 +65,11 @@ PyObject *py::MakePyArgs(const t_symbol *s,int argc,const t_atom *argv,int inlet } /* pValue reference stolen here: */ - PyTuple_SetItem(tmp, ix++, pValue); + PyTuple_SET_ITEM(tmp, ix++, pValue); } if(tmp != pArgs) { - PyTuple_SetItem(pArgs, pix++, tmp); + PyTuple_SET_ITEM(pArgs, pix++, tmp); #if PY_VERSION_HEX >= 0x02020000 _PyTuple_Resize(&pArgs,pix); #else @@ -126,7 +119,8 @@ flext::AtomList *py::GetPyArgs(PyObject *pValue,PyObject **self) if(PyInt_Check(arg)) SetInt((*ret)[ix],PyInt_AsLong(arg)); else if(PyLong_Check(arg)) SetInt((*ret)[ix],PyLong_AsLong(arg)); else if(PyFloat_Check(arg)) SetFloat((*ret)[ix],(float)PyFloat_AsDouble(arg)); - else if(PyString_Check(arg)) SetString((*ret)[ix],PyString_AsString(arg)); + else if(pySymbol_Check(arg)) SetSymbol((*ret)[ix],pySymbol_AS_SYMBOL(arg)); + else if(PyString_Check(arg)) SetString((*ret)[ix],PyString_AS_STRING(arg)); else if(ix == 0 && self && PyInstance_Check(arg)) { // assumed to be self ... that should be checked _somehow_ !!! Py_INCREF(arg); @@ -136,7 +130,7 @@ flext::AtomList *py::GetPyArgs(PyObject *pValue,PyObject **self) PyObject *tp = PyObject_Type(arg); PyObject *stp = tp?PyObject_Str(tp):NULL; char *tmp = ""; - if(stp) tmp = PyString_AsString(stp); + if(stp) tmp = PyString_AS_STRING(stp); post("py/pyext: Could not convert argument %s",tmp); Py_XDECREF(stp); Py_XDECREF(tp); diff --git a/externals/grill/py/source/pyext.cpp b/externals/grill/py/source/pyext.cpp index fed6c89e..4acafd60 100644 --- a/externals/grill/py/source/pyext.cpp +++ b/externals/grill/py/source/pyext.cpp @@ -20,20 +20,13 @@ void pyext::Setup(t_classid c) { sym_get = flext::MakeSymbol("get"); - FLEXT_CADDMETHOD_(c,0,"reload.",m_reload); FLEXT_CADDMETHOD_(c,0,"reload",m_reload_); - FLEXT_CADDMETHOD_(c,0,"dir",m_dir); - FLEXT_CADDMETHOD_(c,0,"dir+",m_dir_); - FLEXT_CADDMETHOD_(c,0,"doc",m_doc); + FLEXT_CADDMETHOD_(c,0,"reload.",m_reload); FLEXT_CADDMETHOD_(c,0,"doc+",m_doc_); - - FLEXT_CADDATTR_VAR(c,"args",args,ms_args); + FLEXT_CADDMETHOD_(c,0,"dir+",m_dir_); FLEXT_CADDATTR_GET(c,"dir+",mg_dir_); -#ifdef FLEXT_THREADS - FLEXT_CADDATTR_VAR1(c,"detach",detach); - FLEXT_CADDMETHOD_(c,0,"stop",m_stop); -#endif + FLEXT_CADDATTR_VAR(c,"args",args,ms_args); FLEXT_CADDMETHOD_(c,0,"get",m_get); FLEXT_CADDMETHOD_(c,0,"set",m_set); @@ -172,58 +165,8 @@ pyext::pyext(int argc,const t_atom *argv): if(methname) { MakeInstance(); - if(pyobj) { - if(inlets >= 0) { - // set number of inlets - PyObject *res = PyInt_FromLong(inlets); - int ret = PyObject_SetAttrString(pyobj,"_inlets",res); - FLEXT_ASSERT(!ret); - } - if(outlets >= 0) { - // set number of outlets - PyObject *res = PyInt_FromLong(outlets); - int ret = PyObject_SetAttrString(pyobj,"_outlets",res); - FLEXT_ASSERT(!ret); - } - - DoInit(); // call __init__ constructor - // __init__ can override the number of inlets and outlets - - if(inlets < 0) { - // get number of inlets - inlets = 1; - PyObject *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(); - } - if(outlets < 0) { - // get number of outlets - outlets = 1; - PyObject *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(); - } - } + if(pyobj) + InitInOut(inlets,outlets); } else inlets = outlets = 0; @@ -245,12 +188,7 @@ pyext::~pyext() { PyThreadState *state = PyLock(); - ClearBinding(); - - if(pyobj) { - if(pyobj->ob_refcnt > 1) post("%s - Python object is still referenced",thisName()); - Py_DECREF(pyobj); // opposite of SetClssMeth - } + DoExit(); Unregister("_pyext"); UnimportModule(); @@ -269,8 +207,8 @@ bool pyext::DoInit() PyObject *init = PyObject_GetAttrString(pyobj,"__init__"); // get ref if(init) { - if(PyCallable_Check(init)) { - PyObject *res = PyEval_CallObject(init,pargs); + if(PyMethod_Check(init)) { + PyObject *res = PyObject_CallObject(init,pargs); if(!res) PyErr_Print(); else @@ -283,6 +221,86 @@ bool pyext::DoInit() return true; } +void pyext::DoExit() +{ + ClearBinding(); + + if(pyobj) { + if(pyobj->ob_refcnt > 1) { + post("%s - Python object is still referenced",thisName()); + + // Force-quit object: + // call __del__ manually + // this is dangerous, because it could get called a second time + // if object really has no more references then + PyObject *meth = PyObject_GetAttrString(pyobj,"__del__"); // get ref + if(meth) { + if(PyMethod_Check(meth)) { + PyObject *res = PyObject_CallObject(meth,NULL); + if(!res) + PyErr_Print(); + else + Py_DECREF(res); + } + Py_DECREF(meth); + } + } + Py_DECREF(pyobj); // opposite of SetClssMeth + } +} + +void pyext::InitInOut(int &inl,int &outl) +{ + if(inl >= 0) { + // set number of inlets + int ret = PyObject_SetAttrString(pyobj,"_inlets",PyInt_FromLong(inl)); + FLEXT_ASSERT(!ret); + } + if(outl >= 0) { + // set number of outlets + int ret = PyObject_SetAttrString(pyobj,"_outlets",PyInt_FromLong(outl)); + FLEXT_ASSERT(!ret); + } + + DoInit(); // call __init__ constructor + // __init__ can override the number of inlets and outlets + + if(inl < 0) { + // get number of inlets + inl = 1; + PyObject *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)) + inl = PyInt_AS_LONG(res); + Py_DECREF(res); + } + else + PyErr_Clear(); + } + if(outl < 0) { + // get number of outlets + outl = 1; + PyObject *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)) + outl = PyInt_AS_LONG(res); + Py_DECREF(res); + } + else + PyErr_Clear(); + } +} + bool pyext::MakeInstance() { // pyobj should already have been decref'd / cleared before getting here!! @@ -311,8 +329,7 @@ bool pyext::MakeInstance() void pyext::Reload() { - ClearBinding(); - Py_XDECREF(pyobj); + DoExit(); // by here, the Python class destructor should have been called! @@ -320,6 +337,12 @@ void pyext::Reload() ReloadModule(); MakeInstance(); + + int inl = -1,outl = -1; + InitInOut(inl,outl); + + if(inl != inlets || outl != outlets) + post("%s - Inlet and outlet count can't be changed by reload",thisName()); } diff --git a/externals/grill/py/source/pyext.h b/externals/grill/py/source/pyext.h index 45ba866d..f94b2f19 100644 --- a/externals/grill/py/source/pyext.h +++ b/externals/grill/py/source/pyext.h @@ -70,6 +70,8 @@ private: void ClearBinding(); bool MakeInstance(); bool DoInit(); + void DoExit(); + void InitInOut(int &inlets,int &outlets); AtomList args; diff --git a/externals/grill/py/source/pyprefix.h b/externals/grill/py/source/pyprefix.h new file mode 100644 index 00000000..fcce64a1 --- /dev/null +++ b/externals/grill/py/source/pyprefix.h @@ -0,0 +1,27 @@ +/* + +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 __PREFIX_H +#define __PREFIX_H + +#define FLEXT_ATTRIBUTES 1 +#include + +#if FLEXT_OS == FLEXT_OS_MAC +#include +#else +#include +#endif + +#if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 500) +#error You need at least flext version 0.5.0 +#endif + +#endif diff --git a/externals/grill/py/source/pysymbol.cpp b/externals/grill/py/source/pysymbol.cpp new file mode 100644 index 00000000..176f9895 --- /dev/null +++ b/externals/grill/py/source/pysymbol.cpp @@ -0,0 +1,168 @@ +/* + +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 "pysymbol.h" + +inline pySymbol *symbol_newsym(const t_symbol *sym) +{ + pySymbol *self = (pySymbol *)pySymbol_Type.tp_alloc(&pySymbol_Type, 0); + if(self) self->sym = sym; + return self; +} + +static PyObject *symbol_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + return (PyObject *)symbol_newsym(flext::sym__); +} + +static int symbol_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + FLEXT_ASSERT(pySymbol_Check(self)); + + PyObject *arg = PySequence_GetItem(args,0); // new reference + if(!arg) return -1; + + int ret = 0; + + if(pySymbol_Check(arg)) + ((pySymbol *)self)->sym = pySymbol_AS_SYMBOL(arg); + else if(PyString_Check(arg)) + ((pySymbol *)self)->sym = flext::MakeSymbol(PyString_AS_STRING(arg)); + else + ret = -1; + Py_DECREF(arg); + + return ret; +} + +static PyObject *symbol_str(PyObject *self) +{ + FLEXT_ASSERT(pySymbol_Check(self)); + return (PyObject *)PyString_FromString(pySymbol_AS_STRING(self)); +} + +static PyObject *symbol_repr(PyObject *self) +{ + FLEXT_ASSERT(pySymbol_Check(self)); + return (PyObject *)PyString_FromFormat("",pySymbol_AS_STRING(self)); +} + +static PyObject *symbol_richcompare(PyObject *a,PyObject *b,int cmp) +{ + if(pySymbol_Check(a) && pySymbol_Check(b)) { + const t_symbol *asym = pySymbol_AS_SYMBOL(a); + const t_symbol *bsym = pySymbol_AS_SYMBOL(a); + bool ret; + switch(cmp) { + case Py_LT: ret = asym < bsym; + case Py_LE: ret = asym <= bsym; + case Py_EQ: ret = asym == bsym; + case Py_NE: ret = asym != bsym; + case Py_GT: ret = asym > bsym; + case Py_GE: ret = asym >= bsym; + } + return PyBool_FromLong(ret); + } + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; +} + +static long symbol_hash(PyObject *self) +{ + FLEXT_ASSERT(pySymbol_Check(self)); + return (long)pySymbol_AS_SYMBOL(self); +} + +PyTypeObject pySymbol_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Symbol", /*tp_name*/ + sizeof(pySymbol), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + symbol_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + symbol_hash, /*tp_hash */ + 0, /*tp_call*/ + symbol_str, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT /*| Py_TPFLAGS_BASETYPE*/, /*tp_flags*/ + "Symbol objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + symbol_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + symbol_init, /* tp_init */ + 0, /* tp_alloc */ + symbol_new, /* tp_new */ +}; + +pySymbol *pySymbol__; +pySymbol *pySymbol_bang; +pySymbol *pySymbol_list; +pySymbol *pySymbol_symbol; +pySymbol *pySymbol_float; +pySymbol *pySymbol_int; + + +void initsymbol() +{ + if(PyType_Ready(&pySymbol_Type) < 0) + FLEXT_ASSERT(false); + else + Py_INCREF(&pySymbol_Type); + + // initialize predefined objects + pySymbol__ = symbol_newsym(flext::sym__); + pySymbol_bang = symbol_newsym(flext::sym_bang); + pySymbol_list = symbol_newsym(flext::sym_list); + pySymbol_symbol = symbol_newsym(flext::sym_symbol); + pySymbol_float = symbol_newsym(flext::sym_float); + pySymbol_int = symbol_newsym(flext::sym_int); +} + + +PyObject *pySymbol_FromSymbol(const t_symbol *sym) +{ + pySymbol *op; + if(sym == flext::sym__) + Py_INCREF(op = pySymbol__); + else if(sym == flext::sym_bang) + Py_INCREF(op = pySymbol_bang); + else if(sym == flext::sym_list) + Py_INCREF(op = pySymbol_list); + else if(sym == flext::sym_symbol) + Py_INCREF(op = pySymbol_symbol); + else if(sym == flext::sym_float) + Py_INCREF(op = pySymbol_float); + else if(sym == flext::sym_int) + Py_INCREF(op = pySymbol_int); + else + op = symbol_newsym(sym); + return (PyObject *)op; +} diff --git a/externals/grill/py/source/pysymbol.h b/externals/grill/py/source/pysymbol.h new file mode 100644 index 00000000..bf06437d --- /dev/null +++ b/externals/grill/py/source/pysymbol.h @@ -0,0 +1,57 @@ +/* + +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 "pyprefix.h" + +typedef struct { + PyObject_HEAD + /* Type-specific fields go here. */ + const t_symbol *sym; +} pySymbol; + +extern PyTypeObject pySymbol_Type; + +extern pySymbol *pySymbol__; +extern pySymbol *pySymbol_bang; +extern pySymbol *pySymbol_list; +extern pySymbol *pySymbol_symbol; +extern pySymbol *pySymbol_float; +extern pySymbol *pySymbol_int; + + +#define pySymbol_Check(op) PyObject_TypeCheck(op, &pySymbol_Type) +#define pySymbol_CheckExact(op) ((op)->ob_type == &PySymbol_Type) + + +PyObject *pySymbol_FromSymbol(const t_symbol *sym); + +inline const t_symbol *pySymbol_AS_SYMBOL(PyObject *op) +{ + return ((pySymbol *)op)->sym; +} + +inline const t_symbol *pySymbol_AsSymbol(PyObject *op) +{ + return pySymbol_Check(op)?pySymbol_AS_SYMBOL(op):NULL; +} + +inline const char *pySymbol_AS_STRING(PyObject *op) +{ + return flext::GetString(pySymbol_AS_SYMBOL(op)); +} + +inline const t_symbol *pyObject_AsSymbol(PyObject *op) +{ + if(PyString_Check(op)) + return flext::MakeSymbol(PyString_AS_STRING(op)); + else + return pySymbol_AsSymbol(op); +} + -- cgit v1.2.1