From 38f3d28f38ca8167e4f28433097f5b2b656daa1e Mon Sep 17 00:00:00 2001 From: Thomas Grill Date: Tue, 8 May 2018 00:20:06 +0200 Subject: [PATCH 01/40] Changes for python3 compilation --- build/gnumake-mac-gcc.inc | 2 +- source/pyprefix.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/gnumake-mac-gcc.inc b/build/gnumake-mac-gcc.inc index 8806024..7ae6eb2 100644 --- a/build/gnumake-mac-gcc.inc +++ b/build/gnumake-mac-gcc.inc @@ -6,7 +6,7 @@ _LOCAL_LIBRARY := /Library/Python/$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION) _SYSTEM_LIBRARY := /System/Library/Python/$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION) DEFS += -DPY_EXPORTS -INCPATH += -F/Library/Frameworks -framework Python +#INCPATH += -F/Library/Frameworks -framework Python ifeq ($(PY_DEFAULT),1) LIBS += $(_SYSTEM_FRAMEWORK)/Python diff --git a/source/pyprefix.h b/source/pyprefix.h index 8b242a4..66d1820 100644 --- a/source/pyprefix.h +++ b/source/pyprefix.h @@ -16,7 +16,7 @@ WARRANTIES, see the file, "license.txt," in this distribution. // otherwise some functions don't get defined #include -#if FLEXT_OS == FLEXT_OS_MAC +#if 0 //FLEXT_OS == FLEXT_OS_MAC #include #else #include From b23904c40d74f98946ad70d6f092fd780416b45e Mon Sep 17 00:00:00 2001 From: Thomas Grill Date: Thu, 20 Jun 2019 23:19:05 +0200 Subject: [PATCH 02/40] Bashed string/unicode and int/long inconsistencies --- source/clmeth.cpp | 35 ++++++++++++++++---- source/pyargs.cpp | 40 +++++++++++++++++++---- source/pybase.cpp | 79 +++++++++++++++++++++++++++++++++++++-------- source/pybuffer.cpp | 14 ++++++-- source/pybuffer.h | 14 ++++++-- source/pybundle.cpp | 24 +++++++++++--- source/pyext.cpp | 48 ++++++++++++++++++++++++--- source/pysymbol.cpp | 40 +++++++++++++++++++---- source/pysymbol.h | 20 ++++++++++-- 9 files changed, 267 insertions(+), 47 deletions(-) diff --git a/source/clmeth.cpp b/source/clmeth.cpp index 4427b54..a7e6d8d 100644 --- a/source/clmeth.cpp +++ b/source/clmeth.cpp @@ -1,7 +1,7 @@ /* py/pyext - python external object for PD and Max/MSP -Copyright (c)2002-2008 Thomas Grill (gr@grrrr.org) +Copyright (c)2002-2019 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. */ @@ -85,7 +85,13 @@ PyObject* pyext::pyext__str__(PyObject *,PyObject *args) return NULL; } - return PyString_FromFormat("",self); + return +#if PY_MAJOR_VERSION < 3 + PyString_FromFormat +#else + PyUnicode_FromFormat +#endif + ("", self); } PyObject* pyext::pyext_setattr(PyObject *,PyObject *args) @@ -120,14 +126,19 @@ PyObject* pyext::pyext_setattr(PyObject *,PyObject *args) PyObject* pyext::pyext_getattr(PyObject *,PyObject *args) { - PyObject *self,*name,*ret = NULL; - if(!PyArg_ParseTuple(args, "OO:pyext_getattr", &self,&name)) { + PyObject *self, *name, *ret = NULL; + if(!PyArg_ParseTuple(args, "OO:pyext_getattr", &self, &name)) { // handle error ERRINTERNAL(); } +#if PY_MAJOR_VERSION < 3 if(PyString_Check(name)) { - char* sname = PyString_AS_STRING(name); + const char *sname = PyString_AS_STRING(name); +#else + if(PyUnicode_Check(name)) { + const char *sname = PyUnicode_AsUTF8(name); +#endif if(sname) { #ifdef FLEXT_THREADS if(!strcmp(sname,"_shouldexit")) { @@ -182,7 +193,12 @@ PyObject *pyext::pyext_outlet(PyObject *,PyObject *args) if( sz >= 2 && (self = PyTuple_GET_ITEM(args,0)) != NULL && PyInstance_Check(self) && - (outl = PyTuple_GET_ITEM(args,1)) != NULL && PyInt_Check(outl) + (outl = PyTuple_GET_ITEM(args,1)) != NULL && +#if PY_MAJOR_VERSION < 3 + PyInt_Check(outl) +#else + PyLong_Check(outl) +#endif ) { pyext *ext = GetThis(self); if(!ext) { @@ -211,7 +227,12 @@ PyObject *pyext::pyext_outlet(PyObject *,PyObject *args) val = PyTuple_GetSlice(args,2,sz); // new ref #endif - int o = PyInt_AS_LONG(outl); + int o; +#if PY_MAJOR_VERSION < 3 + o = PyInt_AS_LONG(outl); +#else + o = PyLong_AS_LONG(outl); +#endif if(o >= 1 && o <= ext->Outlets()) { // offset outlet by signal outlets o += ext->sigoutlets; diff --git a/source/pyargs.cpp b/source/pyargs.cpp index 837c899..6845f98 100644 --- a/source/pyargs.cpp +++ b/source/pyargs.cpp @@ -1,7 +1,7 @@ /* py/pyext - python external object for PD and MaxMSP -Copyright (c)2002-2015 Thomas Grill (gr@grrrr.org) +Copyright (c)2002-2019 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. */ @@ -20,7 +20,13 @@ static PyObject *MakePyAtom(const t_atom &at) // 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); + return (double)ival == fval? +#if PY_MAJOR_VERSION < 3 + PyInt_FromLong(ival) +#else + PyLong_FromLong(ival) +#endif + :PyFloat_FromDouble(fval); } #else else if(flext::IsFloat(at)) @@ -69,7 +75,13 @@ PyObject *pybase::MakePyArgs(const t_symbol *s,int argc,const t_atom *argv,int i int pix = 0; if(inlet >= 0) - PyTuple_SET_ITEM(ret,pix++,PyInt_FromLong(inlet)); + PyTuple_SET_ITEM(ret, pix++, +#if PY_MAJOR_VERSION < 3 + PyInt_FromLong(inlet) +#else + PyLong_FromLong(inlet) +#endif + ); if(any) PyTuple_SET_ITEM(ret,pix++,pySymbol_FromSymbol(s)); @@ -126,7 +138,12 @@ PyObject *pybase::MakePyArg(const t_symbol *s,int argc,const t_atom *argv) inline bool issym(PyObject *p) { - return PyString_Check(p) || pySymbol_Check(p); +return + PyUnicode_Check(p) +#if PY_MAJOR_VERSION < 3 + || PyString_Check(p) +#endif + || pySymbol_Check(p); } inline bool isseq(PyObject *p) @@ -136,16 +153,27 @@ inline bool isseq(PyObject *p) const t_symbol *pybase::getone(t_atom &at,PyObject *arg) { +#if PY_MAJOR_VERSION < 3 if(PyInt_Check(arg)) { flext::SetInt(at,PyInt_AsLong(arg)); return sym_fint; } - else if(PyLong_Check(arg)) { flext::SetInt(at,PyLong_AsLong(arg)); return sym_fint; } + else +#endif + if(PyLong_Check(arg)) { flext::SetInt(at,PyLong_AsLong(arg)); return sym_fint; } else if(PyFloat_Check(arg)) { flext::SetFloat(at,(float)PyFloat_AsDouble(arg)); return flext::sym_float; } else if(pySymbol_Check(arg)) { flext::SetSymbol(at,pySymbol_AS_SYMBOL(arg)); return flext::sym_symbol; } +#if PY_MAJOR_VERSION < 3 else if(PyString_Check(arg)) { flext::SetString(at,PyString_AS_STRING(arg)); return flext::sym_symbol; } +#endif + else if(PyUnicode_Check(arg)) { flext::SetString(at,PyUnicode_AsUTF8(arg)); return flext::sym_symbol; } else { PyObject *tp = PyObject_Type(arg); PyObject *stp = tp?PyObject_Str(tp):NULL; const char *tmp = ""; - if(stp) tmp = PyString_AS_STRING(stp); + if(stp) +#if PY_MAJOR_VERSION < 3 + tmp = PyString_AS_STRING(stp); +#else + tmp = PyUnicode_AsUTF8(stp); +#endif flext::post("py/pyext: Could not convert argument %s",tmp); Py_XDECREF(stp); Py_XDECREF(tp); diff --git a/source/pybase.cpp b/source/pybase.cpp index 826e6a4..abaf583 100644 --- a/source/pybase.cpp +++ b/source/pybase.cpp @@ -1,7 +1,7 @@ /* py/pyext - python external object for PD and MaxMSP -Copyright (c)2002-2015 Thomas Grill (gr@grrrr.org) +Copyright (c)2002-2019 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. */ @@ -103,7 +103,7 @@ void pybase::lib_setup() post(""); post("------------------------------------------------"); post("py/pyext %s - python script objects",PY__VERSION); - post("(C)2002-2015 Thomas Grill - http://grrrr.org/ext"); + post("(C)2002-2019 Thomas Grill - http://grrrr.org/ext"); post(""); post("using Python %s",Py_GetVersion()); @@ -310,10 +310,21 @@ void pybase::m__doc(PyObject *obj) ThrLock lock; PyObject *docf = PyDict_GetItemString(obj,"__doc__"); // borrowed!!! - if(docf && PyString_Check(docf)) { + if(docf && +#if PY_MAJOR_VERSION < 3 + PyString_Check(docf) +#else + PyUnicode_Check(docf) +#endif + ) { post(""); - const char *s = PyString_AS_STRING(docf); + const char *s; +#if PY_MAJOR_VERSION < 3 + s = PyString_AS_STRING(docf); +#else + s = PyUnicode_AsUTF8(docf); +#endif // FIX: Python doc strings can easily be larger than 1k characters // -> split into separate lines @@ -650,7 +661,12 @@ void pybase::AddToPath(const char *dir) if(dir && *dir) { PyObject *pobj = PySys_GetObject(const_cast("path")); if(pobj && PyList_Check(pobj)) { - PyObject *ps = PyString_FromString(dir); + PyObject *ps; +#if PY_MAJOR_VERSION < 3 + ps = PyString_FromString(dir); +#else + ps = PyUnicode_FromString(dir); +#endif if(!PySequence_Contains(pobj,ps)) PyList_Append(pobj,ps); // makes new reference Py_DECREF(ps); @@ -745,30 +761,67 @@ PyObject* pybase::StdOut_Write(PyObject* self, PyObject* 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'); + const char *cstr; +#if PY_MAJOR_VERSION < 3 + cstr = PyString_AS_STRING(str); +#else + cstr = PyUnicode_AsUTF8(str); +#endif + const char *lf = strchr(cstr, '\n'); // line feed in string if(!lf) { // no -> just append - if(output) - PyString_ConcatAndDel(&output,str); // str is decrefd + if(output) { +#if PY_MAJOR_VERSION < 3 + PyString_ConcatAndDel(&output, str); // str is decrefd +#else + PyObject *newobj = PyUnicode_Concat(output, str); + Py_DECREF(output); + Py_DECREF(str); + output = newobj; +#endif + } 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 + PyObject *part = +#if PY_MAJOR_VERSION < 3 + PyString_FromStringAndSize +#else + PyUnicode_FromStringAndSize +#endif + (cstr, lf-cstr); // new reference + if(output) { +#if PY_MAJOR_VERSION < 3 + PyString_ConcatAndDel(&output, part); // part is decrefd +#else + PyObject *newobj = PyUnicode_Concat(output, part); + Py_DECREF(output); + Py_DECREF(part); + output = newobj; +#endif + } else output = part; // take str reference // output concatenated string +#if PY_MAJOR_VERSION < 3 post(PyString_AS_STRING(output)); +#else + post(PyUnicode_AsUTF8(output)); +#endif Py_DECREF(output); - output = PyString_FromString(lf+1); // new reference + output = +#if PY_MAJOR_VERSION < 3 + PyString_FromString +#else + PyUnicode_FromString +#endif + (lf+1); // new reference } } diff --git a/source/pybuffer.cpp b/source/pybuffer.cpp index 4690381..212975d 100644 --- a/source/pybuffer.cpp +++ b/source/pybuffer.cpp @@ -1,7 +1,7 @@ /* py/pyext - python script object for PD and Max/MSP -Copyright (c)2002-2015 Thomas Grill (gr@grrrr.org) +Copyright (c)2002-2019 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. */ @@ -105,8 +105,12 @@ static int buffer_init(PyObject *obj, PyObject *args, PyObject *kwds) if(pySymbol_Check(arg)) self->sym = pySymbol_AS_SYMBOL(arg); +#if PY_MAJOR_VERSION < 3 else if(PyString_Check(arg)) self->sym = flext::MakeSymbol(PyString_AS_STRING(arg)); +#endif + else if(PyUnicode_Check(arg)) + self->sym = flext::MakeSymbol(PyUnicode_AsUTF8(arg)); else ret = -1; Py_DECREF(arg); @@ -125,7 +129,13 @@ static int buffer_init(PyObject *obj, PyObject *args, PyObject *kwds) static PyObject *buffer_repr(PyObject *self) { FLEXT_ASSERT(pySamplebuffer_Check(self)); - return (PyObject *)PyString_FromFormat("",pySamplebuffer_AS_STRING(self)); + return (PyObject *) +#if PY_MAJOR_VERSION < 3 + PyString_FromFormat +#else + PyUnicode_FromFormat +#endif + ("", pySamplebuffer_AS_STRING(self)); } static long buffer_hash(PyObject *self) diff --git a/source/pybuffer.h b/source/pybuffer.h index 5869d13..d405a22 100644 --- a/source/pybuffer.h +++ b/source/pybuffer.h @@ -1,7 +1,7 @@ /* py/pyext - python script object for PD and Max/MSP -Copyright (c)2002-2015 Thomas Grill (gr@grrrr.org) +Copyright (c)2002-2019 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. */ @@ -56,7 +56,17 @@ inline PyObject *pySamplebuffer_FromString(const char *str) inline PyObject *pySamplebuffer_FromString(PyObject *str) { - return pySamplebuffer_FromString(PyString_AsString(str)); + const char *cstr; +#if PY_MAJOR_VERSION < 3 + if(PyString_Check(str)) + cstr = PyString_AsString(str); + else +#endif + if(PyUnicode_Check(str)) + cstr = PyUnicode_AsUTF8(str); + else + PyErr_SetString(PyExc_TypeError, "Type must be string or unicode"); + return pySamplebuffer_FromString(cstr); } inline const t_symbol *pySamplebuffer_AS_SYMBOL(PyObject *op) diff --git a/source/pybundle.cpp b/source/pybundle.cpp index 9bfacea..8f136ec 100644 --- a/source/pybundle.cpp +++ b/source/pybundle.cpp @@ -1,7 +1,7 @@ /* py/pyext - python script object for PD and Max/MSP -Copyright (c)2002-2015 Thomas Grill (gr@grrrr.org) +Copyright (c)2002-2019 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. */ @@ -56,7 +56,13 @@ static PyObject *bundle_send(PyObject *obj) static PyObject *bundle_repr(PyObject *self) { FLEXT_ASSERT(pyBundle_Check(self)); - return (PyObject *)PyString_FromFormat("",pyBundle_AS_BUNDLE(self)); + return (PyObject *) +#if PY_MAJOR_VERSION < 3 + PyString_FromFormat +#else + PyUnicode_FromFormat +#endif + ("", pyBundle_AS_BUNDLE(self)); } static PyObject *bundle_str(PyObject *self) @@ -103,12 +109,22 @@ static PyObject *bundle_append(PyObject *self,PyObject *args) if(sz > 2 && (tg = PyTuple_GET_ITEM(args,0)) != NULL && PyInstance_Check(tg) && - (outl = PyTuple_GET_ITEM(args,1)) != NULL && PyInt_Check(outl) + (outl = PyTuple_GET_ITEM(args,1)) != NULL && +#if PY_MAJOR_VERSION < 3 + PyInt_Check(outl) +#else + PyLong_Check(outl) +#endif ) { // Sending to outlet ext = pyext::GetThis(tg); + +#if PY_MAJOR_VERSION < 3 o = PyInt_AS_LONG(outl); - +#else + o = PyLong_AS_LONG(outl); +#endif + if(o < 1 || o > ext->Outlets()) { PyErr_SetString(PyExc_ValueError,"Outlet index out of range"); return NULL; diff --git a/source/pyext.cpp b/source/pyext.cpp index 152e126..fd947ee 100644 --- a/source/pyext.cpp +++ b/source/pyext.cpp @@ -1,7 +1,7 @@ /* py/pyext - python script object for PD and Max/MSP -Copyright (c)2002-2015 Thomas Grill (gr@grrrr.org) +Copyright (c)2002-2019 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. */ @@ -48,7 +48,12 @@ void pyext::Setup(t_classid c) // register/initialize pyext base class along with module class_dict = PyDict_New(); - PyObject *className = PyString_FromString(PYEXT_CLASS); + PyObject *className; +#if PY_MAJOR_VERSION < 3 + className = PyString_FromString(PYEXT_CLASS); +#else + className = PyUnicode_FromString(PYEXT_CLASS); +#endif PyMethodDef *def; // add setattr/getattr to class @@ -78,7 +83,13 @@ void pyext::Setup(t_classid c) // after merge so that it's not in class_dict as well... PyDict_SetItemString(module_dict, PYEXT_CLASS,class_obj); // increases class_obj ref count by 1 - PyDict_SetItemString(class_dict,"__doc__",PyString_FromString(pyext_doc)); + PyObject *str; +#if PY_MAJOR_VERSION < 3 + str = PyString_FromString(pyext_doc); +#else + str = PyUnicode_FromString(pyext_doc); +#endif + PyDict_SetItemString(class_dict, "__doc__", str); } pyext *pyext::GetThis(PyObject *self) @@ -327,12 +338,24 @@ bool pyext::InitInOut(int &inl,int &outl) { if(inl >= 0) { // set number of inlets - int ret = PyObject_SetAttrString(pyobj,"_inlets",PyInt_FromLong(inl)); + PyObject *obj; +#if PY_MAJOR_VERSION < 3 + obj = PyInt_FromLong(inl); +#else + obj = PyLong_FromLong(inl); +#endif + int ret = PyObject_SetAttrString(pyobj, "_inlets", obj); FLEXT_ASSERT(!ret); } if(outl >= 0) { // set number of outlets - int ret = PyObject_SetAttrString(pyobj,"_outlets",PyInt_FromLong(outl)); + PyObject *obj; +#if PY_MAJOR_VERSION < 3 + obj = PyInt_FromLong(outl); +#else + obj = PyLong_FromLong(outl); +#endif + int ret = PyObject_SetAttrString(pyobj, "_outlets", obj); FLEXT_ASSERT(!ret); } @@ -350,8 +373,15 @@ bool pyext::InitInOut(int &inl,int &outl) Py_DECREF(res); res = fres; } +#if PY_MAJOR_VERSION < 3 if(PyInt_Check(res)) inl = PyInt_AS_LONG(res); + else +#endif + if(PyLong_Check(res)) + inl = PyLong_AS_LONG(res); + else + PyErr_SetString(PyExc_TypeError, "Type must be integer"); Py_DECREF(res); } else @@ -367,8 +397,16 @@ bool pyext::InitInOut(int &inl,int &outl) Py_DECREF(res); res = fres; } +#if PY_MAJOR_VERSION < 3 if(PyInt_Check(res)) outl = PyInt_AS_LONG(res); + else +#endif + if(PyLong_Check(res)) + outl = PyLong_AS_LONG(res); + else + PyErr_SetString(PyExc_TypeError, "Type must be integer"); + Py_DECREF(res); } else diff --git a/source/pysymbol.cpp b/source/pysymbol.cpp index 50ee9da..743207f 100644 --- a/source/pysymbol.cpp +++ b/source/pysymbol.cpp @@ -1,7 +1,7 @@ /* py/pyext - python script object for PD and Max/MSP -Copyright (c)2002-2015 Thomas Grill (gr@grrrr.org) +Copyright (c)2002-2019 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. */ @@ -32,10 +32,14 @@ static int symbol_init(PyObject *self, PyObject *args, PyObject *kwds) if(pySymbol_Check(arg)) ((pySymbol *)self)->sym = pySymbol_AS_SYMBOL(arg); +#if PY_MAJOR_VERSION < 3 else if(PyString_Check(arg)) ((pySymbol *)self)->sym = flext::MakeSymbol(PyString_AS_STRING(arg)); +#endif + else if(PyUnicode_Check(arg)) + ((pySymbol *)self)->sym = flext::MakeSymbol(PyUnicode_AsUTF8(arg)); else { - PyErr_SetString(PyExc_TypeError,"string or symbol argument expected"); + PyErr_SetString(PyExc_TypeError, "string, unicode or symbol argument expected"); ret = -1; } Py_DECREF(arg); @@ -46,13 +50,25 @@ static int symbol_init(PyObject *self, PyObject *args, PyObject *kwds) static PyObject *symbol_str(PyObject *self) { FLEXT_ASSERT(pySymbol_Check(self)); - return (PyObject *)PyString_FromString(pySymbol_AS_STRING(self)); + return (PyObject *) +#if PY_MAJOR_VERSION < 3 + PyString_FromString +#else + PyUnicode_FromString +#endif + (pySymbol_AS_STRING(self)); } static PyObject *symbol_repr(PyObject *self) { FLEXT_ASSERT(pySymbol_Check(self)); - return (PyObject *)PyString_FromFormat("",pySymbol_AS_STRING(self)); + return (PyObject *) +#if PY_MAJOR_VERSION < 3 + PyString_FromFormat +#else + PyUnicode_FromFormat +#endif + ("", pySymbol_AS_STRING(self)); } static PyObject *symbol_richcompare(PyObject *a,PyObject *b,int cmp) @@ -101,7 +117,13 @@ static PyObject *symbol_item(PyObject *s,Py_ssize_t i) if(i < 0) i += len; if(i >= 0 && i < len) - return PyString_FromStringAndSize(str+i,1); + return +#if PY_MAJOR_VERSION < 3 + PyString_FromStringAndSize +#else + PyUnicode_FromStringAndSize +#endif + (str+i,1); else { Py_INCREF(Py_None); return Py_None; @@ -120,7 +142,13 @@ static PyObject *symbol_slice(PyObject *s,Py_ssize_t ilow = 0,Py_ssize_t ihigh = if(ihigh < 0) ihigh += len; if(ihigh >= len) ihigh = len-1; - return PyString_FromStringAndSize(str+ilow,ilow <= ihigh?ihigh-ilow+1:0); + return +#if PY_MAJOR_VERSION < 3 + PyString_FromStringAndSize +#else + PyUnicode_FromStringAndSize +#endif + (str+ilow, ilow <= ihigh?ihigh-ilow+1:0); } static PyObject *symbol_concat(PyObject *s,PyObject *op) diff --git a/source/pysymbol.h b/source/pysymbol.h index 0febfde..f033c46 100644 --- a/source/pysymbol.h +++ b/source/pysymbol.h @@ -1,7 +1,7 @@ /* py/pyext - python script object for PD and Max/MSP -Copyright (c)2002-2015 Thomas Grill (gr@grrrr.org) +Copyright (c)2002-2019 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. */ @@ -61,7 +61,18 @@ inline PyObject *pySymbol_FromString(const char *str) inline PyObject *pySymbol_FromString(PyObject *str) { - return pySymbol_FromString(PyString_AsString(str)); + const char *cstr; +#if PY_MAJOR_VERSION < 3 + if(PyString_Check(str)) + cstr = PyString_AsString(str); + else +#endif + if(PyUnicode_Check(str)) + cstr = PyUnicode_AsUTF8(str); + else + PyErr_SetString(PyExc_TypeError, "Type must be string or unicode"); + + return pySymbol_FromString(cstr); } inline const t_symbol *pySymbol_AS_SYMBOL(PyObject *op) @@ -81,9 +92,14 @@ inline const char *pySymbol_AS_STRING(PyObject *op) inline const t_symbol *pyObject_AsSymbol(PyObject *op) { +#if PY_MAJOR_VERSION < 3 if(PyString_Check(op)) return flext::MakeSymbol(PyString_AS_STRING(op)); else +#endif + if(PyUnicode_Check(op)) + return flext::MakeSymbol(PyUnicode_AsUTF8(op)); + else return pySymbol_AsSymbol(op); } From 260c441df16302641b15942214b807fb024d1417 Mon Sep 17 00:00:00 2001 From: Thomas Grill Date: Thu, 20 Jun 2019 23:51:33 +0200 Subject: [PATCH 03/40] Buffer fixed --- source/pybuffer.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++--- source/pyext.cpp | 13 +++++++--- 2 files changed, 66 insertions(+), 7 deletions(-) diff --git a/source/pybuffer.cpp b/source/pybuffer.cpp index 212975d..ab3bcfb 100644 --- a/source/pybuffer.cpp +++ b/source/pybuffer.cpp @@ -527,6 +527,7 @@ static PyObject *buffer_multiply(PyObject *s,PyObject *op) return NULL; } +#if PY_MAJOR_VERSION < 3 static PyObject *buffer_divide(PyObject *s,PyObject *op) { pySamplebuffer *self = reinterpret_cast(s); @@ -540,6 +541,35 @@ static PyObject *buffer_divide(PyObject *s,PyObject *op) else return NULL; } +#endif + +static PyObject *buffer_true_divide(PyObject *s,PyObject *op) +{ + pySamplebuffer *self = reinterpret_cast(s); + PyObject *nobj = buffer_slice(s); + if(nobj) { + PyObject *ret = PyNumber_TrueDivide(nobj, op); + if(ret == nobj) self->dirty = true; + Py_DECREF(nobj); + return ret; + } + else + return NULL; +} + +static PyObject *buffer_floor_divide(PyObject *s,PyObject *op) +{ + pySamplebuffer *self = reinterpret_cast(s); + PyObject *nobj = buffer_slice(s); + if(nobj) { + PyObject *ret = PyNumber_FloorDivide(nobj, op); + if(ret == nobj) self->dirty = true; + Py_DECREF(nobj); + return ret; + } + else + return NULL; +} static PyObject *buffer_remainder(PyObject *s,PyObject *op) { @@ -677,6 +707,7 @@ static PyObject *buffer_inplace_multiply(PyObject *s,PyObject *op) return NULL; } +#if PY_MAJOR_VERSION < 3 static PyObject *buffer_inplace_divide(PyObject *s,PyObject *op) { pySamplebuffer *self = reinterpret_cast(s); @@ -690,6 +721,21 @@ static PyObject *buffer_inplace_divide(PyObject *s,PyObject *op) else return NULL; } +#endif + +static PyObject *buffer_inplace_floor_divide(PyObject *s,PyObject *op) +{ + pySamplebuffer *self = reinterpret_cast(s); + PyObject *nobj = buffer_slice(s); + if(nobj) { + PyObject *ret = PyNumber_InPlaceFloorDivide(nobj,op); + if(ret == nobj) self->dirty = true; + Py_DECREF(nobj); + return ret; + } + else + return NULL; +} static PyObject *buffer_inplace_remainder(PyObject *s,PyObject *op) { @@ -725,7 +771,11 @@ static PyNumberMethods buffer_as_number = { (binaryfunc)buffer_add, /*nb_add*/ (binaryfunc)buffer_subtract, /*nb_subtract*/ (binaryfunc)buffer_multiply, /*nb_multiply*/ +#if PY_MAJOR_VERSION < 3 (binaryfunc)buffer_divide, /*nb_divide*/ +#else + 0, /*nb_divide not supported */ +#endif (binaryfunc)buffer_remainder, /*nb_remainder*/ (binaryfunc)buffer_divmod, /*nb_divmod*/ (ternaryfunc)buffer_power, /*nb_power*/ @@ -748,7 +798,11 @@ static PyNumberMethods buffer_as_number = { (binaryfunc)buffer_inplace_add, /* nb_inplace_add */ (binaryfunc)buffer_inplace_subtract, /* nb_inplace_subtract */ (binaryfunc)buffer_inplace_multiply, /* nb_inplace_multiply */ +#if PY_MAJOR_VERSION < 3 (binaryfunc)buffer_inplace_divide, /* nb_inplace_divide */ +#else + 0, /* nb_inplace_divide not supported */ +#endif (binaryfunc)buffer_inplace_remainder, /* nb_inplace_remainder */ (ternaryfunc)buffer_inplace_power, /* nb_inplace_power */ 0, /* nb_inplace_lshift */ @@ -756,9 +810,9 @@ static PyNumberMethods buffer_as_number = { 0, /* nb_inplace_and */ 0, /* nb_inplace_xor */ 0, /* nb_inplace_or */ -// buffer_floor_div, /* nb_floor_divide */ -// buffer_div, /* nb_true_divide */ -// buffer_inplace_floor_div, /* nb_inplace_floor_divide */ + (binaryfunc)buffer_floor_divide, /* nb_floor_divide */ + (binaryfunc)buffer_true_divide, /* nb_true_divide */ + (binaryfunc)buffer_inplace_floor_divide, /* nb_inplace_floor_divide */ // buffer_inplace_div, /* nb_inplace_true_divide */ }; diff --git a/source/pyext.cpp b/source/pyext.cpp index fd947ee..0deb2e9 100644 --- a/source/pyext.cpp +++ b/source/pyext.cpp @@ -421,14 +421,19 @@ bool pyext::MakeInstance() // pyobj should already have been decref'd / cleared before getting here!! if(module && methname) { - PyObject *pref = PyObject_GetAttrString(module,const_cast(GetString(methname))); + PyObject *pref = PyObject_GetAttrString(module, const_cast(GetString(methname))); if(!pref) PyErr_Print(); else { +#if PY_MAJOR_VERSION < 3 if(PyClass_Check(pref)) { // make instance, but don't call __init__ - pyobj = PyInstance_NewRaw(pref,NULL); - + pyobj = PyInstance_NewRaw(pref, NULL); +#else + if(PyObject_IsInstance(pref, (PyObject *)&PyType_Type)) { + // pyobj = PyBaseObject_Type.tp_new() + // TODO: correctly initialize instance +#endif if(!pyobj) PyErr_Print(); } else @@ -537,7 +542,7 @@ bool pyext::CbMethodResort(int n,const t_symbol *s,int argc,const t_atom *argv) void pyext::m_help() { post(""); - post("%s %s - python class object, (C)2002-2012 Thomas Grill",thisName(),PY__VERSION); + post("%s %s - python class object, (C)2002-2019 Thomas Grill",thisName(),PY__VERSION); #ifdef FLEXT_DEBUG post("DEBUG VERSION, compiled on " __DATE__ " " __TIME__); #endif From 7764061037fc91b7642bbedae1e5ce48c68709f1 Mon Sep 17 00:00:00 2001 From: Miranda Kastemaa Date: Wed, 7 Aug 2019 17:32:49 +0300 Subject: [PATCH 04/40] support conda python --- build/config-mac.def | 13 +++++---- build/gnumake-mac-gcc.inc | 57 ++++++++++++++++++++++++++++----------- 2 files changed, 49 insertions(+), 21 deletions(-) diff --git a/build/config-mac.def b/build/config-mac.def index 0a59aa9..9fbc4a2 100644 --- a/build/config-mac.def +++ b/build/config-mac.def @@ -1,6 +1,9 @@ -# set PY_DEFAULT=0 if you manually installed Python on your system -# (overriding the one that came with OS X by default) -PY_DEFAULT=1 +# which kind of Python installation to use +# system - macOS system default +# local - local installation +# conda - conda environment; specify the environment root dir using the +# PY_CONDA_ROOT environment variable +PY_KIND=conda ######################################################################### @@ -11,7 +14,7 @@ PY_DEFAULT=1 # Mac OSX 10.6 -> default Python version (major.minor) = 2.6 PY_MAJOR_VERSION=2 -PY_MINOR_VERSION=6 +PY_MINOR_VERSION=7 ######################################################################### @@ -19,7 +22,7 @@ PY_MINOR_VERSION=6 # for info see http://numeric.scipy.org # numarray and numeric are outdated -# PY_NUMPY=1 +PY_NUMPY=1 # PY_NUMARRAY=1 # PY_NUMERIC=1 diff --git a/build/gnumake-mac-gcc.inc b/build/gnumake-mac-gcc.inc index 8806024..34495a4 100644 --- a/build/gnumake-mac-gcc.inc +++ b/build/gnumake-mac-gcc.inc @@ -1,3 +1,41 @@ +DEFS += -DPY_EXPORTS + +ifdef PY_NUMPY +DEFS += -DPY_NUMPY +endif + +ifdef PY_NUMARRAY +DEFS += -DPY_NUMARRAY +endif + +ifdef PY_NUMERIC +DEFS += -DPY_NUMERIC +endif + +ifdef PY_USE_GIL +DEFS += -DPY_USE_GIL +endif + +ifdef PY_USE_INOFFICIAL +DEFS += -DPY_USE_INOFFICIAL +endif + +ifeq ($(PY_KIND),conda) + +ifndef PY_CONDA_ROOT +$(error PY_CONDA_ROOT is undefined) +endif + +LIBS += $(PY_CONDA_ROOT)/lib/libpython$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION).dylib +INCPATH += -I$(PY_CONDA_ROOT)/include +INCPATH += -I$(PY_CONDA_ROOT)/include/python$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION) + +ifdef PY_NUMPY +INCPATH += -I$(PY_CONDA_ROOT)/lib/python$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION)/site-packages/numpy/core/include +endif + +else + # don't use -framework Python, since this will stick to the default system version _LOCAL_FRAMEWORK := /Library/Frameworks/Python.framework/Versions/$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION) @@ -5,10 +43,9 @@ _SYSTEM_FRAMEWORK := /System/Library/Frameworks/Python.framework/Versions/$(PY_M _LOCAL_LIBRARY := /Library/Python/$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION) _SYSTEM_LIBRARY := /System/Library/Python/$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION) -DEFS += -DPY_EXPORTS INCPATH += -F/Library/Frameworks -framework Python -ifeq ($(PY_DEFAULT),1) +ifeq ($(PY_KIND),system) LIBS += $(_SYSTEM_FRAMEWORK)/Python INCPATH += -I$(_SYSTEM_FRAMEWORK)/Headers else @@ -16,27 +53,15 @@ LIBS += $(_LOCAL_FRAMEWORK)/Python INCPATH += -I$(_LOCAL_FRAMEWORK)/Headers endif -ifdef PY_NUMARRAY -DEFS += -DPY_NUMARRAY -endif ifdef PY_NUMPY -DEFS += -DPY_NUMPY INCPATH += -I$(_LOCAL_LIBRARY)/python$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION)/site-packages/numpy/core/include -ifeq ($(PY_DEFAULT),1) + +ifeq ($(PY_KIND),system) INCPATH += -I$(_SYSTEM_FRAMEWORK)/lib/python$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION)/site-packages/numpy/core/include INCPATH += -I$(_SYSTEM_FRAMEWORK)/Extras/lib/python/numpy/core/include else INCPATH += -I$(_LOCAL_FRAMEWORK)/lib/python$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION)/site-packages/numpy/core/include endif endif -ifdef PY_NUMERIC -DEFS += -DPY_NUMERIC -endif -ifdef PY_USE_GIL -DEFS += -DPY_USE_GIL -endif - -ifdef PY_USE_INOFFICIAL -DEFS += -DPY_USE_INOFFICIAL endif From 21e3b3271f7c6a3e4f65660512b04aa2216a3dbc Mon Sep 17 00:00:00 2001 From: Miranda Kastemaa Date: Thu, 8 Aug 2019 14:55:42 +0300 Subject: [PATCH 05/40] add conda lib dir to rpath --- build/gnumake-mac-gcc.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/build/gnumake-mac-gcc.inc b/build/gnumake-mac-gcc.inc index 34495a4..d93ae5f 100644 --- a/build/gnumake-mac-gcc.inc +++ b/build/gnumake-mac-gcc.inc @@ -29,6 +29,7 @@ endif LIBS += $(PY_CONDA_ROOT)/lib/libpython$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION).dylib INCPATH += -I$(PY_CONDA_ROOT)/include INCPATH += -I$(PY_CONDA_ROOT)/include/python$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION) +LDFLAGS += -rpath $(PY_CONDA_ROOT)/lib ifdef PY_NUMPY INCPATH += -I$(PY_CONDA_ROOT)/lib/python$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION)/site-packages/numpy/core/include From ac328cdcd85b9fb5ed93ca5bb7427702ca90dc94 Mon Sep 17 00:00:00 2001 From: Miranda Kastemaa Date: Wed, 14 Aug 2019 17:15:32 +0300 Subject: [PATCH 06/40] add dummy StdOut_Flush() method to make absl logging happy --- source/pybase.cpp | 7 +++++++ source/pybase.h | 1 + 2 files changed, 8 insertions(+) diff --git a/source/pybase.cpp b/source/pybase.cpp index 826e6a4..cb3ea97 100644 --- a/source/pybase.cpp +++ b/source/pybase.cpp @@ -18,6 +18,7 @@ WARRANTIES, see the file, "license.txt," in this distribution. static PyMethodDef StdOut_Methods[] = { { "write", pybase::StdOut_Write, 1 }, + { "flush", pybase::StdOut_Flush, 1 }, { NULL, NULL, } }; @@ -776,6 +777,12 @@ PyObject* pybase::StdOut_Write(PyObject* self, PyObject* args) return Py_None; } +// dummy flush method, since some logging libraries call this +PyObject* pybase::StdOut_Flush(PyObject* self, PyObject* args) +{ + Py_INCREF(Py_None); + return Py_None; +} class work_data { diff --git a/source/pybase.h b/source/pybase.h index 8a3105e..cfda6bf 100644 --- a/source/pybase.h +++ b/source/pybase.h @@ -269,6 +269,7 @@ class pybase }; static PyObject* StdOut_Write(PyObject* Self, PyObject* Args); + static PyObject* StdOut_Flush(PyObject* Self, PyObject* Args); }; #endif From 1f7e520e4147e1c9cbfe480f669b563f4d0a566a Mon Sep 17 00:00:00 2001 From: Miranda Kastemaa Date: Fri, 16 Aug 2019 11:03:20 +0300 Subject: [PATCH 07/40] call Py_SetProgramName() when building for conda env --- build/gnumake-mac-gcc.inc | 1 + source/pybase.cpp | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/build/gnumake-mac-gcc.inc b/build/gnumake-mac-gcc.inc index d93ae5f..bc2ec27 100644 --- a/build/gnumake-mac-gcc.inc +++ b/build/gnumake-mac-gcc.inc @@ -26,6 +26,7 @@ ifndef PY_CONDA_ROOT $(error PY_CONDA_ROOT is undefined) endif +DEFS += -DPY_INTERPRETER=$(PY_CONDA_ROOT)/bin/python LIBS += $(PY_CONDA_ROOT)/lib/libpython$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION).dylib INCPATH += -I$(PY_CONDA_ROOT)/include INCPATH += -I$(PY_CONDA_ROOT)/include/python$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION) diff --git a/source/pybase.cpp b/source/pybase.cpp index cb3ea97..6694d3e 100644 --- a/source/pybase.cpp +++ b/source/pybase.cpp @@ -15,6 +15,9 @@ WARRANTIES, see the file, "license.txt," in this distribution. #include #endif +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) + static PyMethodDef StdOut_Methods[] = { { "write", pybase::StdOut_Write, 1 }, @@ -99,8 +102,17 @@ void initsymbol(); void initsamplebuffer(); void initbundle(); + + void pybase::lib_setup() -{ +{ +#ifdef PY_INTERPRETER + { + static char py_program_name[] = TOSTRING(PY_INTERPRETER); + Py_SetProgramName(py_program_name); + } +#endif + post(""); post("------------------------------------------------"); post("py/pyext %s - python script objects",PY__VERSION); From 8308d8af67fc53a02427dcac9e7209eae5962b7e Mon Sep 17 00:00:00 2001 From: Miranda Kastemaa Date: Tue, 27 Aug 2019 16:52:00 +0300 Subject: [PATCH 08/40] don't include when using conda --- build/gnumake-mac-gcc.inc | 2 ++ source/pybuffer.h | 2 +- source/pybundle.h | 2 +- source/pyprefix.h | 2 +- source/pysymbol.h | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/build/gnumake-mac-gcc.inc b/build/gnumake-mac-gcc.inc index bc2ec27..1ebf47a 100644 --- a/build/gnumake-mac-gcc.inc +++ b/build/gnumake-mac-gcc.inc @@ -38,6 +38,8 @@ endif else +DEFS += -DPY_USE_FRAMEWORK + # don't use -framework Python, since this will stick to the default system version _LOCAL_FRAMEWORK := /Library/Frameworks/Python.framework/Versions/$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION) diff --git a/source/pybuffer.h b/source/pybuffer.h index 5869d13..d7ad071 100644 --- a/source/pybuffer.h +++ b/source/pybuffer.h @@ -15,7 +15,7 @@ WARRANTIES, see the file, "license.txt," in this distribution. #error You need at least flext version 0.5.0 #endif -#if FLEXT_OS == FLEXT_OS_MAC +#ifdef PY_USE_FRAMEWORK #include #else #include diff --git a/source/pybundle.h b/source/pybundle.h index b397ffb..f862235 100644 --- a/source/pybundle.h +++ b/source/pybundle.h @@ -15,7 +15,7 @@ WARRANTIES, see the file, "license.txt," in this distribution. #error You need at least flext version 0.5.0 #endif -#if FLEXT_OS == FLEXT_OS_MAC +#ifdef PY_USE_FRAMEWORK #include #else #include diff --git a/source/pyprefix.h b/source/pyprefix.h index 8b242a4..e9a624c 100644 --- a/source/pyprefix.h +++ b/source/pyprefix.h @@ -16,7 +16,7 @@ WARRANTIES, see the file, "license.txt," in this distribution. // otherwise some functions don't get defined #include -#if FLEXT_OS == FLEXT_OS_MAC +#ifdef PY_USE_FRAMEWORK #include #else #include diff --git a/source/pysymbol.h b/source/pysymbol.h index 0febfde..0b66138 100644 --- a/source/pysymbol.h +++ b/source/pysymbol.h @@ -15,7 +15,7 @@ WARRANTIES, see the file, "license.txt," in this distribution. #error You need at least flext version 0.5.0 #endif -#if FLEXT_OS == FLEXT_OS_MAC +#ifdef PY_USE_FRAMEWORK #include #else #include From 0c800aa49caa840650791073e7183b85859afa6d Mon Sep 17 00:00:00 2001 From: Miranda Kastemaa Date: Tue, 27 Aug 2019 16:57:00 +0300 Subject: [PATCH 09/40] more conda fixing --- source/pybuffer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/pybuffer.cpp b/source/pybuffer.cpp index 4690381..81bb456 100644 --- a/source/pybuffer.cpp +++ b/source/pybuffer.cpp @@ -18,7 +18,7 @@ WARRANTIES, see the file, "license.txt," in this distribution. #ifdef PY_ARRAYS #ifdef PY_NUMARRAY -# if FLEXT_OS == FLEXT_OS_MAC +# ifdef PY_USE_FRAMEWORK # include # else # include @@ -31,7 +31,7 @@ inline bool arrsupport() { return numtype != tAny; } # if defined(PY_NUMPY) # include # else -# if FLEXT_OS == FLEXT_OS_MAC +# ifdef PY_USE_FRAMEWORK # include # else # include @@ -60,7 +60,7 @@ PyObject *pybase::py_arraysupport(PyObject *self,PyObject *args) // PD defines a T_OBJECT symbol #undef T_OBJECT -#if FLEXT_OS == FLEXT_OS_MAC +#ifdef PY_USE_FRAMEWORK #include "Python/bufferobject.h" #include "Python/structmember.h" #else From c0b374ee1e672ef2c14039bdc62cad5f64a95e1b Mon Sep 17 00:00:00 2001 From: miranda kastemaa Date: Tue, 10 Mar 2020 13:23:07 +0200 Subject: [PATCH 10/40] python 3 compatibility fixes --- source/bound.cpp | 4 +- source/clmeth.cpp | 9 +- source/pybase.cpp | 79 +++++++++++++++- source/pybuffer.cpp | 217 +++++++++++++++++++++++++------------------- source/pybundle.cpp | 5 +- source/pydsp.cpp | 20 +++- source/pyext.cpp | 7 +- source/pyext.h | 2 +- source/pymeth.cpp | 4 + source/pysymbol.cpp | 76 ++++++++-------- 10 files changed, 276 insertions(+), 147 deletions(-) diff --git a/source/bound.cpp b/source/bound.cpp index 419760e..01cf681 100644 --- a/source/bound.cpp +++ b/source/bound.cpp @@ -86,7 +86,7 @@ PyObject *pyext::pyext_bind(PyObject *,PyObject *args) 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) || !PyCallable_Check(meth)) { + else if(!PyCallable_Check(meth)) { post("py/pyext - Wrong argument types!"); } else { @@ -131,7 +131,7 @@ PyObject *pyext::pyext_unbind(PyObject *,PyObject *args) 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) || !PyCallable_Check(meth)) { + else if(!PyCallable_Check(meth)) { post("py/pyext - Wrong argument types!"); } else { diff --git a/source/clmeth.cpp b/source/clmeth.cpp index a7e6d8d..340dcc4 100644 --- a/source/clmeth.cpp +++ b/source/clmeth.cpp @@ -114,10 +114,7 @@ PyObject* pyext::pyext_setattr(PyObject *,PyObject *args) } */ if(!handled) { - if(PyInstance_Check(self)) - PyDict_SetItem(((PyInstanceObject *)self)->in_dict, name,val); - else - ERRINTERNAL(); + PyDict_SetItem(self, name, val); } Py_INCREF(Py_None); @@ -192,7 +189,7 @@ PyObject *pyext::pyext_outlet(PyObject *,PyObject *args) if( sz >= 2 && - (self = PyTuple_GET_ITEM(args,0)) != NULL && PyInstance_Check(self) && + (self = PyTuple_GET_ITEM(args,0)) != NULL && (outl = PyTuple_GET_ITEM(args,1)) != NULL && #if PY_MAJOR_VERSION < 3 PyInt_Check(outl) @@ -333,7 +330,7 @@ PyObject *pyext::pyext_tocanvas(PyObject *,PyObject *args) PyObject *self; // borrowed ref if( sz >= 1 && - (self = PyTuple_GET_ITEM(args,0)) != NULL && PyInstance_Check(self) + (self = PyTuple_GET_ITEM(args,0)) != NULL ) { pyext *ext = GetThis(self); if(!ext) { diff --git a/source/pybase.cpp b/source/pybase.cpp index 9d887fa..41fa70a 100644 --- a/source/pybase.cpp +++ b/source/pybase.cpp @@ -17,6 +17,8 @@ WARRANTIES, see the file, "license.txt," in this distribution. #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) +#define WSTRINGIFY(x) L ## #x +#define TOWSTRING(x) WSTRINGIFY(x) static PyMethodDef StdOut_Methods[] = { @@ -108,7 +110,11 @@ void pybase::lib_setup() { #ifdef PY_INTERPRETER { +#if PY_MAJOR_VERSION < 3 static char py_program_name[] = TOSTRING(PY_INTERPRETER); +#else + static wchar_t py_program_name[] = TOWSTRING(PY_INTERPRETER); +#endif Py_SetProgramName(py_program_name); } #endif @@ -163,20 +169,70 @@ void pybase::lib_setup() #endif // sys.argv must be set to empty tuple +#if PY_MAJOR_VERSION < 3 const char *nothing = ""; PySys_SetArgv(0,const_cast(¬hing)); +#else + const wchar_t *nothing = L""; + PySys_SetArgv(0,const_cast(¬hing)); +#endif // register/initialize pyext module only once! +#if PY_MAJOR_VERSION < 3 module_obj = Py_InitModule(const_cast(PYEXT_MODULE), func_tbl); +#else + struct PyModuleDef pyext_module_def = { + PyModuleDef_HEAD_INIT, // PyModuleDef_Base m_base + PYEXT_MODULE, // const char *m_name + py_doc, // const char *m_doc + -1, // Py_ssize_t m_size + func_tbl, // PyMethodDef *m_methods + NULL, // PyModuleDef_Slot *m_slots + NULL, // traverseproc m_traverse + NULL, // inquiry m_clear + NULL // freefunc m_free + }; + module_obj = PyModule_Create(&pyext_module_def); +#endif module_dict = PyModule_GetDict(module_obj); // borrowed reference PyModule_AddStringConstant(module_obj,"__doc__",(char *)py_doc); // redirect stdout PyObject* py_out; +#if PY_MAJOR_VERSION < 3 py_out = Py_InitModule(const_cast("stdout"), StdOut_Methods); +#else + struct PyModuleDef stdout_module_def = { + PyModuleDef_HEAD_INIT, // PyModuleDef_Base m_base + "stdout", // const char *m_name + "", // const char *m_doc + -1, // Py_ssize_t m_size + func_tbl, // PyMethodDef *m_methods + NULL, // PyModuleDef_Slot *m_slots + NULL, // traverseproc m_traverse + NULL, // inquiry m_clear + NULL // freefunc m_free + }; + py_out = PyModule_Create(&stdout_module_def); +#endif PySys_SetObject(const_cast("stdout"), py_out); +#if PY_MAJOR_VERSION < 3 py_out = Py_InitModule(const_cast("stderr"), StdOut_Methods); +#else + struct PyModuleDef stderr_module_def = { + PyModuleDef_HEAD_INIT, // PyModuleDef_Base m_base + "stderr", // const char *m_name + "", // const char *m_doc + -1, // Py_ssize_t m_size + func_tbl, // PyMethodDef *m_methods + NULL, // PyModuleDef_Slot *m_slots + NULL, // traverseproc m_traverse + NULL, // inquiry m_clear + NULL // freefunc m_free + }; + py_out = PyModule_Create(&stdout_module_def); +#endif PySys_SetObject(const_cast("stderr"), py_out); // get garbage collector function @@ -445,13 +501,32 @@ void pybase::SetArgs() // script arguments int argc = args.Count(); const t_atom *argv = args.Atoms(); +#if PY_MAJOR_VERSION < 3 char **sargv = new char *[argc+1]; +#else + wchar_t **sargv = new wchar_t *[argc+1]; +#endif for(int i = 0; i <= argc; ++i) { +#if PY_MAJOR_VERSION < 3 sargv[i] = new char[256]; - if(!i) +#else + sargv[i] = new wchar_t[256]; +#endif + if(!i) { +#if PY_MAJOR_VERSION < 3 strcpy(sargv[i],"py/pyext"); - else +#else + wcscpy(sargv[i],L"py/pyext"); +#endif + } else { +#if PY_MAJOR_VERSION < 3 GetAString(argv[i-1],sargv[i],255); +#else + char *arg = new char[256]; + GetAString(argv[i-1],arg,255); + mbstowcs(sargv[i],arg,255); +#endif + } } // the arguments to the module are only recognized once! (at first use in a patcher) diff --git a/source/pybuffer.cpp b/source/pybuffer.cpp index b4c9222..6892d03 100644 --- a/source/pybuffer.cpp +++ b/source/pybuffer.cpp @@ -61,10 +61,8 @@ PyObject *pybase::py_arraysupport(PyObject *self,PyObject *args) #undef T_OBJECT #ifdef PY_USE_FRAMEWORK -#include "Python/bufferobject.h" #include "Python/structmember.h" #else -#include "bufferobject.h" #include "structmember.h" #endif @@ -168,7 +166,8 @@ static PyObject *buffer_dirty(PyObject *obj) static PyObject *buffer_resize(PyObject *obj,PyObject *args,PyObject *kwds) { - flext::buffer *b = ((pySamplebuffer *)obj)->buf; + pySamplebuffer *self = reinterpret_cast(obj); + flext::buffer *b = self->buf; if(b) { int frames,keep = 1,zero = 1; static char const *kwlist[] = {"frames", "keep", "zero", NULL}; @@ -192,47 +191,63 @@ static PyMethodDef buffer_methods[] = { {NULL} /* Sentinel */ }; +// support the buffer protocol +static int buffer_getbuffer(PyObject *obj, Py_buffer *view, int flags) { + if(flags & PyBUF_INDIRECT) { + PyErr_SetString(PyExc_BufferError, "PyBUF_INDIRECT not supported"); + view->obj = NULL; + return -1; + } -// support the buffer protocol + if(flags & PyBUF_STRIDES) { + PyErr_SetString(PyExc_BufferError, "PyBUF_STRIDES not supported"); + view->obj = NULL; + return -1; + } -static Py_ssize_t buffer_readbuffer(PyObject *obj, Py_ssize_t segment, void **ptrptr) -{ - flext::buffer *b = ((pySamplebuffer *)obj)->buf; - ptrptr[0] = b->Data(); - return b->Channels()*b->Frames()*sizeof(t_sample); -} + if(flags & PyBUF_ND) { + PyErr_SetString(PyExc_BufferError, "PyBUF_ND not supported"); + view->obj = NULL; + return -1; + } -static Py_ssize_t buffer_writebuffer(PyObject *obj, Py_ssize_t segment, void **ptrptr) -{ - flext::buffer *b = ((pySamplebuffer *)obj)->buf; - ptrptr[0] = b->Data(); - return b->Channels()*b->Frames()*sizeof(t_sample); -} + if(flags & PyBUF_F_CONTIGUOUS) { + PyErr_SetString(PyExc_BufferError, "PyBUF_F_CONTIGUOUS not supported"); + view->obj = NULL; + return -1; + } -static Py_ssize_t buffer_segcount(PyObject *obj, Py_ssize_t *lenp) -{ - flext::buffer *b = ((pySamplebuffer *)obj)->buf; - if(lenp) lenp[0] = b->Channels()*b->Frames()*sizeof(t_sample); - return 1; + pySamplebuffer *self = reinterpret_cast(obj); + flext::buffer *b = self->buf; + + view->buf = (void *)b->Data(); + Py_INCREF(obj); + view->obj = obj; + view->len = b->Channels()*b->Frames()*sizeof(t_sample); + view->readonly = false; + view->itemsize = 1; + view->format = NULL; + view->ndim = 1; + view->shape = NULL; + view->strides = NULL; + view->suboffsets = NULL; + view->internal = NULL; + + if(flags & PyBUF_FORMAT) { + view->format = "B"; + } + + return 0; } -static Py_ssize_t buffer_charbuffer(PyObject *obj, Py_ssize_t segment, -#if PY_VERSION_HEX < 0x02050000 - const -#endif - char **ptrptr) -{ - flext::buffer *b = ((pySamplebuffer *)obj)->buf; - ptrptr[0] = (char *)b->Data(); - return b->Channels()*b->Frames()*sizeof(t_sample); +static void buffer_releasebuffer(PyObject *obj, Py_buffer *view) { + // nothing to do here } static PyBufferProcs buffer_as_buffer = { - buffer_readbuffer, - buffer_writebuffer, - buffer_segcount, - buffer_charbuffer + buffer_getbuffer, + buffer_releasebuffer }; static Py_ssize_t buffer_length(PyObject *s) @@ -279,16 +294,15 @@ PyObject *arrayfrombuffer(PyObject *buf,int c,int n) #ifdef PY_NUMARRAY arr = (PyObject *)NA_NewAllFromBuffer(c == 1?1:2,shape,numtype,buf,0,0,NA_ByteOrder(),1,1); #else - void *data; - Py_ssize_t len; - int err = PyObject_AsWriteBuffer(buf,&data,&len); + Py_buffer view; + int err = PyObject_GetBuffer(buf, &view, PyBUF_SIMPLE | PyBUF_WRITABLE); if(!err) { - FLEXT_ASSERT(len <= n*c*sizeof(t_sample)); + FLEXT_ASSERT(view->len <= n*c*sizeof(t_sample)); // Py_INCREF(buf); // ATTENTION... this won't be released any more!! # ifdef PY_NUMPY - arr = PyArray_NewFromDescr(&PyArray_Type,PyArray_DescrNewFromType(numtype),c == 1?1:2,shape,0,(char *)data,NPY_WRITEABLE|NPY_C_CONTIGUOUS,NULL); + arr = PyArray_NewFromDescr(&PyArray_Type,PyArray_DescrNewFromType(numtype),c == 1?1:2,shape,0,(char *)view.buf,NPY_WRITEABLE|NPY_C_CONTIGUOUS,NULL); # else - arr = PyArray_FromDimsAndData(c == 1?1:2,shape,numtype,(char *)data); + arr = PyArray_FromDimsAndData(c == 1?1:2,shape,numtype,(char *)view.buf); # endif } else { @@ -462,13 +476,14 @@ static PyObject *buffer_repeat(PyObject *s,Py_ssize_t rep) static PySequenceMethods buffer_as_seq = { - buffer_length, /* inquiry sq_length; __len__ */ - buffer_concat, /* __add__ */ - buffer_repeat, /* __mul__ */ - buffer_item, /* intargfunc sq_item; __getitem__ */ - buffer_slice, /* intintargfunc sq_slice; __getslice__ */ - buffer_ass_item, /* intobjargproc sq_ass_item; __setitem__ */ - buffer_ass_slice, /* intintobjargproc sq_ass_slice; __setslice__ */ + buffer_length, /* lenfunc sq_length __len__ */ + buffer_concat, /* binaryfunc sq_concat __add__ */ + buffer_repeat, /* ssizeargfunc sq_repeat __mul__ */ + buffer_item, /* ssizeargfunc sq_item; __getitem__ */ + NULL, /* ssizeobjargproc sq_ass_item __setitem__ */ + NULL, /* objobjproc sq_contains __contains__ */ + NULL, /* binaryfunc sq_inplace_concat __iadd__ */ + NULL /* ssizeargfunc sq_inplace_repeat __imul */ }; static PyObject *buffer_iter(PyObject *s) @@ -723,6 +738,20 @@ static PyObject *buffer_inplace_divide(PyObject *s,PyObject *op) } #endif +static PyObject *buffer_inplace_true_divide(PyObject *s,PyObject *op) +{ + pySamplebuffer *self = reinterpret_cast(s); + PyObject *nobj = buffer_slice(s); + if(nobj) { + PyObject *ret = PyNumber_InPlaceTrueDivide(nobj,op); + if(ret == nobj) self->dirty = true; + Py_DECREF(nobj); + return ret; + } + else + return NULL; +} + static PyObject *buffer_inplace_floor_divide(PyObject *s,PyObject *op) { pySamplebuffer *self = reinterpret_cast(s); @@ -768,57 +797,59 @@ static PyObject *buffer_inplace_power(PyObject *s,PyObject *op1,PyObject *op2) static PyNumberMethods buffer_as_number = { - (binaryfunc)buffer_add, /*nb_add*/ - (binaryfunc)buffer_subtract, /*nb_subtract*/ - (binaryfunc)buffer_multiply, /*nb_multiply*/ + (binaryfunc)buffer_add, // binaryfunc nb_add + (binaryfunc)buffer_subtract, // binaryfunc nb_subtract + (binaryfunc)buffer_multiply, // binaryfunc nb_multiply #if PY_MAJOR_VERSION < 3 - (binaryfunc)buffer_divide, /*nb_divide*/ -#else - 0, /*nb_divide not supported */ + (binaryfunc)buffer_divide, // binaryfunc nb_divide +#endif + (binaryfunc)buffer_remainder, // nb_binaryfunc remainder + (binaryfunc)buffer_divmod, // binaryfunc nb_divmod + (ternaryfunc)buffer_power, // ternaryfunc nb_power + (unaryfunc)buffer_negative, // unaryfunc nb_negative + (unaryfunc)buffer_pos, // unaryfunc nb_pos + (unaryfunc)buffer_absolute, // unaryfunc np_absolute + 0, //(inquiry)buffer_nonzero, // inquiry nb_nonzero + 0, // unaryfunc nb_invert + 0, // binaryfunc nb_lshift + 0, // binaryfunc nb_rshift + 0, // binaryfunc nb_and + 0, // binaryfunc nb_xor + 0, // binaryfunc nb_or +#if PY_MAJOR_VERSION < 3 + (coercion)buffer_coerce, // coercion nb_coerce #endif - (binaryfunc)buffer_remainder, /*nb_remainder*/ - (binaryfunc)buffer_divmod, /*nb_divmod*/ - (ternaryfunc)buffer_power, /*nb_power*/ - (unaryfunc)buffer_negative, - (unaryfunc)buffer_pos, /*nb_pos*/ - (unaryfunc)buffer_absolute, /* (unaryfunc)buffer_abs, */ - 0, //(inquiry)buffer_nonzero, /*nb_nonzero*/ - 0, /*nb_invert*/ - 0, /*nb_lshift*/ - 0, /*nb_rshift*/ - 0, /*nb_and*/ - 0, /*nb_xor*/ - 0, /*nb_or*/ - (coercion)buffer_coerce, /*nb_coerce*/ - 0, /*nb_int*/ - 0, /*nb_long*/ - 0, /*nb_float*/ - 0, /*nb_oct*/ - 0, /*nb_hex*/ - (binaryfunc)buffer_inplace_add, /* nb_inplace_add */ - (binaryfunc)buffer_inplace_subtract, /* nb_inplace_subtract */ - (binaryfunc)buffer_inplace_multiply, /* nb_inplace_multiply */ + 0, // unaryfunc nb_int #if PY_MAJOR_VERSION < 3 - (binaryfunc)buffer_inplace_divide, /* nb_inplace_divide */ + 0, // unaryfunc nb_long + 0, // unaryfunc nb_float + 0, // unaryfunc nb_oct + 0, // unaryfunc nb_hex #else - 0, /* nb_inplace_divide not supported */ + 0, // void *nb_reserved + 0, // unaryfunc nb_float #endif - (binaryfunc)buffer_inplace_remainder, /* nb_inplace_remainder */ - (ternaryfunc)buffer_inplace_power, /* nb_inplace_power */ - 0, /* nb_inplace_lshift */ - 0, /* nb_inplace_rshift */ - 0, /* nb_inplace_and */ - 0, /* nb_inplace_xor */ - 0, /* nb_inplace_or */ - (binaryfunc)buffer_floor_divide, /* nb_floor_divide */ - (binaryfunc)buffer_true_divide, /* nb_true_divide */ - (binaryfunc)buffer_inplace_floor_divide, /* nb_inplace_floor_divide */ -// buffer_inplace_div, /* nb_inplace_true_divide */ + (binaryfunc)buffer_inplace_add, // binaryfunc nb_inplace_add + (binaryfunc)buffer_inplace_subtract, // binaryfunc nb_inplace_subtract + (binaryfunc)buffer_inplace_multiply, // binaryfunc nb_inplace_multiply +#if PY_MAJOR_VERSION < 3 + (binaryfunc)buffer_inplace_divide, // binaryfunc nb_inplace_divide +#endif + (binaryfunc)buffer_inplace_remainder, // binaryfunc nb_inplace_remainder + (ternaryfunc)buffer_inplace_power, // ternaryfunc nb_inplace_power + 0, // binaryfunc nb_inplace_lshift + 0, // binaryfunc nb_inplace_rshift + 0, // binaryfunc nb_inplace_and + 0, // binaryfunc nb_inplace_xor + 0, // binaryfunc nb_inplace_or + (binaryfunc)buffer_floor_divide, // binaryfunc nb_floor_divide + (binaryfunc)buffer_true_divide, // binaryfunc nb_true_divide + (binaryfunc)buffer_inplace_floor_divide, // binaryfunc nb_inplace_floor_divide + (binaryfunc)buffer_inplace_true_divide, // binaryfunc nb_inplace_true_divide }; PyTypeObject pySamplebuffer_Type = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ + PyVarObject_HEAD_INIT(NULL, 0) "Buffer", /*tp_name*/ sizeof(pySamplebuffer), /*tp_basicsize*/ 0, /*tp_itemsize*/ @@ -838,6 +869,9 @@ PyTypeObject pySamplebuffer_Type = { 0, /*tp_setattro*/ &buffer_as_buffer, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT /*| Py_TPFLAGS_BASETYPE*/, /*tp_flags*/ +#if PY_MAJOR_VERSION < 3 + | Py_TPFLAGS_HAVE_NEWBUFFER +#endif "Samplebuffer objects", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ @@ -861,13 +895,14 @@ PyTypeObject pySamplebuffer_Type = { // Must have this as a function because the import_array macro in numpy version 1.01 strangely has a return statement included. // Furthermore the import error printout from this macro is ugly, but we accept that for now, waiting for later numpy updates to fix all of this. #ifdef PY_ARRAYS -static void __import_array__() +static int __import_array__() { #ifdef PY_NUMARRAY import_libnumarray(); #else import_array(); #endif + return 0; } #endif diff --git a/source/pybundle.cpp b/source/pybundle.cpp index 8f136ec..b2512d6 100644 --- a/source/pybundle.cpp +++ b/source/pybundle.cpp @@ -108,7 +108,7 @@ static PyObject *bundle_append(PyObject *self,PyObject *args) int o; if(sz > 2 && - (tg = PyTuple_GET_ITEM(args,0)) != NULL && PyInstance_Check(tg) && + (tg = PyTuple_GET_ITEM(args,0)) != NULL && (outl = PyTuple_GET_ITEM(args,1)) != NULL && #if PY_MAJOR_VERSION < 3 PyInt_Check(outl) @@ -195,8 +195,7 @@ static PyMethodDef bundle_methods[] = { PyTypeObject pyBundle_Type = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ + PyVarObject_HEAD_INIT(NULL, 0) "Bundle", /*tp_name*/ sizeof(pyBundle), /*tp_basicsize*/ 0, /*tp_itemsize*/ diff --git a/source/pydsp.cpp b/source/pydsp.cpp index 49e6aee..7676557 100644 --- a/source/pydsp.cpp +++ b/source/pydsp.cpp @@ -83,7 +83,15 @@ void pydsp::NewBuffers() for(i = 0; i < ins; ++i) { Py_XDECREF(buffers[i]); - PyObject *b = PyBuffer_FromReadWriteMemory(insigs[i],n*sizeof(t_sample)); + PyObject *b = +#if PY_MAJOR_VERSION < 3 + PyBuffer_FromReadWriteMemory(insigs[i],n*sizeof(t_sample)); +#elif PY_MINOR_VERSION >= 3 + PyMemoryView_FromMemory(reinterpret_cast(insigs[i]), n*sizeof(t_sample), PyBUF_WRITE); +#else +#error "TODO" +#endif + buffers[i] = arrayfrombuffer(b,1,n); Py_DECREF(b); } @@ -95,7 +103,15 @@ void pydsp::NewBuffers() Py_XINCREF(buffers[i]); } else { - PyObject *b = PyBuffer_FromReadWriteMemory(outsigs[i],n*sizeof(t_sample)); + PyObject *b = +#if PY_MAJOR_VERSION < 3 + PyBuffer_FromReadWriteMemory(outsigs[i],n*sizeof(t_sample)); +#elif PY_MINOR_VERSION >= 3 + PyMemoryView_FromMemory(reinterpret_cast(outsigs[i]), n*sizeof(t_sample), PyBUF_WRITE); +#else +#error "TODO" +#endif + buffers[ins+i] = arrayfrombuffer(b,1,n); Py_DECREF(b); } diff --git a/source/pyext.cpp b/source/pyext.cpp index 0deb2e9..c96b7b3 100644 --- a/source/pyext.cpp +++ b/source/pyext.cpp @@ -63,13 +63,16 @@ void pyext::Setup(t_classid c) Py_DECREF(func); } - class_obj = PyClass_New(NULL, class_dict, className); + //class_obj = PyClass_New(NULL, class_dict, className); + PyObject *classBases = PyTuple_New(0); + class_obj = PyObject_CallFunctionObjArgs((PyObject *)&PyType_Type, className, classBases, class_dict, NULL); + Py_DECREF(classBases); Py_DECREF(className); // add methods to class for (def = meth_tbl; def->ml_name != NULL; def++) { PyObject *func = PyCFunction_New(def, NULL); - PyObject *method = PyMethod_New(func, NULL, class_obj); // increases class_obj ref count by 1 + PyObject *method = PyInstanceMethod_New(func); // increases class_obj ref count by 1 PyDict_SetItemString(class_dict, def->ml_name, method); Py_DECREF(func); Py_DECREF(method); diff --git a/source/pyext.h b/source/pyext.h index 61b892a..f27b8f3 100644 --- a/source/pyext.h +++ b/source/pyext.h @@ -65,7 +65,7 @@ class pyext void ms_initargs(const AtomList &a) { m_reload_(a.Count(),a.Atoms()); } void m_dir_() { m__dir(pyobj); } void mg_dir_(AtomList &lst) { GetDir(pyobj,lst); } - void m_doc_() { m__doc(((PyInstanceObject *)pyobj)->in_class->cl_dict); } + void m_doc_() { m__doc(pyobj); } void m_get(const t_symbol *s); void m_set(int argc,const t_atom *argv); diff --git a/source/pymeth.cpp b/source/pymeth.cpp index 0ff87ed..42cbfca 100644 --- a/source/pymeth.cpp +++ b/source/pymeth.cpp @@ -382,7 +382,11 @@ bool pymeth::CbMethodResort(int n,const t_symbol *s,int argc,const t_atom *argv) else if(self != objects[0]) { // type hasn't changed, but object has PyObject *f = function; +#if PY_MAJOR_VERSION < 3 function = PyMethod_New(PyMethod_GET_FUNCTION(f),objects[0],PyMethod_GET_CLASS(f)); +#else + function = PyMethod_New(PyMethod_GET_FUNCTION(f),objects[0]); +#endif Py_DECREF(f); } } diff --git a/source/pysymbol.cpp b/source/pysymbol.cpp index 743207f..5d2a46c 100644 --- a/source/pysymbol.cpp +++ b/source/pysymbol.cpp @@ -178,13 +178,14 @@ static PyObject *symbol_repeat(PyObject *s,Py_ssize_t rep) } static PySequenceMethods symbol_as_seq = { - symbol_length, /* inquiry sq_length; __len__ */ - symbol_concat, /* __add__ */ - symbol_repeat, /* __mul__ */ - symbol_item, /* intargfunc sq_item; __getitem__ */ - symbol_slice, /* intintargfunc sq_slice; __getslice__ */ - NULL, /* intobjargproc sq_ass_item; __setitem__ */ - NULL, /* intintobjargproc sq_ass_slice; __setslice__ */ + symbol_length, /* lenfunc sq_length __len__ */ + symbol_concat, /* binaryfunc sq_concat __add__ */ + symbol_repeat, /* ssizeargfunc sq_repeat __mul__ */ + symbol_item, /* ssizeargfunc sq_item; __getitem__ */ + NULL, /* ssizeobjargproc sq_ass_item __setitem__ */ + NULL, /* objobjproc sq_contains __contains__ */ + NULL, /* binaryfunc sq_inplace_concat __iadd__ */ + NULL /* ssizeargfunc sq_inplace_repeat __imul */ }; static PyObject *symbol_iter(PyObject *s) @@ -203,45 +204,44 @@ static PyObject *symbol_iter(PyObject *s) 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*/ - &symbol_as_seq, /*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 */ + PyVarObject_HEAD_INIT(NULL, 0) + "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 */ + &symbol_as_seq, /* 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 */ - symbol_iter, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ + 0, /* tp_weaklistoffset */ + symbol_iter, /* 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 */ + symbol_init, /* tp_init */ 0, /* tp_alloc */ - symbol_new, /* tp_new */ + symbol_new, /* tp_new */ }; pySymbol *pySymbol__; From 99abb6a03dd76e08064f41b411ab193f75c3bca1 Mon Sep 17 00:00:00 2001 From: miranda kastemaa Date: Tue, 10 Mar 2020 13:47:23 +0200 Subject: [PATCH 11/40] support pymalloc python build --- build/config-mac.def | 3 +++ build/gnumake-mac-gcc.inc | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/build/config-mac.def b/build/config-mac.def index 8184e69..a86906f 100644 --- a/build/config-mac.def +++ b/build/config-mac.def @@ -33,3 +33,6 @@ PY_USE_GIL=1 # use inofficial (pure data) functionality # PY_USE_INOFFICIAL=1 + +# use python with pymalloc (look for "pythonX.Ym" files) +PY_USE_PYMALLOC=1 diff --git a/build/gnumake-mac-gcc.inc b/build/gnumake-mac-gcc.inc index c224c00..dadf7ce 100644 --- a/build/gnumake-mac-gcc.inc +++ b/build/gnumake-mac-gcc.inc @@ -27,10 +27,14 @@ $(error PY_CONDA_ROOT is undefined) endif DEFS += -DPY_INTERPRETER=$(PY_CONDA_ROOT)/bin/python -LIBS += $(PY_CONDA_ROOT)/lib/libpython$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION).dylib INCPATH += -I$(PY_CONDA_ROOT)/include +ifdef PY_USE_PYMALLOC INCPATH += -I$(PY_CONDA_ROOT)/include/python$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION)m +LIBS += $(PY_CONDA_ROOT)/lib/libpython$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION)m.dylib +else INCPATH += -I$(PY_CONDA_ROOT)/include/python$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION) +LIBS += $(PY_CONDA_ROOT)/lib/libpython$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION).dylib +endif LDFLAGS += -rpath $(PY_CONDA_ROOT)/lib ifdef PY_NUMPY From 379ab50e47ed0c34b450ee03fff49f2fb91b582d Mon Sep 17 00:00:00 2001 From: miranda kastemaa Date: Tue, 10 Mar 2020 14:18:51 +0200 Subject: [PATCH 12/40] reorganize module defs & fix builtins import --- source/modmeth.cpp | 14 ++++++++++ source/pybase.cpp | 65 +++++++++++++++++++++------------------------- source/pybase.h | 3 +++ 3 files changed, 47 insertions(+), 35 deletions(-) diff --git a/source/modmeth.cpp b/source/modmeth.cpp index 2a35b07..2325f10 100644 --- a/source/modmeth.cpp +++ b/source/modmeth.cpp @@ -52,6 +52,20 @@ const char *pybase::py_doc = "_tuple(args...): Make a tuple from args\n" ; +#if PY_MAJOR_VERSION >= 3 +PyModuleDef pybase::pyext_module_def = { + PyModuleDef_HEAD_INIT, // PyModuleDef_Base m_base + PYEXT_MODULE, // const char *m_name + py_doc, // const char *m_doc + -1, // Py_ssize_t m_size + func_tbl, // PyMethodDef *m_methods + NULL, // PyModuleDef_Slot *m_slots + NULL, // traverseproc m_traverse + NULL, // inquiry m_clear + NULL // freefunc m_free +}; +#endif + #ifdef FLEXT_THREADS void pybase::tick(void *) { diff --git a/source/pybase.cpp b/source/pybase.cpp index 41fa70a..a416a4b 100644 --- a/source/pybase.cpp +++ b/source/pybase.cpp @@ -27,6 +27,30 @@ static PyMethodDef StdOut_Methods[] = { NULL, NULL, } }; +static PyModuleDef StdOut_ModuleDef = { + PyModuleDef_HEAD_INIT, // PyModuleDef_Base m_base + "stdout", // const char *m_name + "pyext standard output", // const char *m_doc + -1, // Py_ssize_t m_size + StdOut_Methods, // PyMethodDef *m_methods + NULL, // PyModuleDef_Slot *m_slots + NULL, // traverseproc m_traverse + NULL, // inquiry m_clear + NULL // freefunc m_free +}; + +static PyModuleDef StdErr_ModuleDef = { + PyModuleDef_HEAD_INIT, // PyModuleDef_Base m_base + "pyext standard error", // const char *m_name + "", // const char *m_doc + -1, // Py_ssize_t m_size + StdOut_Methods, // PyMethodDef *m_methods + NULL, // PyModuleDef_Slot *m_slots + NULL, // traverseproc m_traverse + NULL, // inquiry m_clear + NULL // freefunc m_free +}; + static PyObject *gcollect = NULL; #ifdef FLEXT_THREADS @@ -181,17 +205,6 @@ void pybase::lib_setup() #if PY_MAJOR_VERSION < 3 module_obj = Py_InitModule(const_cast(PYEXT_MODULE), func_tbl); #else - struct PyModuleDef pyext_module_def = { - PyModuleDef_HEAD_INIT, // PyModuleDef_Base m_base - PYEXT_MODULE, // const char *m_name - py_doc, // const char *m_doc - -1, // Py_ssize_t m_size - func_tbl, // PyMethodDef *m_methods - NULL, // PyModuleDef_Slot *m_slots - NULL, // traverseproc m_traverse - NULL, // inquiry m_clear - NULL // freefunc m_free - }; module_obj = PyModule_Create(&pyext_module_def); #endif module_dict = PyModule_GetDict(module_obj); // borrowed reference @@ -203,35 +216,13 @@ void pybase::lib_setup() #if PY_MAJOR_VERSION < 3 py_out = Py_InitModule(const_cast("stdout"), StdOut_Methods); #else - struct PyModuleDef stdout_module_def = { - PyModuleDef_HEAD_INIT, // PyModuleDef_Base m_base - "stdout", // const char *m_name - "", // const char *m_doc - -1, // Py_ssize_t m_size - func_tbl, // PyMethodDef *m_methods - NULL, // PyModuleDef_Slot *m_slots - NULL, // traverseproc m_traverse - NULL, // inquiry m_clear - NULL // freefunc m_free - }; - py_out = PyModule_Create(&stdout_module_def); + py_out = PyModule_Create(&StdOut_ModuleDef); #endif PySys_SetObject(const_cast("stdout"), py_out); #if PY_MAJOR_VERSION < 3 py_out = Py_InitModule(const_cast("stderr"), StdOut_Methods); #else - struct PyModuleDef stderr_module_def = { - PyModuleDef_HEAD_INIT, // PyModuleDef_Base m_base - "stderr", // const char *m_name - "", // const char *m_doc - -1, // Py_ssize_t m_size - func_tbl, // PyMethodDef *m_methods - NULL, // PyModuleDef_Slot *m_slots - NULL, // traverseproc m_traverse - NULL, // inquiry m_clear - NULL // freefunc m_free - }; - py_out = PyModule_Create(&stdout_module_def); + py_out = PyModule_Create(&StdErr_ModuleDef); #endif PySys_SetObject(const_cast("stderr"), py_out); @@ -242,7 +233,11 @@ void pybase::lib_setup() Py_DECREF(gcobj); } +#if PY_MAJOR_VERSION < 3 builtins_obj = PyImport_ImportModule("__builtin__"); +#else + builtins_obj = PyImport_ImportModule("builtins"); +#endif builtins_dict = PyModule_GetDict(builtins_obj); // borrowed reference // add symbol type diff --git a/source/pybase.h b/source/pybase.h index cfda6bf..1db827c 100644 --- a/source/pybase.h +++ b/source/pybase.h @@ -111,6 +111,9 @@ class pybase static PyObject *module_obj,*module_dict; static PyObject *builtins_obj,*builtins_dict; static PyMethodDef func_tbl[],attr_tbl[]; +#if PY_MAJOR_VERSION >= 3 + static PyModuleDef pyext_module_def; +#endif static PyObject *py__doc__(PyObject *,PyObject *args); static PyObject *py_send(PyObject *,PyObject *args); From 34c529ed7fdef4d1199398b917102974118d087b Mon Sep 17 00:00:00 2001 From: miranda kastemaa Date: Tue, 10 Mar 2020 16:18:01 +0200 Subject: [PATCH 13/40] fix module init --- scripts/simple.py | 43 ++++++++--------- source/pybase.cpp | 118 +++++++++++++++++++++------------------------- source/pybase.h | 21 +++++++++ 3 files changed, 96 insertions(+), 86 deletions(-) diff --git a/scripts/simple.py b/scripts/simple.py index 1aa211c..b2c40bc 100644 --- a/scripts/simple.py +++ b/scripts/simple.py @@ -1,3 +1,4 @@ +from __future__ import print_function # py/pyext - python script objects for PD and MaxMSP # # Copyright (c) 2002-2007 Thomas Grill (gr@grrrr.org) @@ -68,72 +69,71 @@ try: import pyext except: - print "ERROR: This script must be loaded by the PD/Max pyext external" + print("ERROR: This script must be loaded by the PD/Max pyext external") ################################################################# class ex1(pyext._class): """Example of a simple class which receives messages and prints to the console""" - + # number of inlets and outlets _inlets=3 _outlets=0 - # methods for first inlet def bang_1(self): - print "Bang into first inlet" + print("Bang into first inlet") def int_1(self,f): - print "Integer",f,"into first inlet" + print("Integer",f,"into first inlet") def float_1(self,f): - print "Float",f,"into first inlet" + print("Float",f,"into first inlet") def list_1(self,*s): - print "List",s,"into first inlet" + print("List",s,"into first inlet") # methods for second inlet def hey_2(self): - print "Tag 'hey' into second inlet" + print("Tag 'hey' into second inlet") def ho_2(self): - print "Tag 'ho' into second inlet" + print("Tag 'ho' into second inlet") def lets_2(self): - print "Tag 'lets' into second inlet" + print("Tag 'lets' into second inlet") def go_2(self): - print "Tag 'go' into second inlet" + print("Tag 'go' into second inlet") def _anything_2(self,*args): - print "Some other message into second inlet:",args + print("Some other message into second inlet:",args) # methods for third inlet def onearg_3(self,a): - print "Tag 'onearg' into third inlet:",a + print("Tag 'onearg' into third inlet:",a) def twoargs_3(self,*a): if len(a) == 2: - print "Tag 'twoargs' into third inlet:",a[0],a[1] + print("Tag 'twoargs' into third inlet:",a[0],a[1]) else: - print "Tag 'twoargs': wrong number of arguments" + print("Tag 'twoargs': wrong number of arguments") def threeargs_3(self,*a): if len(a) == 3: - print "Tag 'threeargs' into third inlet",a[0],a[1],a[2] + print("Tag 'threeargs' into third inlet",a[0],a[1],a[2]) else: - print "Tag 'threeargs': wrong number of arguments" + print("Tag 'threeargs': wrong number of arguments") def varargs_3(self,*args): # with *args there can be arguments or not - print "Tag 'varargs' into third inlet",args + print("Tag 'varargs' into third inlet",args) @@ -149,10 +149,10 @@ class ex2(pyext._class): # methods for all inlets def hello_(self,n): - print "Tag 'hello' into inlet",n + print("Tag 'hello' into inlet",n) def _anything_(self,n,*args): - print "Message into inlet",n,":",args + print("Message into inlet",n,":",args) # methods for first inlet @@ -196,7 +196,7 @@ def __init__(self,*args): if isNumber(args[0]): self.tmp = args[0] else: - print "ex3: __init__ has superfluous arguments" + print("ex3: __init__ has superfluous arguments") # methods @@ -212,3 +212,4 @@ def int_1(self,f): def int_2(self,f): self.float_2(f) + diff --git a/source/pybase.cpp b/source/pybase.cpp index a416a4b..c778302 100644 --- a/source/pybase.cpp +++ b/source/pybase.cpp @@ -27,30 +27,6 @@ static PyMethodDef StdOut_Methods[] = { NULL, NULL, } }; -static PyModuleDef StdOut_ModuleDef = { - PyModuleDef_HEAD_INIT, // PyModuleDef_Base m_base - "stdout", // const char *m_name - "pyext standard output", // const char *m_doc - -1, // Py_ssize_t m_size - StdOut_Methods, // PyMethodDef *m_methods - NULL, // PyModuleDef_Slot *m_slots - NULL, // traverseproc m_traverse - NULL, // inquiry m_clear - NULL // freefunc m_free -}; - -static PyModuleDef StdErr_ModuleDef = { - PyModuleDef_HEAD_INIT, // PyModuleDef_Base m_base - "pyext standard error", // const char *m_name - "", // const char *m_doc - -1, // Py_ssize_t m_size - StdOut_Methods, // PyMethodDef *m_methods - NULL, // PyModuleDef_Slot *m_slots - NULL, // traverseproc m_traverse - NULL, // inquiry m_clear - NULL // freefunc m_free -}; - static PyObject *gcollect = NULL; #ifdef FLEXT_THREADS @@ -128,7 +104,46 @@ void initsymbol(); void initsamplebuffer(); void initbundle(); +MOD_INIT(pyext) +{ + return MOD_SUCCESS_VAL(pybase::pyext_init()); +} +PyObject *pybase::pyext_init() +{ + if(module_obj == NULL) { + PyObject *m; + + MOD_DEF(m, PYEXT_MODULE, py_doc, func_tbl); + + PyModule_AddStringConstant(m,"__doc__",(char *)py_doc); + + // add symbol type + initsymbol(); + PyModule_AddObject(m,"Symbol",(PyObject *)&pySymbol_Type); + + // pre-defined symbols + PyModule_AddObject(m,"_s_",(PyObject *)pySymbol__); + PyModule_AddObject(m,"_s_bang",(PyObject *)pySymbol_bang); + PyModule_AddObject(m,"_s_list",(PyObject *)pySymbol_list); + PyModule_AddObject(m,"_s_symbol",(PyObject *)pySymbol_symbol); + PyModule_AddObject(m,"_s_float",(PyObject *)pySymbol_float); + PyModule_AddObject(m,"_s_int",(PyObject *)pySymbol_int); + + // add samplebuffer type + initsamplebuffer(); + PyModule_AddObject(m,"Buffer",(PyObject *)&pySamplebuffer_Type); + + // add message bundle type + initbundle(); + PyModule_AddObject(m,"Bundle",(PyObject *)&pyBundle_Type); + + module_obj = m; + module_dict = PyModule_GetDict(m); // borrowed reference + } + + return module_obj; +} void pybase::lib_setup() { @@ -167,6 +182,8 @@ void pybase::lib_setup() // ------------------------------------------------------------- + PyImport_AppendInittab(PYEXT_MODULE, MOD_INIT_NAME(pyext)); + Py_Initialize(); #ifdef FLEXT_DEBUG @@ -201,31 +218,22 @@ void pybase::lib_setup() PySys_SetArgv(0,const_cast(¬hing)); #endif - // register/initialize pyext module only once! -#if PY_MAJOR_VERSION < 3 - module_obj = Py_InitModule(const_cast(PYEXT_MODULE), func_tbl); -#else - module_obj = PyModule_Create(&pyext_module_def); -#endif - module_dict = PyModule_GetDict(module_obj); // borrowed reference - - PyModule_AddStringConstant(module_obj,"__doc__",(char *)py_doc); - + // import the pyext module to ensure init + PyImport_ImportModule(PYEXT_MODULE); + // redirect stdout PyObject* py_out; -#if PY_MAJOR_VERSION < 3 - py_out = Py_InitModule(const_cast("stdout"), StdOut_Methods); -#else - py_out = PyModule_Create(&StdOut_ModuleDef); -#endif + + { + MOD_DEF(py_out, "stdout", "pyext standard output", StdOut_Methods); + } PySys_SetObject(const_cast("stdout"), py_out); -#if PY_MAJOR_VERSION < 3 - py_out = Py_InitModule(const_cast("stderr"), StdOut_Methods); -#else - py_out = PyModule_Create(&StdErr_ModuleDef); -#endif + + { + MOD_DEF(py_out, "stderr", "pyext standard error", StdOut_Methods); + } PySys_SetObject(const_cast("stderr"), py_out); - + // get garbage collector function PyObject *gcobj = PyImport_ImportModule("gc"); if(gcobj) { @@ -240,26 +248,6 @@ void pybase::lib_setup() #endif builtins_dict = PyModule_GetDict(builtins_obj); // borrowed reference - // 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); - - // add message bundle type - initbundle(); - PyModule_AddObject(module_obj,"Bundle",(PyObject *)&pyBundle_Type); - // ------------------------------------------------------------- #if FLEXT_SYS == FLEXT_SYS_PD && defined(PD_DEVEL_VERSION) && defined(PY_USE_INOFFICIAL) // add PD paths diff --git a/source/pybase.h b/source/pybase.h index 1db827c..e13d672 100644 --- a/source/pybase.h +++ b/source/pybase.h @@ -24,6 +24,26 @@ WARRANTIES, see the file, "license.txt," in this distribution. typedef int ThrState; // dummy #endif +#if PY_MAJOR_VERSION < 3 +#define MOD_ERROR_VAL +#define MOD_SUCCESS_VAL(val) +#define MOD_INIT_NAME(name) init##name +#define MOD_INIT(name) void MOD_INIT_NAME(name)(void) +#define MOD_DEF(ob, name, doc, methods) \ + ob = Py_InitModule3(name, methods, doc); +#else +#define MOD_ERROR_VAL NULL +#define MOD_SUCCESS_VAL(val) val +#define MOD_INIT_NAME(name) PyInit_##name +#define MOD_INIT(name) PyMODINIT_FUNC MOD_INIT_NAME(name)(void) +#define MOD_DEF(ob, name, doc, methods) \ + static struct PyModuleDef moduledef = { \ + PyModuleDef_HEAD_INIT, name, doc, -1, methods, }; \ + ob = PyModule_Create(&moduledef); +#endif + +MOD_INIT(pyext); + class pybase : public flext { @@ -38,6 +58,7 @@ class pybase static const t_symbol *GetPyArgs(AtomList &lst,PyObject *pValue,int offs = 0); static const t_symbol *GetPyAtom(AtomList &lst,PyObject *pValue); + static PyObject *pyext_init(); static void lib_setup(); protected: From 22e960149fa5a1df465064b65739fd01267aa035 Mon Sep 17 00:00:00 2001 From: miranda kastemaa Date: Tue, 10 Mar 2020 16:56:09 +0200 Subject: [PATCH 14/40] fix python 2 build --- source/pyargs.cpp | 3 ++- source/pybuffer.cpp | 56 +++++++++++++++++++++++++++++++++++++++++---- source/pybuffer.h | 4 ++-- source/pyext.cpp | 14 ++++++++---- source/pysymbol.cpp | 3 ++- source/pysymbol.h | 8 +++---- 6 files changed, 71 insertions(+), 17 deletions(-) diff --git a/source/pyargs.cpp b/source/pyargs.cpp index 6845f98..0dd3c15 100644 --- a/source/pyargs.cpp +++ b/source/pyargs.cpp @@ -162,8 +162,9 @@ const t_symbol *pybase::getone(t_atom &at,PyObject *arg) else if(pySymbol_Check(arg)) { flext::SetSymbol(at,pySymbol_AS_SYMBOL(arg)); return flext::sym_symbol; } #if PY_MAJOR_VERSION < 3 else if(PyString_Check(arg)) { flext::SetString(at,PyString_AS_STRING(arg)); return flext::sym_symbol; } -#endif +#else else if(PyUnicode_Check(arg)) { flext::SetString(at,PyUnicode_AsUTF8(arg)); return flext::sym_symbol; } +#endif else { PyObject *tp = PyObject_Type(arg); PyObject *stp = tp?PyObject_Str(tp):NULL; diff --git a/source/pybuffer.cpp b/source/pybuffer.cpp index 6892d03..4bba04e 100644 --- a/source/pybuffer.cpp +++ b/source/pybuffer.cpp @@ -106,9 +106,10 @@ static int buffer_init(PyObject *obj, PyObject *args, PyObject *kwds) #if PY_MAJOR_VERSION < 3 else if(PyString_Check(arg)) self->sym = flext::MakeSymbol(PyString_AS_STRING(arg)); -#endif +#else else if(PyUnicode_Check(arg)) self->sym = flext::MakeSymbol(PyUnicode_AsUTF8(arg)); +#endif else ret = -1; Py_DECREF(arg); @@ -193,6 +194,38 @@ static PyMethodDef buffer_methods[] = { // support the buffer protocol +static Py_ssize_t buffer_readbuffer(PyObject *obj, Py_ssize_t segment, void **ptrptr) +{ + flext::buffer *b = ((pySamplebuffer *)obj)->buf; + ptrptr[0] = b->Data(); + return b->Channels()*b->Frames()*sizeof(t_sample); +} + +static Py_ssize_t buffer_writebuffer(PyObject *obj, Py_ssize_t segment, void **ptrptr) +{ + flext::buffer *b = ((pySamplebuffer *)obj)->buf; + ptrptr[0] = b->Data(); + return b->Channels()*b->Frames()*sizeof(t_sample); +} + +static Py_ssize_t buffer_segcount(PyObject *obj, Py_ssize_t *lenp) +{ + flext::buffer *b = ((pySamplebuffer *)obj)->buf; + if(lenp) lenp[0] = b->Channels()*b->Frames()*sizeof(t_sample); + return 1; +} + +static Py_ssize_t buffer_charbuffer(PyObject *obj, Py_ssize_t segment, +#if PY_VERSION_HEX < 0x02050000 + const +#endif + char **ptrptr) +{ + flext::buffer *b = ((pySamplebuffer *)obj)->buf; + ptrptr[0] = (char *)b->Data(); + return b->Channels()*b->Frames()*sizeof(t_sample); +} + static int buffer_getbuffer(PyObject *obj, Py_buffer *view, int flags) { if(flags & PyBUF_INDIRECT) { PyErr_SetString(PyExc_BufferError, "PyBUF_INDIRECT not supported"); @@ -246,8 +279,15 @@ static void buffer_releasebuffer(PyObject *obj, Py_buffer *view) { } static PyBufferProcs buffer_as_buffer = { +#if PY_MAJOR_VERSION < 3 + buffer_readbuffer, + buffer_writebuffer, + buffer_segcount, + buffer_charbuffer +#else buffer_getbuffer, buffer_releasebuffer +#endif }; static Py_ssize_t buffer_length(PyObject *s) @@ -868,10 +908,11 @@ PyTypeObject pySamplebuffer_Type = { 0, /*tp_getattro*/ 0, /*tp_setattro*/ &buffer_as_buffer, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT /*| Py_TPFLAGS_BASETYPE*/, /*tp_flags*/ + Py_TPFLAGS_DEFAULT /*| Py_TPFLAGS_BASETYPE*/ /*tp_flags*/ #if PY_MAJOR_VERSION < 3 | Py_TPFLAGS_HAVE_NEWBUFFER #endif + , "Samplebuffer objects", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ @@ -894,15 +935,22 @@ PyTypeObject pySamplebuffer_Type = { // Must have this as a function because the import_array macro in numpy version 1.01 strangely has a return statement included. // Furthermore the import error printout from this macro is ugly, but we accept that for now, waiting for later numpy updates to fix all of this. +// The situation is further complicated by Python 3, where numpy's import_array returns NULL... #ifdef PY_ARRAYS -static int __import_array__() +#ifdef PY_NUMARRAY +#define IMPORT_ARRAY_RET_TYPE void +#elif PY_MAJOR_VERSION < 3 +#define IMPORT_ARRAY_RET_TYPE void +#else +#define IMPORT_ARRAY_RET_TYPE void * +#endif +static IMPORT_ARRAY_RET_TYPE __import_array__() { #ifdef PY_NUMARRAY import_libnumarray(); #else import_array(); #endif - return 0; } #endif diff --git a/source/pybuffer.h b/source/pybuffer.h index b14a6e5..8c98255 100644 --- a/source/pybuffer.h +++ b/source/pybuffer.h @@ -60,10 +60,10 @@ inline PyObject *pySamplebuffer_FromString(PyObject *str) #if PY_MAJOR_VERSION < 3 if(PyString_Check(str)) cstr = PyString_AsString(str); - else -#endif +#else if(PyUnicode_Check(str)) cstr = PyUnicode_AsUTF8(str); +#endif else PyErr_SetString(PyExc_TypeError, "Type must be string or unicode"); return pySamplebuffer_FromString(cstr); diff --git a/source/pyext.cpp b/source/pyext.cpp index c96b7b3..d6a2106 100644 --- a/source/pyext.cpp +++ b/source/pyext.cpp @@ -72,7 +72,11 @@ void pyext::Setup(t_classid c) // add methods to class for (def = meth_tbl; def->ml_name != NULL; def++) { PyObject *func = PyCFunction_New(def, NULL); - PyObject *method = PyInstanceMethod_New(func); // increases class_obj ref count by 1 +#if PY_MAJOR_VERSION < 3 + PyObject *method = PyMethod_New(func, NULL, class_obj); // increases class_obj ref count by 1 +#else + PyObject *method = PyInstanceMethod_New(func); +#endif PyDict_SetItemString(class_dict, def->ml_name, method); Py_DECREF(func); Py_DECREF(method); @@ -379,10 +383,10 @@ bool pyext::InitInOut(int &inl,int &outl) #if PY_MAJOR_VERSION < 3 if(PyInt_Check(res)) inl = PyInt_AS_LONG(res); - else -#endif +#else if(PyLong_Check(res)) inl = PyLong_AS_LONG(res); +#endif else PyErr_SetString(PyExc_TypeError, "Type must be integer"); Py_DECREF(res); @@ -403,10 +407,10 @@ bool pyext::InitInOut(int &inl,int &outl) #if PY_MAJOR_VERSION < 3 if(PyInt_Check(res)) outl = PyInt_AS_LONG(res); - else -#endif +#else if(PyLong_Check(res)) outl = PyLong_AS_LONG(res); +#endif else PyErr_SetString(PyExc_TypeError, "Type must be integer"); diff --git a/source/pysymbol.cpp b/source/pysymbol.cpp index 5d2a46c..d03cb2f 100644 --- a/source/pysymbol.cpp +++ b/source/pysymbol.cpp @@ -35,9 +35,10 @@ static int symbol_init(PyObject *self, PyObject *args, PyObject *kwds) #if PY_MAJOR_VERSION < 3 else if(PyString_Check(arg)) ((pySymbol *)self)->sym = flext::MakeSymbol(PyString_AS_STRING(arg)); -#endif +#else else if(PyUnicode_Check(arg)) ((pySymbol *)self)->sym = flext::MakeSymbol(PyUnicode_AsUTF8(arg)); +#endif else { PyErr_SetString(PyExc_TypeError, "string, unicode or symbol argument expected"); ret = -1; diff --git a/source/pysymbol.h b/source/pysymbol.h index 7ecd9cc..5e7353c 100644 --- a/source/pysymbol.h +++ b/source/pysymbol.h @@ -65,10 +65,10 @@ inline PyObject *pySymbol_FromString(PyObject *str) #if PY_MAJOR_VERSION < 3 if(PyString_Check(str)) cstr = PyString_AsString(str); - else -#endif +#else if(PyUnicode_Check(str)) cstr = PyUnicode_AsUTF8(str); +#endif else PyErr_SetString(PyExc_TypeError, "Type must be string or unicode"); @@ -95,10 +95,10 @@ inline const t_symbol *pyObject_AsSymbol(PyObject *op) #if PY_MAJOR_VERSION < 3 if(PyString_Check(op)) return flext::MakeSymbol(PyString_AS_STRING(op)); - else -#endif +#else if(PyUnicode_Check(op)) return flext::MakeSymbol(PyUnicode_AsUTF8(op)); +#endif else return pySymbol_AsSymbol(op); } From 4ec81db4e5ae38079a6c65c1009c04d41ee51217 Mon Sep 17 00:00:00 2001 From: miranda kastemaa Date: Tue, 17 Mar 2020 13:45:42 +0200 Subject: [PATCH 15/40] more python 3 fixing... simple-1.pd now works --- source/clmeth.cpp | 137 +++++++++++++++++++++++++++++++++++++++----- source/pybase.cpp | 4 ++ source/pybuffer.cpp | 2 +- source/pyext.cpp | 17 +++++- 4 files changed, 143 insertions(+), 17 deletions(-) diff --git a/source/clmeth.cpp b/source/clmeth.cpp index 340dcc4..1a18eb1 100644 --- a/source/clmeth.cpp +++ b/source/clmeth.cpp @@ -29,17 +29,22 @@ PyMethodDef pyext::meth_tbl[] = #endif { "_invec", pyext::pyext_invec, METH_VARARGS,"Get input vector" }, { "_outvec", pyext::pyext_outvec, METH_VARARGS,"Get output vector" }, +#if PY_MAJOR_VERSION >= 3 + { "__setattr__", pyext::pyext_setattr, METH_VARARGS,"Set class attribute" }, + { "__getattr__", pyext::pyext_getattr, METH_VARARGS,"Get class attribute" }, +#endif {NULL, NULL, 0, NULL} /* Sentinel */ }; PyMethodDef pyext::attr_tbl[] = { +#if PY_MAJOR_VERSION < 3 { "__setattr__", pyext::pyext_setattr, METH_VARARGS,"Set class attribute" }, { "__getattr__", pyext::pyext_getattr, METH_VARARGS,"Get class attribute" }, +#endif { NULL, NULL,0,NULL }, }; - const char *pyext::pyext_doc = "py/pyext - python external object for PD and Max/MSP, (C)2002-2008 Thomas Grill\n" "\n" @@ -76,15 +81,21 @@ PyObject* pyext::pyext__del__(PyObject *,PyObject *args) } */ +#if PY_MAJOR_VERSION < 3 PyObject* pyext::pyext__str__(PyObject *,PyObject *args) +#else +PyObject* pyext::pyext__str__(PyObject *self, PyObject *args) +#endif { +#if PY_MAJOR_VERSION < 3 PyObject *self; if(!PyArg_ParseTuple(args, "O:pyext__str__",&self)) { // handle error ERRINTERNAL(); return NULL; } - +#endif + return #if PY_MAJOR_VERSION < 3 PyString_FromFormat @@ -94,10 +105,20 @@ PyObject* pyext::pyext__str__(PyObject *,PyObject *args) ("", self); } +#if PY_MAJOR_VERSION < 3 PyObject* pyext::pyext_setattr(PyObject *,PyObject *args) +#else +PyObject* pyext::pyext_setattr(PyObject *self, PyObject *args) +#endif { +#if PY_MAJOR_VERSION < 3 PyObject *self,*name,*val; - if(!PyArg_ParseTuple(args, "OOO:pyext_setattr", &self,&name,&val)) { + if(!PyArg_ParseTuple(args, "OOO:pyext_setattr", &self,&name,&val)) +#else + PyObject *name, *val; + if(!PyArg_ParseTuple(args, "OO:pyext_setattr", &name, &val)) +#endif + { // handle error ERRINTERNAL(); return NULL; @@ -121,19 +142,35 @@ PyObject* pyext::pyext_setattr(PyObject *,PyObject *args) return Py_None; } +#if PY_MAJOR_VERSION < 3 PyObject* pyext::pyext_getattr(PyObject *,PyObject *args) +#else +PyObject* pyext::pyext_getattr(PyObject *self, PyObject *args) +#endif { +#if PY_MAJOR_VERSION < 3 PyObject *self, *name, *ret = NULL; - if(!PyArg_ParseTuple(args, "OO:pyext_getattr", &self, &name)) { + if(!PyArg_ParseTuple(args, "OO:pyext_getattr", &self, &name)) +#else + PyObject *name, *ret = NULL; + if(!PyArg_ParseTuple(args, "O:pyext_getattr", &name)) +#endif + { // handle error ERRINTERNAL(); + return NULL; } #if PY_MAJOR_VERSION < 3 - if(PyString_Check(name)) { + if(PyString_Check(name)) +#else + if(PyUnicode_Check(name)) + +#endif + { +#if PY_MAJOR_VERSION < 3 const char *sname = PyString_AS_STRING(name); #else - if(PyUnicode_Check(name)) { const char *sname = PyUnicode_AsUTF8(name); #endif if(sname) { @@ -175,7 +212,11 @@ PyObject* pyext::pyext_getattr(PyObject *,PyObject *args) } //! Send message to outlet +#if PY_MAJOR_VERSION < 3 PyObject *pyext::pyext_outlet(PyObject *,PyObject *args) +#else +PyObject *pyext::pyext_outlet(PyObject *self, PyObject *args) +#endif { bool ok = false; @@ -185,11 +226,19 @@ PyObject *pyext::pyext_outlet(PyObject *,PyObject *args) int sz = PyTuple_GET_SIZE(args); // borrowed references! +#if PY_MAJOR_VERSION < 3 PyObject *self,*outl; +#else + PyObject *outl; +#endif if( sz >= 2 && - (self = PyTuple_GET_ITEM(args,0)) != NULL && +#if PY_MAJOR_VERSION < 3 + (self = PyTuple_GET_ITEM(args,0)) != NULL && +#else + self != NULL && +#endif (outl = PyTuple_GET_ITEM(args,1)) != NULL && #if PY_MAJOR_VERSION < 3 PyInt_Check(outl) @@ -256,11 +305,23 @@ PyObject *pyext::pyext_outlet(PyObject *,PyObject *args) #ifdef FLEXT_THREADS //! Detach threads +#if PY_MAJOR_VERSION < 3 PyObject *pyext::pyext_detach(PyObject *,PyObject *args) +#else +PyObject *pyext::pyext_detach(PyObject *self, PyObject *args) +#endif { - PyObject *self; +#if PY_MAJOR_VERSION < 3 + PyObject *self; +#endif int val; - if(!PyArg_ParseTuple(args, "Oi:pyext_detach",&self,&val)) { + if( +#if PY_MAJOR_VERSION < 3 + !PyArg_ParseTuple(args, "Oi:pyext_detach",&self,&val) +#else + !PyArg_ParseTuple(args, "i:pyext_detach", &val) +#endif + ) { // handle error PyErr_SetString(PyExc_SyntaxError,"pyext - Syntax: _detach(self,[0/1/2])"); return NULL; @@ -284,11 +345,23 @@ PyObject *pyext::pyext_detach(PyObject *,PyObject *args) } //! Stop running threads +#if PY_MAJOR_VERSION < 3 PyObject *pyext::pyext_stop(PyObject *,PyObject *args) +#else +PyObject *pyext::pyext_stop(PyObject *self, PyObject *args) +#endif { - PyObject *self; +#if PY_MAJOR_VERSION < 3 + PyObject *self; +#endif int val = -1; - if(!PyArg_ParseTuple(args, "O|i:pyext_stop",&self,&val)) { + if( +#if PY_MAJOR_VERSION < 3 + !PyArg_ParseTuple(args, "O|i:pyext_stop",&self,&val) +#else + !PyArg_ParseTuple(args, "|i:pyext_stop", &val) +#endif + ) { // handle error PyErr_SetString(PyExc_SyntaxError,"pyext - Syntax: _stop(self,{wait time})"); return NULL; @@ -320,17 +393,27 @@ PyObject *pyext::pyext_stop(PyObject *,PyObject *args) #if FLEXT_SYS == FLEXT_SYS_PD //! Send message to canvas +#if PY_MAJOR_VERSION < 3 PyObject *pyext::pyext_tocanvas(PyObject *,PyObject *args) +#else +PyObject *pyext::pyext_tocanvas(PyObject *self, PyObject *args) +#endif { FLEXT_ASSERT(PyTuple_Check(args)); int sz = PyTuple_GET_SIZE(args); bool ok = false; +#if PY_MAJOR_VERSION < 3 PyObject *self; // borrowed ref +#endif if( sz >= 1 && +#if PY_MAJOR_VERSION < 3 (self = PyTuple_GET_ITEM(args,0)) != NULL +#else + self != NULL +#endif ) { pyext *ext = GetThis(self); if(!ext) { @@ -381,11 +464,23 @@ PyObject *pyext::pyext_tocanvas(PyObject *,PyObject *args) } #endif +#if PY_MAJOR_VERSION < 3 PyObject *pyext::pyext_invec(PyObject *,PyObject *args) +#else +PyObject *pyext::pyext_invec(PyObject *self, PyObject *args) +#endif { - PyObject *self; +#if PY_MAJOR_VERSION < 3 + PyObject *self; +#endif int val = -1; - if(!PyArg_ParseTuple(args, "O|i:pyext_invec",&self,&val)) { + if( +#if PY_MAJOR_VERSION < 3 + !PyArg_ParseTuple(args, "O|i:pyext_invec",&self,&val) +#else + !PyArg_ParseTuple(args, "|i:pyext_invec", &val) +#endif + ) { // handle error PyErr_SetString(PyExc_SyntaxError,"pyext - Syntax: _invec(self,inlet)"); return NULL; @@ -410,11 +505,23 @@ PyObject *pyext::pyext_invec(PyObject *,PyObject *args) return Py_None; } +#if PY_MAJOR_VERSION < 3 PyObject *pyext::pyext_outvec(PyObject *,PyObject *args) +#else +PyObject *pyext::pyext_outvec(PyObject *self, PyObject *args) +#endif { - PyObject *self; +#if PY_MAJOR_VERSION < 3 + PyObject *self; +#endif int val = -1; - if(!PyArg_ParseTuple(args, "O|i:pyext_outvec",&self,&val)) { + if( +#if PY_MAJOR_VERSION < 3 + !PyArg_ParseTuple(args, "O|i:pyext_outvec",&self,&val) +#else + !PyArg_ParseTuple(args, "|i:pyext_outvec", &val) +#endif + ) { // handle error PyErr_SetString(PyExc_SyntaxError,"pyext - Syntax: _outvec(self,inlet)"); return NULL; diff --git a/source/pybase.cpp b/source/pybase.cpp index c778302..7a5358a 100644 --- a/source/pybase.cpp +++ b/source/pybase.cpp @@ -1058,7 +1058,11 @@ bool pybase::collect() PyObject *ret = PyObject_CallObject(gcollect,NULL); if(ret) { #ifdef FLEXT_DEBUG +#if PY_MAJOR_VERSION < 3 int refs = PyInt_AsLong(ret); +#else + int refs = PyLong_AsLong(ret); +#endif if(refs) post("py/pyext - Garbage collector reports %i unreachable objects",refs); #endif Py_DECREF(ret); diff --git a/source/pybuffer.cpp b/source/pybuffer.cpp index 4bba04e..22eb1c4 100644 --- a/source/pybuffer.cpp +++ b/source/pybuffer.cpp @@ -337,7 +337,7 @@ PyObject *arrayfrombuffer(PyObject *buf,int c,int n) Py_buffer view; int err = PyObject_GetBuffer(buf, &view, PyBUF_SIMPLE | PyBUF_WRITABLE); if(!err) { - FLEXT_ASSERT(view->len <= n*c*sizeof(t_sample)); + FLEXT_ASSERT(view.len <= n*c*sizeof(t_sample)); // Py_INCREF(buf); // ATTENTION... this won't be released any more!! # ifdef PY_NUMPY arr = PyArray_NewFromDescr(&PyArray_Type,PyArray_DescrNewFromType(numtype),c == 1?1:2,shape,0,(char *)view.buf,NPY_WRITEABLE|NPY_C_CONTIGUOUS,NULL); diff --git a/source/pyext.cpp b/source/pyext.cpp index d6a2106..84f805d 100644 --- a/source/pyext.cpp +++ b/source/pyext.cpp @@ -214,7 +214,10 @@ bool pyext::Init() if(methname) { MakeInstance(); - if(pyobj) InitInOut(inlets,outlets); + if(pyobj) { + SetThis(); + InitInOut(inlets,outlets); + } } else inlets = outlets = 0; @@ -366,9 +369,11 @@ bool pyext::InitInOut(int &inl,int &outl) FLEXT_ASSERT(!ret); } +#if PY_MAJOR_VERSION < 3 // __init__ can override the number of inlets and outlets if(!DoInit()) // call __init__ constructor return false; +#endif if(inl < 0) { // get number of inlets @@ -440,6 +445,16 @@ bool pyext::MakeInstance() if(PyObject_IsInstance(pref, (PyObject *)&PyType_Type)) { // pyobj = PyBaseObject_Type.tp_new() // TODO: correctly initialize instance + + PyObject *pargs = MakePyArgs(NULL, initargs.Count(), initargs.Atoms()); + + if(!pargs) { + PyErr_Print(); + } else { + pyobj = PyObject_CallObject(pref, pargs); + + Py_DECREF(pargs); + } #endif if(!pyobj) PyErr_Print(); } From 6e8ea8212d6d9e9b451e30c81787b9f8f08b02eb Mon Sep 17 00:00:00 2001 From: miranda kastemaa Date: Tue, 17 Mar 2020 17:16:42 +0200 Subject: [PATCH 16/40] python 3 - simple-2.pd now working --- source/clmeth.cpp | 98 ++++++----------------------------------------- source/pyext.cpp | 5 ++- 2 files changed, 14 insertions(+), 89 deletions(-) diff --git a/source/clmeth.cpp b/source/clmeth.cpp index 1a18eb1..a28ca1d 100644 --- a/source/clmeth.cpp +++ b/source/clmeth.cpp @@ -81,20 +81,14 @@ PyObject* pyext::pyext__del__(PyObject *,PyObject *args) } */ -#if PY_MAJOR_VERSION < 3 PyObject* pyext::pyext__str__(PyObject *,PyObject *args) -#else -PyObject* pyext::pyext__str__(PyObject *self, PyObject *args) -#endif { -#if PY_MAJOR_VERSION < 3 PyObject *self; if(!PyArg_ParseTuple(args, "O:pyext__str__",&self)) { // handle error ERRINTERNAL(); return NULL; } -#endif return #if PY_MAJOR_VERSION < 3 @@ -105,19 +99,10 @@ PyObject* pyext::pyext__str__(PyObject *self, PyObject *args) ("", self); } -#if PY_MAJOR_VERSION < 3 PyObject* pyext::pyext_setattr(PyObject *,PyObject *args) -#else -PyObject* pyext::pyext_setattr(PyObject *self, PyObject *args) -#endif { -#if PY_MAJOR_VERSION < 3 PyObject *self,*name,*val; if(!PyArg_ParseTuple(args, "OOO:pyext_setattr", &self,&name,&val)) -#else - PyObject *name, *val; - if(!PyArg_ParseTuple(args, "OO:pyext_setattr", &name, &val)) -#endif { // handle error ERRINTERNAL(); @@ -135,26 +120,27 @@ PyObject* pyext::pyext_setattr(PyObject *self, PyObject *args) } */ if(!handled) { - PyDict_SetItem(self, name, val); + if( +#if PY_MAJOR_VERSION < 3 + PyDict_SetItem(self, name, val) +#else + PyObject_SetAttr(self, name, val) +#endif + < 0 + ) { + ERRINTERNAL(); + return NULL; + } } Py_INCREF(Py_None); return Py_None; } -#if PY_MAJOR_VERSION < 3 PyObject* pyext::pyext_getattr(PyObject *,PyObject *args) -#else -PyObject* pyext::pyext_getattr(PyObject *self, PyObject *args) -#endif { -#if PY_MAJOR_VERSION < 3 PyObject *self, *name, *ret = NULL; if(!PyArg_ParseTuple(args, "OO:pyext_getattr", &self, &name)) -#else - PyObject *name, *ret = NULL; - if(!PyArg_ParseTuple(args, "O:pyext_getattr", &name)) -#endif { // handle error ERRINTERNAL(); @@ -212,11 +198,7 @@ PyObject* pyext::pyext_getattr(PyObject *self, PyObject *args) } //! Send message to outlet -#if PY_MAJOR_VERSION < 3 PyObject *pyext::pyext_outlet(PyObject *,PyObject *args) -#else -PyObject *pyext::pyext_outlet(PyObject *self, PyObject *args) -#endif { bool ok = false; @@ -226,19 +208,11 @@ PyObject *pyext::pyext_outlet(PyObject *self, PyObject *args) int sz = PyTuple_GET_SIZE(args); // borrowed references! -#if PY_MAJOR_VERSION < 3 PyObject *self,*outl; -#else - PyObject *outl; -#endif if( sz >= 2 && -#if PY_MAJOR_VERSION < 3 (self = PyTuple_GET_ITEM(args,0)) != NULL && -#else - self != NULL && -#endif (outl = PyTuple_GET_ITEM(args,1)) != NULL && #if PY_MAJOR_VERSION < 3 PyInt_Check(outl) @@ -305,22 +279,12 @@ PyObject *pyext::pyext_outlet(PyObject *self, PyObject *args) #ifdef FLEXT_THREADS //! Detach threads -#if PY_MAJOR_VERSION < 3 PyObject *pyext::pyext_detach(PyObject *,PyObject *args) -#else -PyObject *pyext::pyext_detach(PyObject *self, PyObject *args) -#endif { -#if PY_MAJOR_VERSION < 3 PyObject *self; -#endif int val; if( -#if PY_MAJOR_VERSION < 3 !PyArg_ParseTuple(args, "Oi:pyext_detach",&self,&val) -#else - !PyArg_ParseTuple(args, "i:pyext_detach", &val) -#endif ) { // handle error PyErr_SetString(PyExc_SyntaxError,"pyext - Syntax: _detach(self,[0/1/2])"); @@ -345,22 +309,12 @@ PyObject *pyext::pyext_detach(PyObject *self, PyObject *args) } //! Stop running threads -#if PY_MAJOR_VERSION < 3 PyObject *pyext::pyext_stop(PyObject *,PyObject *args) -#else -PyObject *pyext::pyext_stop(PyObject *self, PyObject *args) -#endif { -#if PY_MAJOR_VERSION < 3 PyObject *self; -#endif int val = -1; if( -#if PY_MAJOR_VERSION < 3 !PyArg_ParseTuple(args, "O|i:pyext_stop",&self,&val) -#else - !PyArg_ParseTuple(args, "|i:pyext_stop", &val) -#endif ) { // handle error PyErr_SetString(PyExc_SyntaxError,"pyext - Syntax: _stop(self,{wait time})"); @@ -393,27 +347,17 @@ PyObject *pyext::pyext_stop(PyObject *self, PyObject *args) #if FLEXT_SYS == FLEXT_SYS_PD //! Send message to canvas -#if PY_MAJOR_VERSION < 3 PyObject *pyext::pyext_tocanvas(PyObject *,PyObject *args) -#else -PyObject *pyext::pyext_tocanvas(PyObject *self, PyObject *args) -#endif { FLEXT_ASSERT(PyTuple_Check(args)); int sz = PyTuple_GET_SIZE(args); bool ok = false; -#if PY_MAJOR_VERSION < 3 PyObject *self; // borrowed ref -#endif if( sz >= 1 && -#if PY_MAJOR_VERSION < 3 (self = PyTuple_GET_ITEM(args,0)) != NULL -#else - self != NULL -#endif ) { pyext *ext = GetThis(self); if(!ext) { @@ -464,22 +408,12 @@ PyObject *pyext::pyext_tocanvas(PyObject *self, PyObject *args) } #endif -#if PY_MAJOR_VERSION < 3 PyObject *pyext::pyext_invec(PyObject *,PyObject *args) -#else -PyObject *pyext::pyext_invec(PyObject *self, PyObject *args) -#endif { -#if PY_MAJOR_VERSION < 3 PyObject *self; -#endif int val = -1; if( -#if PY_MAJOR_VERSION < 3 !PyArg_ParseTuple(args, "O|i:pyext_invec",&self,&val) -#else - !PyArg_ParseTuple(args, "|i:pyext_invec", &val) -#endif ) { // handle error PyErr_SetString(PyExc_SyntaxError,"pyext - Syntax: _invec(self,inlet)"); @@ -505,22 +439,12 @@ PyObject *pyext::pyext_invec(PyObject *self, PyObject *args) return Py_None; } -#if PY_MAJOR_VERSION < 3 PyObject *pyext::pyext_outvec(PyObject *,PyObject *args) -#else -PyObject *pyext::pyext_outvec(PyObject *self, PyObject *args) -#endif { -#if PY_MAJOR_VERSION < 3 PyObject *self; -#endif int val = -1; if( -#if PY_MAJOR_VERSION < 3 !PyArg_ParseTuple(args, "O|i:pyext_outvec",&self,&val) -#else - !PyArg_ParseTuple(args, "|i:pyext_outvec", &val) -#endif ) { // handle error PyErr_SetString(PyExc_SyntaxError,"pyext - Syntax: _outvec(self,inlet)"); diff --git a/source/pyext.cpp b/source/pyext.cpp index 84f805d..6e9636b 100644 --- a/source/pyext.cpp +++ b/source/pyext.cpp @@ -74,10 +74,11 @@ void pyext::Setup(t_classid c) PyObject *func = PyCFunction_New(def, NULL); #if PY_MAJOR_VERSION < 3 PyObject *method = PyMethod_New(func, NULL, class_obj); // increases class_obj ref count by 1 + PyDict_SetItemString(class_dict, def->ml_name, method); #else - PyObject *method = PyInstanceMethod_New(func); + PyObject *method = PyMethod_New(func, class_obj); + PyObject_SetAttrString(class_obj, def->ml_name, method); #endif - PyDict_SetItemString(class_dict, def->ml_name, method); Py_DECREF(func); Py_DECREF(method); } From 4408d3d84443dd4743a1ea9edbc0f2711ffd04c2 Mon Sep 17 00:00:00 2001 From: miranda kastemaa Date: Tue, 17 Mar 2020 17:21:27 +0200 Subject: [PATCH 17/40] fix script.py for python 3, fix simple.py docstring --- scripts/script.py | 9 ++++++--- scripts/simple.py | 11 ++++++----- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/scripts/script.py b/scripts/script.py index ff41730..0ef6492 100644 --- a/scripts/script.py +++ b/scripts/script.py @@ -7,14 +7,17 @@ """Several functions to show the py script functionality""" +from __future__ import print_function + +from functools import reduce import sys -print "Script initialized" +print("Script initialized") try: - print "Script arguments: ",sys.argv + print("Script arguments: ",sys.argv) except: - print + print() def numargs(*args): # variable argument list """Return the number of arguments""" diff --git a/scripts/simple.py b/scripts/simple.py index b2c40bc..4377a16 100644 --- a/scripts/simple.py +++ b/scripts/simple.py @@ -1,4 +1,3 @@ -from __future__ import print_function # py/pyext - python script objects for PD and MaxMSP # # Copyright (c) 2002-2007 Thomas Grill (gr@grrrr.org) @@ -59,13 +58,15 @@ or self._outlet(outlet,arg) ... where arg is a sequence containing only atoms - Do not use _outlet inside __init__, since the outlets have not been created at that time. + Do not use _outlet inside __init__, since the outlets have not been created at that time. - Use pyext functions and methods: See the __doc__ strings of the pyext module and the pyext._class base class. """ +from __future__ import print_function + try: import pyext except: @@ -75,7 +76,7 @@ class ex1(pyext._class): """Example of a simple class which receives messages and prints to the console""" - + # number of inlets and outlets _inlets=3 _outlets=0 @@ -207,9 +208,9 @@ def float_2(self,f): self.tmp = f # handlers for MaxMSP int type - def int_1(self,f): + def int_1(self,f): self.float_1(f) - def int_2(self,f): + def int_2(self,f): self.float_2(f) From a32b6dbbbb1f20485e551c969f1d8078ecd3583f Mon Sep 17 00:00:00 2001 From: miranda kastemaa Date: Tue, 17 Mar 2020 17:40:16 +0200 Subject: [PATCH 18/40] more python 3 compat work on example scripts --- scripts/buffer.py | 6 ++++-- scripts/simple.py | 9 ++++++++- scripts/threads.py | 15 ++++++++++++--- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/scripts/buffer.py b/scripts/buffer.py index b58ea42..401cc6b 100644 --- a/scripts/buffer.py +++ b/scripts/buffer.py @@ -12,18 +12,20 @@ Numeric, numarray and numpy (for all of them see http://numeric.scipy.org) """ +from __future__ import print_function + import sys try: import pyext except: - print "ERROR: This script must be loaded by the PD/Max py/pyext external" + print("ERROR: This script must be loaded by the PD/Max py/pyext external") try: # numpy is assumed here... numeric and numarray are considered deprecated import numpy as N except: - print "Failed importing numpy module:",sys.exc_value + print("Failed importing numpy module:",sys.exc_value) def mul(*args): # create buffer objects diff --git a/scripts/simple.py b/scripts/simple.py index 4377a16..3d749d3 100644 --- a/scripts/simple.py +++ b/scripts/simple.py @@ -72,6 +72,13 @@ except: print("ERROR: This script must be loaded by the PD/Max pyext external") +try: + # Python 2 + _long = long + del _long +except NameError: + long = int + ################################################################# class ex1(pyext._class): @@ -172,7 +179,7 @@ def float_2(self,f): # helper function - determine whether argument is a numeric type def isNumber(value): import types - if type(value) in (types.FloatType, types.IntType, types.LongType): + if type(value) in (float, int, long): return 1 else: return 0 diff --git a/scripts/threads.py b/scripts/threads.py index b029910..769127f 100755 --- a/scripts/threads.py +++ b/scripts/threads.py @@ -16,13 +16,22 @@ """ +from __future__ import print_function + try: import pyext except: - print "ERROR: This script must be loaded by the PD/Max pyext external" + print("ERROR: This script must be loaded by the PD/Max pyext external") from time import sleep +try: + # Python 2 + range = xrange +except NameError: + # Python 3 + pass + ################################################################# class ex1(pyext._class): @@ -37,10 +46,10 @@ class ex1(pyext._class): # method for bang to any inlet def bang_(self,n): - for i in xrange(self.loops): + for i in range(self.loops): # if _shouldexit is true, the thread ought to stop if self._shouldexit: - print "BREAK" + print("BREAK") break self._outlet(n,i) From daa554340acdec6e13cfa7e47a8212f7d6808632 Mon Sep 17 00:00:00 2001 From: miranda kastemaa Date: Mon, 23 Mar 2020 19:04:23 +0200 Subject: [PATCH 19/40] first shot at pybuffer in py3. still buggy... --- source/pybuffer.cpp | 143 ++++++++++++++++++++++++++++++++++++++++++-- source/pycompat.cpp | 93 ++++++++++++++++++++++++++++ source/pycompat.h | 40 +++++++++++++ source/pyprefix.h | 23 +++++++ 4 files changed, 293 insertions(+), 6 deletions(-) create mode 100644 source/pycompat.cpp create mode 100644 source/pycompat.h diff --git a/source/pybuffer.cpp b/source/pybuffer.cpp index 22eb1c4..d19e817 100644 --- a/source/pybuffer.cpp +++ b/source/pybuffer.cpp @@ -43,6 +43,7 @@ inline bool arrsupport() { return numtype != tAny; } #endif #endif +#include "pycompat.h" PyObject *pybase::py_arraysupport(PyObject *self,PyObject *args) { @@ -57,7 +58,7 @@ PyObject *pybase::py_arraysupport(PyObject *self,PyObject *args) } -// PD defines a T_OBJECT symbol +// undefine PD's T_OBJECT to avoid conflict with Python's #undef T_OBJECT #ifdef PY_USE_FRAMEWORK @@ -301,6 +302,9 @@ static PyObject *buffer_item(PyObject *s,Py_ssize_t i) pySamplebuffer *self = reinterpret_cast(s); PyObject *ret; if(self->buf) { + if(i < 0) { + i += self->buf->Frames(); + } if (i < 0 || i >= self->buf->Frames()) { PyErr_SetString(PyExc_IndexError,"Index out of range"); ret = NULL; @@ -514,16 +518,139 @@ static PyObject *buffer_repeat(PyObject *s,Py_ssize_t rep) return NULL; } +static PyObject *buffer_subscript(PyObject *s, PyObject *item) +{ + pySamplebuffer *self = reinterpret_cast(s); + PyObject *ret; + +#ifdef PY_ARRAYS + if(arrsupport()) { + if(self->buf) { + const int n = self->buf->Frames(); + const int c = self->buf->Channels(); + + PyObject *nobj = arrayfrombuffer((PyObject *) self, c, n); + ret = PyObject_GetItem(nobj, item); + Py_DECREF(nobj); + } else { + Py_INCREF(Py_None); + ret = Py_None; + } + } + else +#endif + if(PyIndex_Check(item)) { + Py_ssize_t i; + i = PyNumber_AsSsize_t(item, PyExc_IndexError); + if(i == -1 && PyErr_Occurred()) { + ret = NULL; + } else { + if(i < 0) + i += PyList_GET_SIZE(self); + + ret = buffer_item(s, i); + } + } else if(PySlice_Check(item)) { + PyErr_SetString(PyExc_RuntimeError, "No array support"); + ret = NULL; + } else { + PyErr_Format( + PyExc_TypeError, + "buffer indices must be integers or slices, not %.200s", + item->ob_type->tp_name + ); + ret = NULL; + } + + return ret; +} + +static int buffer_ass_subscript(PyObject *s, PyObject *item, PyObject *value) +{ + pySamplebuffer *self = reinterpret_cast(s); + int ret; + + if(PyIndex_Check(item)) { + Py_ssize_t i; + i = PyNumber_AsSsize_t(item, PyExc_IndexError); + if(i == -1 && PyErr_Occurred()) { + ret = -1; + } else { + ret = buffer_ass_item(s, i, value); + } + } else if(PySlice_Check(item)) { +#ifdef PY_ARRAYS + if(arrsupport()) { + if(self->buf) { + const int n = self->buf->Frames(); + const int c = self->buf->Channels(); + + PyArrayObject *out = (PyArrayObject *) PyArray_ContiguousFromObject(value, numtype, 1, 2); + const t_sample *src = (t_sample *) out->data; + + if(!out) { + // exception already set + ret = -1; + } else if(out->nd != 1) { + PyErr_SetString(PyExc_NotImplementedError, "Multiple dimensions not supported yet"); + ret = -1; + } else { + Py_ssize_t ilow, ihigh, istep; + + if(PySlice_Unpack(item, &ilow, &ihigh, &istep) < 0) { + ret = -1; + } else { + Py_ssize_t dlen = PySlice_AdjustIndices(n, &ilow, &ihigh, istep); + int slen = out->dimensions[0]; + int cnt = slen < dlen ? slen : dlen; + flext::buffer::Element *dst = self->buf->Data() + ilow; + for(int i = 0; i < cnt; i += istep) { + dst[i] = src[i]; + } + + self->dirty = true; + ret = 0; + } + } + + Py_XDECREF(out); + } else { + PyErr_SetString(PyExc_ValueError,"Buffer is not assigned"); + ret = -1; + } + } + else +#endif + { + PyErr_SetString(PyExc_RuntimeError, "No array support"); + ret = -1; + } + } else { + PyErr_Format( + PyExc_TypeError, + "buffer indices must be integers or slices, not %.200s", + item->ob_type->tp_name + ); + ret = -1; + } + + return ret; +} static PySequenceMethods buffer_as_seq = { buffer_length, /* lenfunc sq_length __len__ */ buffer_concat, /* binaryfunc sq_concat __add__ */ buffer_repeat, /* ssizeargfunc sq_repeat __mul__ */ - buffer_item, /* ssizeargfunc sq_item; __getitem__ */ + NULL, /* ssizeargfunc sq_item; __getitem__ */ + NULL, /* intintargfunc sq_slice; __getslice__ */ NULL, /* ssizeobjargproc sq_ass_item __setitem__ */ - NULL, /* objobjproc sq_contains __contains__ */ - NULL, /* binaryfunc sq_inplace_concat __iadd__ */ - NULL /* ssizeargfunc sq_inplace_repeat __imul */ + NULL, /* intintobjargproc sq_ass_slice; __setslice__ */ +}; + +static PyMappingMethods buffer_as_mapping = { + buffer_length, // lenfunc mp_length + buffer_subscript, // binaryfunc mp_subscript + buffer_ass_subscript // objobjargproc mp_ass_subscript }; static PyObject *buffer_iter(PyObject *s) @@ -901,7 +1028,7 @@ PyTypeObject pySamplebuffer_Type = { buffer_repr, /*tp_repr*/ &buffer_as_number, /*tp_as_number*/ &buffer_as_seq, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ + &buffer_as_mapping, /*tp_as_mapping*/ buffer_hash, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ @@ -939,10 +1066,13 @@ PyTypeObject pySamplebuffer_Type = { #ifdef PY_ARRAYS #ifdef PY_NUMARRAY #define IMPORT_ARRAY_RET_TYPE void +#define IMPORT_ARRAY_RET_VALUE #elif PY_MAJOR_VERSION < 3 #define IMPORT_ARRAY_RET_TYPE void +#define IMPORT_ARRAY_RET_VALUE #else #define IMPORT_ARRAY_RET_TYPE void * +#define IMPORT_ARRAY_RET_VALUE NULL #endif static IMPORT_ARRAY_RET_TYPE __import_array__() { @@ -951,6 +1081,7 @@ static IMPORT_ARRAY_RET_TYPE __import_array__() #else import_array(); #endif + return IMPORT_ARRAY_RET_VALUE; } #endif diff --git a/source/pycompat.cpp b/source/pycompat.cpp new file mode 100644 index 0000000..809c830 --- /dev/null +++ b/source/pycompat.cpp @@ -0,0 +1,93 @@ +#include "pycompat.h" + +// copied from Python 3.8.2 for compatibility with pre-3.6.1 versions because +// doing the right thing with the older slice functions seems difficult... + +#if PY_VERSION_HEX < 0x03060100 +int +PySlice_Unpack(PyObject *_r, + Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step) +{ + PySliceObject *r = (PySliceObject*)_r; + /* this is harder to get right than you might think */ + + Py_BUILD_ASSERT(PY_SSIZE_T_MIN + 1 <= -PY_SSIZE_T_MAX); + + if (r->step == Py_None) { + *step = 1; + } + else { + if (!_PyEval_SliceIndex(r->step, step)) return -1; + if (*step == 0) { + PyErr_SetString(PyExc_ValueError, + "slice step cannot be zero"); + return -1; + } + /* Here *step might be -PY_SSIZE_T_MAX-1; in this case we replace it + * with -PY_SSIZE_T_MAX. This doesn't affect the semantics, and it + * guards against later undefined behaviour resulting from code that + * does "step = -step" as part of a slice reversal. + */ + if (*step < -PY_SSIZE_T_MAX) + *step = -PY_SSIZE_T_MAX; + } + + if (r->start == Py_None) { + *start = *step < 0 ? PY_SSIZE_T_MAX : 0; + } + else { + if (!_PyEval_SliceIndex(r->start, start)) return -1; + } + + if (r->stop == Py_None) { + *stop = *step < 0 ? PY_SSIZE_T_MIN : PY_SSIZE_T_MAX; + } + else { + if (!_PyEval_SliceIndex(r->stop, stop)) return -1; + } + + return 0; +} + +Py_ssize_t +PySlice_AdjustIndices(Py_ssize_t length, + Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t step) +{ + /* this is harder to get right than you might think */ + + assert(step != 0); + assert(step >= -PY_SSIZE_T_MAX); + + if (*start < 0) { + *start += length; + if (*start < 0) { + *start = (step < 0) ? -1 : 0; + } + } + else if (*start >= length) { + *start = (step < 0) ? length - 1 : length; + } + + if (*stop < 0) { + *stop += length; + if (*stop < 0) { + *stop = (step < 0) ? -1 : 0; + } + } + else if (*stop >= length) { + *stop = (step < 0) ? length - 1 : length; + } + + if (step < 0) { + if (*stop < *start) { + return (*start - *stop - 1) / (-step) + 1; + } + } + else { + if (*start < *stop) { + return (*stop - *start - 1) / step + 1; + } + } + return 0; +} +#endif diff --git a/source/pycompat.h b/source/pycompat.h new file mode 100644 index 0000000..3f5cb26 --- /dev/null +++ b/source/pycompat.h @@ -0,0 +1,40 @@ +#ifndef __PYCOMPAT_H +#define __PYCOMPAT_H + +#ifdef PY_USE_FRAMEWORK +#include +#else +#include +#endif + +// copied from Python 3.8.2 for compatibility with pre-3.6.1 versions because +// doing the right thing with the older slice functions seems difficult... + +#if PY_VERSION_HEX < 0x03060100 +#if PY_MAJOR_VERSION < 3 +/* Assert a build-time dependency, as an expression. + Your compile will fail if the condition isn't true, or can't be evaluated + by the compiler. This can be used in an expression: its value is 0. + Example: + #define foo_to_char(foo) \ + ((char *)(foo) \ + + Py_BUILD_ASSERT_EXPR(offsetof(struct foo, string) == 0)) + Written by Rusty Russell, public domain, http://ccodearchive.net/ */ +#define Py_BUILD_ASSERT_EXPR(cond) \ + (sizeof(char [1 - 2*!(cond)]) - 1) + +#define Py_BUILD_ASSERT(cond) do { \ + (void)Py_BUILD_ASSERT_EXPR(cond); \ + } while(0) +#endif + +int +PySlice_Unpack(PyObject *_r, + Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step); + +Py_ssize_t +PySlice_AdjustIndices(Py_ssize_t length, + Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t step); +#endif + +#endif diff --git a/source/pyprefix.h b/source/pyprefix.h index e9a624c..7da820d 100644 --- a/source/pyprefix.h +++ b/source/pyprefix.h @@ -47,4 +47,27 @@ extern "C" { typedef int Py_ssize_t; #endif +// these are copied from the Python 3.8.2 source because doing the right thing +// with the pre-3.6.1 slice functions seems difficult... + +#if PY_MAJOR_VERSION < 3 +/* Assert a build-time dependency, as an expression. + Your compile will fail if the condition isn't true, or can't be evaluated + by the compiler. This can be used in an expression: its value is 0. + Example: + #define foo_to_char(foo) \ + ((char *)(foo) \ + + Py_BUILD_ASSERT_EXPR(offsetof(struct foo, string) == 0)) + Written by Rusty Russell, public domain, http://ccodearchive.net/ */ +#define Py_BUILD_ASSERT_EXPR(cond) \ + (sizeof(char [1 - 2*!(cond)]) - 1) + +#define Py_BUILD_ASSERT(cond) do { \ + (void)Py_BUILD_ASSERT_EXPR(cond); \ + } while(0) +#endif + +#if PY_VERSION_HEX < 0x03060100 +#endif + #endif From e3dfb2e1879f20093cdd24874f519cdaecf8b4df Mon Sep 17 00:00:00 2001 From: miranda kastemaa Date: Mon, 30 Mar 2020 14:20:32 +0300 Subject: [PATCH 20/40] handle Pd array memory layout using strides --- build/gnumake-mac-gcc.inc | 3 +++ source/pybuffer.cpp | 57 +++++++++++++++------------------------ 2 files changed, 24 insertions(+), 36 deletions(-) diff --git a/build/gnumake-mac-gcc.inc b/build/gnumake-mac-gcc.inc index dadf7ce..3bd383e 100644 --- a/build/gnumake-mac-gcc.inc +++ b/build/gnumake-mac-gcc.inc @@ -74,3 +74,6 @@ endif endif endif + +#DEBUG = 1 +#DFLAGS += -DFLEXT_DEBUG diff --git a/source/pybuffer.cpp b/source/pybuffer.cpp index d19e817..c0e6499 100644 --- a/source/pybuffer.cpp +++ b/source/pybuffer.cpp @@ -228,55 +228,38 @@ static Py_ssize_t buffer_charbuffer(PyObject *obj, Py_ssize_t segment, } static int buffer_getbuffer(PyObject *obj, Py_buffer *view, int flags) { - if(flags & PyBUF_INDIRECT) { - PyErr_SetString(PyExc_BufferError, "PyBUF_INDIRECT not supported"); - view->obj = NULL; - return -1; - } - - if(flags & PyBUF_STRIDES) { - PyErr_SetString(PyExc_BufferError, "PyBUF_STRIDES not supported"); - view->obj = NULL; - return -1; - } - - if(flags & PyBUF_ND) { - PyErr_SetString(PyExc_BufferError, "PyBUF_ND not supported"); - view->obj = NULL; - return -1; - } + pySamplebuffer *self = reinterpret_cast(obj); + flext::buffer *b = self->buf; + const Py_ssize_t len = b->Channels()*b->Frames()*sizeof(t_sample); - if(flags & PyBUF_F_CONTIGUOUS) { - PyErr_SetString(PyExc_BufferError, "PyBUF_F_CONTIGUOUS not supported"); + if(!(flags & PyBUF_STRIDES)) { view->obj = NULL; + PyErr_SetString(PyExc_BufferError, "Buffer requires support for strides"); return -1; } - pySamplebuffer *self = reinterpret_cast(obj); - flext::buffer *b = self->buf; + std::pair *shape_strides = new std::pair(); + shape_strides->first = b->Channels() * b->Frames(); + shape_strides->second = sizeof(FLEXT_ARRAYTYPE); - view->buf = (void *)b->Data(); - Py_INCREF(obj); + view->buf = (void *) b->Data(); view->obj = obj; - view->len = b->Channels()*b->Frames()*sizeof(t_sample); + view->len = len; view->readonly = false; - view->itemsize = 1; - view->format = NULL; + view->itemsize = sizeof(t_sample); + view->format = (flags & PyBUF_FORMAT) ? (char *) "B" : NULL; view->ndim = 1; - view->shape = NULL; - view->strides = NULL; + view->shape = &shape_strides->first; + view->strides = &shape_strides->second; view->suboffsets = NULL; - view->internal = NULL; - - if(flags & PyBUF_FORMAT) { - view->format = "B"; - } + view->internal = (void *) shape_strides; + Py_INCREF(self); return 0; } static void buffer_releasebuffer(PyObject *obj, Py_buffer *view) { - // nothing to do here + delete (std::pair *) view->internal; } static PyBufferProcs buffer_as_buffer = { @@ -339,12 +322,14 @@ PyObject *arrayfrombuffer(PyObject *buf,int c,int n) arr = (PyObject *)NA_NewAllFromBuffer(c == 1?1:2,shape,numtype,buf,0,0,NA_ByteOrder(),1,1); #else Py_buffer view; - int err = PyObject_GetBuffer(buf, &view, PyBUF_SIMPLE | PyBUF_WRITABLE); + int err = PyObject_GetBuffer(buf, &view, PyBUF_WRITABLE | PyBUF_C_CONTIGUOUS); if(!err) { FLEXT_ASSERT(view.len <= n*c*sizeof(t_sample)); // Py_INCREF(buf); // ATTENTION... this won't be released any more!! # ifdef PY_NUMPY - arr = PyArray_NewFromDescr(&PyArray_Type,PyArray_DescrNewFromType(numtype),c == 1?1:2,shape,0,(char *)view.buf,NPY_WRITEABLE|NPY_C_CONTIGUOUS,NULL); + npy_intp strides[2] = {*view.strides, 0}; + arr = PyArray_NewFromDescr(&PyArray_Type, PyArray_DescrNewFromType(numtype), + c == 1 ? 1 : 2, shape, strides, (char *) view.buf, NPY_ARRAY_WRITEABLE | NPY_ARRAY_C_CONTIGUOUS, NULL); # else arr = PyArray_FromDimsAndData(c == 1?1:2,shape,numtype,(char *)view.buf); # endif From ec04393dc13c5ef283988b90a0729ba00bbc5e71 Mon Sep 17 00:00:00 2001 From: miranda kastemaa Date: Mon, 30 Mar 2020 14:41:20 +0300 Subject: [PATCH 21/40] fix numpy buffer format --- source/pybuffer.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/source/pybuffer.cpp b/source/pybuffer.cpp index c0e6499..92770a0 100644 --- a/source/pybuffer.cpp +++ b/source/pybuffer.cpp @@ -30,6 +30,11 @@ inline bool arrsupport() { return numtype != tAny; } #else # if defined(PY_NUMPY) # include +# if _FLEXT_NEED_SAMPLE_CONV +# define PY_NUMPY_BUFFER_FORMAT "f" +# else +# define PY_NUMPY_BUFFER_FORMAT "d" +# endif # else # ifdef PY_USE_FRAMEWORK # include @@ -247,7 +252,7 @@ static int buffer_getbuffer(PyObject *obj, Py_buffer *view, int flags) { view->len = len; view->readonly = false; view->itemsize = sizeof(t_sample); - view->format = (flags & PyBUF_FORMAT) ? (char *) "B" : NULL; + view->format = (flags & PyBUF_FORMAT) ? (char *) PY_NUMPY_BUFFER_FORMAT : NULL; view->ndim = 1; view->shape = &shape_strides->first; view->strides = &shape_strides->second; @@ -571,7 +576,6 @@ static int buffer_ass_subscript(PyObject *s, PyObject *item, PyObject *value) const int c = self->buf->Channels(); PyArrayObject *out = (PyArrayObject *) PyArray_ContiguousFromObject(value, numtype, 1, 2); - const t_sample *src = (t_sample *) out->data; if(!out) { // exception already set @@ -580,6 +584,8 @@ static int buffer_ass_subscript(PyObject *s, PyObject *item, PyObject *value) PyErr_SetString(PyExc_NotImplementedError, "Multiple dimensions not supported yet"); ret = -1; } else { + const t_sample *src = (t_sample *) out->data; + Py_ssize_t ilow, ihigh, istep; if(PySlice_Unpack(item, &ilow, &ihigh, &istep) < 0) { From e6782e7f7ecd38cf46d3c601a66ec19aacf21251 Mon Sep 17 00:00:00 2001 From: miranda kastemaa Date: Tue, 31 Mar 2020 14:34:00 +0300 Subject: [PATCH 22/40] port sig.py to python3 and fix dsp buffer handling --- scripts/sig.py | 18 ++++++++++-------- source/pybuffer.cpp | 27 ++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/scripts/sig.py b/scripts/sig.py index 7009cd8..075721f 100644 --- a/scripts/sig.py +++ b/scripts/sig.py @@ -11,15 +11,17 @@ It will probably once be replaced by Numeric(3) """ +from __future__ import print_function + try: import pyext except: - print "ERROR: This script must be loaded by the PD/Max py/pyext external" + print("ERROR: This script must be loaded by the PD/Max py/pyext external") try: import psyco psyco.full() - print "Using JIT compilation" + print("Using JIT compilation") except: # don't care pass @@ -29,7 +31,7 @@ try: import numpy as N except: - print "Failed importing numpy module:",sys.exc_value + print("Failed importing numpy module:",sys.exc_value) class gain(pyext._class): @@ -40,10 +42,10 @@ def __init__(self): def _signal(self): # Multiply input vector by gain and copy to output - try: - self._outvec(0)[:] = self._invec(0)*self.gain - except: - pass + try: + self._outvec(0)[:] = self._invec(0)*self.gain + except: + pass class gain2(pyext._class): @@ -54,7 +56,7 @@ def __init__(self): def _dsp(self): if not self._arraysupport(): - print "No DSP support" + print("No DSP support") return False # cache vectors in this scope diff --git a/source/pybuffer.cpp b/source/pybuffer.cpp index 92770a0..463a6fd 100644 --- a/source/pybuffer.cpp +++ b/source/pybuffer.cpp @@ -239,7 +239,13 @@ static int buffer_getbuffer(PyObject *obj, Py_buffer *view, int flags) { if(!(flags & PyBUF_STRIDES)) { view->obj = NULL; - PyErr_SetString(PyExc_BufferError, "Buffer requires support for strides"); + PyErr_SetString(PyExc_BufferError, "PyBUF_STRIDES is required"); + return -1; + } + + if(!(flags & PyBUF_FORMAT)) { + view->obj = NULL; + PyErr_SetString(PyExc_BufferError, "PyBUF_FORMAT is required"); return -1; } @@ -327,12 +333,27 @@ PyObject *arrayfrombuffer(PyObject *buf,int c,int n) arr = (PyObject *)NA_NewAllFromBuffer(c == 1?1:2,shape,numtype,buf,0,0,NA_ByteOrder(),1,1); #else Py_buffer view; - int err = PyObject_GetBuffer(buf, &view, PyBUF_WRITABLE | PyBUF_C_CONTIGUOUS); + int err = PyObject_GetBuffer(buf, &view, PyBUF_WRITABLE | PyBUF_FORMAT | PyBUF_STRIDES); if(!err) { FLEXT_ASSERT(view.len <= n*c*sizeof(t_sample)); // Py_INCREF(buf); // ATTENTION... this won't be released any more!! # ifdef PY_NUMPY - npy_intp strides[2] = {*view.strides, 0}; + // the results of PyObject_GetBuffer() differ a bit depending on + // whether we're dealing with a pySamplebuffer (Pd array) or a numpy + // array (Pd signal). + // + // for pySamplebuffer, we get stride information that must be used + // to correctly deal with float32 Pd. + // + // for numpy arrays, we get strides=1 (due to format="B"), which + // breaks PyArray_NewFromDescr(), so instead we pass strides=NULL. + bool use_strides = pySamplebuffer_Check(buf); + npy_intp strides_arr[2] = {0, 0}; + npy_intp *strides = NULL; + if(use_strides) { + strides_arr[0] = *view.strides; + strides = strides_arr; + } arr = PyArray_NewFromDescr(&PyArray_Type, PyArray_DescrNewFromType(numtype), c == 1 ? 1 : 2, shape, strides, (char *) view.buf, NPY_ARRAY_WRITEABLE | NPY_ARRAY_C_CONTIGUOUS, NULL); # else From aaaf6c1e20cf87dc0258b2b13e9ba91596515153 Mon Sep 17 00:00:00 2001 From: miranda kastemaa Date: Tue, 31 Mar 2020 15:20:09 +0300 Subject: [PATCH 23/40] port sendrecv.py to python3 --- scripts/sendrecv.py | 220 ++++++++++++++++++++++---------------------- 1 file changed, 111 insertions(+), 109 deletions(-) diff --git a/scripts/sendrecv.py b/scripts/sendrecv.py index 9d873ba..0c58a5e 100644 --- a/scripts/sendrecv.py +++ b/scripts/sendrecv.py @@ -18,10 +18,12 @@ """ +from __future__ import print_function + try: - import pyext + import pyext except: - print "ERROR: This script must be loaded by the PD/Max pyext external" + print("ERROR: This script must be loaded by the PD/Max pyext external") from time import sleep @@ -29,92 +31,92 @@ ################################################################# def recv_gl(arg): - """This is a global receive function, it has no access to class members.""" - print "GLOBAL",arg + """This is a global receive function, it has no access to class members.""" + print("GLOBAL",arg) class ex1(pyext._class): - """Example of a class which receives and sends messages + """Example of a class which receives and sends messages - It has two creation arguments: a receiver and a sender name. - There are no inlets and outlets. - Python functions (one global function, one class method) are bound to PD's or Max/MSP's receive symbols. - The class method sends the received messages out again. - """ + It has two creation arguments: a receiver and a sender name. + There are no inlets and outlets. + Python functions (one global function, one class method) are bound to PD's or Max/MSP's receive symbols. + The class method sends the received messages out again. + """ - # no inlets and outlets - _inlets=1 - _outlets=0 + # no inlets and outlets + _inlets=1 + _outlets=0 - recvname="" - sendname="" + recvname="" + sendname="" - def recv(self,*arg): - """This is a class-local receive function, which has access to class members.""" + def recv(self,*arg): + """This is a class-local receive function, which has access to class members.""" - # print some stuff - print "CLASS",self.recvname,arg + # print some stuff + print("CLASS",self.recvname,arg) - # send data to specified send address - self._send(self.sendname,arg) + # send data to specified send address + self._send(self.sendname,arg) - def __init__(self,*args): - """Class constructor""" + def __init__(self,*args): + """Class constructor""" - # store sender/receiver names - if len(args) >= 1: self.recvname = args[0] - if len(args) >= 2: self.sendname = args[1] + # store sender/receiver names + if len(args) >= 1: self.recvname = args[0] + if len(args) >= 2: self.sendname = args[1] - self.bind_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 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 unbind_1(self): + self._unbind(self.recvname,self.recv) + self._unbind(self.recvname,recv_gl) - def __del__(self): - """Class destructor""" + def __del__(self): + """Class destructor""" - # unbinding is automatically done at destruction - pass + # unbinding is automatically done at destruction + pass ################################################################# class ex2(pyext._class): - """Example of a class which receives a message and forwards it to an outlet + """Example of a class which receives a message and forwards it to an outlet - It has one creation argument: the receiver name. - """ + It has one creation argument: the receiver name. + """ - # define inlets and outlets - _inlets=0 - _outlets=1 + # define inlets and outlets + _inlets=0 + _outlets=1 - recvname="" + recvname="" - def recv(self,*arg): - """This is a class-local receive function""" + def recv(self,*arg): + """This is a class-local receive function""" - # send received data to outlet - self._outlet(1,arg) + # send received data to outlet + self._outlet(1,arg) - def __init__(self,rname): - """Class constructor""" + def __init__(self,rname): + """Class constructor""" - # store receiver names - self.recvname = rname + # store receiver names + self.recvname = rname - # bind function to receiver name - self._bind(self.recvname,self.recv) + # bind function to receiver name + self._bind(self.recvname,self.recv) ################################################################# @@ -124,57 +126,57 @@ def __init__(self,rname): from random import random,randint class ex3(pyext._class): - """Example of a class which does some object manipulation by scripting""" - - - # define inlets and outlets - _inlets=1 - _outlets=0 - - def __init__(self): - """Class constructor""" - - # called scripting method should run on its own thread - if self._isthreaded: - print "Threading is on" - self._detach(1) - - def bang_1(self): - """Do some scripting - PD only!""" - - num = 12 # number of objects - ori = complex(150,180) # origin - rad = 100 # radius - l = range(num) # initialize list - - # make flower - self._tocanvas("obj",ori.real,ori.imag,"bng",20,250,50,0,"empty","yeah","empty",0,-6,64,8,-24198,-1,-1) - for i in xrange(num): - l[i] = ori+rad*exp(complex(0,i*2*pi/num)) - self._tocanvas("obj",l[i].real,l[i].imag,"bng",15,250,50,0,"empty","yeah"+str(i),"empty",0,-6,64,8,0,-1,-1) - self._tocanvas("connect",6,0,7+i,0) - - # blink - for i in range(10): - self._send("yeah","bang") - sleep(1./(i+1)) - - # move objects around - for i in xrange(200): - ix = randint(0,num-1) - l[ix] = ori+rad*complex(2*random()-1,2*random()-1) - self._send("yeah"+str(ix),"pos",l[ix].real,l[ix].imag) - sleep(0.02) - - # now delete - # this is not well-done... from time to time an object remains - self._tocanvas("editmode",1) - for i in xrange(num): - self._tocanvas("mouse",l[i].real,l[i].imag,0,0) - self._tocanvas("cut") - - self._tocanvas("mouse",ori.real+1,ori.imag+1,0,0) - self._tocanvas("cut") - - self._tocanvas("editmode",0) + """Example of a class which does some object manipulation by scripting""" + + + # define inlets and outlets + _inlets=1 + _outlets=0 + + def __init__(self): + """Class constructor""" + + # called scripting method should run on its own thread + if self._isthreaded: + print("Threading is on") + self._detach(1) + + def bang_1(self): + """Do some scripting - PD only!""" + + num = 12 # number of objects + ori = complex(150,180) # origin + rad = 100 # radius + l = range(num) # initialize list + + # make flower + self._tocanvas("obj",ori.real,ori.imag,"bng",20,250,50,0,"empty","yeah","empty",0,-6,64,8,-24198,-1,-1) + for i in xrange(num): + l[i] = ori+rad*exp(complex(0,i*2*pi/num)) + self._tocanvas("obj",l[i].real,l[i].imag,"bng",15,250,50,0,"empty","yeah"+str(i),"empty",0,-6,64,8,0,-1,-1) + self._tocanvas("connect",6,0,7+i,0) + + # blink + for i in range(10): + self._send("yeah","bang") + sleep(1./(i+1)) + + # move objects around + for i in xrange(200): + ix = randint(0,num-1) + l[ix] = ori+rad*complex(2*random()-1,2*random()-1) + self._send("yeah"+str(ix),"pos",l[ix].real,l[ix].imag) + sleep(0.02) + + # now delete + # this is not well-done... from time to time an object remains + self._tocanvas("editmode",1) + for i in xrange(num): + self._tocanvas("mouse",l[i].real,l[i].imag,0,0) + self._tocanvas("cut") + + self._tocanvas("mouse",ori.real+1,ori.imag+1,0,0) + self._tocanvas("cut") + + self._tocanvas("editmode",0) From e8a95d75049dec3a359be7b00558d9eb60145264 Mon Sep 17 00:00:00 2001 From: miranda kastemaa Date: Tue, 31 Mar 2020 15:36:52 +0300 Subject: [PATCH 24/40] untabify simple.py --- scripts/simple.py | 246 +++++++++++++++++++++++----------------------- 1 file changed, 123 insertions(+), 123 deletions(-) diff --git a/scripts/simple.py b/scripts/simple.py index 3d749d3..9e57f10 100644 --- a/scripts/simple.py +++ b/scripts/simple.py @@ -13,64 +13,64 @@ - Inherit your class from pyext._class - Specfiy the number of inlets and outlets: - Use the class members (variables) _inlets and _outlets - If not given they default to 1 - You can also use class methods with the same names to return the respective number + Use the class members (variables) _inlets and _outlets + If not given they default to 1 + You can also use class methods with the same names to return the respective number - Constructors/Destructors - You can specify an __init__ constructor and/or an __del__ destructor. - The constructor will be called with the object's arguments + You can specify an __init__ constructor and/or an __del__ destructor. + The constructor will be called with the object's arguments - e.g. if your PD or MaxMSP object looks like - [pyext script class arg1 arg2 arg3] + e.g. if your PD or MaxMSP object looks like + [pyext script class arg1 arg2 arg3] - then the __init__(self,*args) function will be called with a tuple argument - args = (arg1,arg2,arg3) - With this syntax, you will have to give at least one argument. - By defining the constructor as __init__(self,*args) you can also initialize - the class without arguments. + then the __init__(self,*args) function will be called with a tuple argument + args = (arg1,arg2,arg3) + With this syntax, you will have to give at least one argument. + By defining the constructor as __init__(self,*args) you can also initialize + the class without arguments. - Methods called by pyext - The general format is 'tag_inlet(self,arg)' resp. 'tag_inlet(self,*args)': - tag is the PD or MaxMSP message header.. either bang, float, list etc. - inlet is the inlet (starting from 1) from which messages are received. - args is a tuple which corresponds to the content of the message. args can be omitted. + The general format is 'tag_inlet(self,arg)' resp. 'tag_inlet(self,*args)': + tag is the PD or MaxMSP message header.. either bang, float, list etc. + inlet is the inlet (starting from 1) from which messages are received. + args is a tuple which corresponds to the content of the message. args can be omitted. - The inlet index can be omitted. The method name then has the format 'tag_(self,inlet,args)'. - Here, the inlet index is a additional parameter to the method + The inlet index can be omitted. The method name then has the format 'tag_(self,inlet,args)'. + Here, the inlet index is a additional parameter to the method - You can also set up methods which react on any message. These have the special forms - _anything_inlet(self,*args) - or - _anything_(self,inlet,*args) + You can also set up methods which react on any message. These have the special forms + _anything_inlet(self,*args) + or + _anything_(self,inlet,*args) - Please see below for examples. + Please see below for examples. - Any return values are ignored - use _outlet (see below). + Any return values are ignored - use _outlet (see below). - Generally, you should avoid method_, method_xx forms for your non-pyext class methods. - Identifiers (variables and functions) with leading underscores are reserved for pyext. + Generally, you should avoid method_, method_xx forms for your non-pyext class methods. + Identifiers (variables and functions) with leading underscores are reserved for pyext. - Send messages to outlets: - Use the inherited _outlet method. - You can either use the form - self._outlet(outlet,arg1,arg2,arg3,arg4) ... where all args are atoms (no sequence types!) - or - self._outlet(outlet,arg) ... where arg is a sequence containing only atoms - - Do not use _outlet inside __init__, since the outlets have not been created at that time. + Use the inherited _outlet method. + You can either use the form + self._outlet(outlet,arg1,arg2,arg3,arg4) ... where all args are atoms (no sequence types!) + or + self._outlet(outlet,arg) ... where arg is a sequence containing only atoms + + Do not use _outlet inside __init__, since the outlets have not been created at that time. - Use pyext functions and methods: - See the __doc__ strings of the pyext module and the pyext._class base class. + See the __doc__ strings of the pyext module and the pyext._class base class. """ from __future__ import print_function try: - import pyext + import pyext except: - print("ERROR: This script must be loaded by the PD/Max pyext external") + print("ERROR: This script must be loaded by the PD/Max pyext external") try: # Python 2 @@ -82,142 +82,142 @@ ################################################################# class ex1(pyext._class): - """Example of a simple class which receives messages and prints to the console""" - - # number of inlets and outlets - _inlets=3 - _outlets=0 + """Example of a simple class which receives messages and prints to the console""" + + # number of inlets and outlets + _inlets=3 + _outlets=0 - # methods for first inlet + # methods for first inlet - def bang_1(self): - print("Bang into first inlet") + def bang_1(self): + print("Bang into first inlet") - def int_1(self,f): - print("Integer",f,"into first inlet") + def int_1(self,f): + print("Integer",f,"into first inlet") - def float_1(self,f): - print("Float",f,"into first inlet") + def float_1(self,f): + print("Float",f,"into first inlet") - def list_1(self,*s): - print("List",s,"into first inlet") + def list_1(self,*s): + print("List",s,"into first inlet") - # methods for second inlet + # methods for second inlet - def hey_2(self): - print("Tag 'hey' into second inlet") + def hey_2(self): + print("Tag 'hey' into second inlet") - def ho_2(self): - print("Tag 'ho' into second inlet") + def ho_2(self): + print("Tag 'ho' into second inlet") - def lets_2(self): - print("Tag 'lets' into second inlet") + def lets_2(self): + print("Tag 'lets' into second inlet") - def go_2(self): - print("Tag 'go' into second inlet") + def go_2(self): + print("Tag 'go' into second inlet") - def _anything_2(self,*args): - print("Some other message into second inlet:",args) + def _anything_2(self,*args): + print("Some other message into second inlet:",args) - # methods for third inlet + # methods for third inlet - def onearg_3(self,a): - print("Tag 'onearg' into third inlet:",a) + def onearg_3(self,a): + print("Tag 'onearg' into third inlet:",a) - def twoargs_3(self,*a): - if len(a) == 2: - print("Tag 'twoargs' into third inlet:",a[0],a[1]) - else: - print("Tag 'twoargs': wrong number of arguments") + def twoargs_3(self,*a): + if len(a) == 2: + print("Tag 'twoargs' into third inlet:",a[0],a[1]) + else: + print("Tag 'twoargs': wrong number of arguments") - def threeargs_3(self,*a): - if len(a) == 3: - print("Tag 'threeargs' into third inlet",a[0],a[1],a[2]) - else: - print("Tag 'threeargs': wrong number of arguments") + def threeargs_3(self,*a): + if len(a) == 3: + print("Tag 'threeargs' into third inlet",a[0],a[1],a[2]) + else: + print("Tag 'threeargs': wrong number of arguments") - def varargs_3(self,*args): - # with *args there can be arguments or not + def varargs_3(self,*args): + # with *args there can be arguments or not - print("Tag 'varargs' into third inlet",args) + print("Tag 'varargs' into third inlet",args) ################################################################# class ex2(pyext._class): - """Example of a simple class which receives messages and writes to outlets""" + """Example of a simple class which receives messages and writes to outlets""" - # number of inlets and outlets - _inlets=3 - _outlets=2 + # number of inlets and outlets + _inlets=3 + _outlets=2 - # methods for all inlets + # methods for all inlets - def hello_(self,n): - print("Tag 'hello' into inlet",n) + def hello_(self,n): + print("Tag 'hello' into inlet",n) - def _anything_(self,n,*args): - print("Message into inlet",n,":",args) + def _anything_(self,n,*args): + print("Message into inlet",n,":",args) - # methods for first inlet + # methods for first inlet - def float_1(self,f): - self._outlet(2,f) + def float_1(self,f): + self._outlet(2,f) - # methods for second inlet + # methods for second inlet - def float_2(self,f): - self._outlet(1,f) + def float_2(self,f): + self._outlet(1,f) ################################################################# # helper function - determine whether argument is a numeric type def isNumber(value): - import types - if type(value) in (float, int, long): - return 1 - else: - return 0 + import types + if type(value) in (float, int, long): + return 1 + else: + return 0 class ex3(pyext._class): - """Example of a simple class doing a typical number addition - - It uses a constructor and a class member as temporary storage. - """ + """Example of a simple class doing a typical number addition + + It uses a constructor and a class member as temporary storage. + """ - # number of inlets and outlets - _inlets=2 - _outlets=1 + # number of inlets and outlets + _inlets=2 + _outlets=1 - # temporary storage - tmp=0 + # temporary storage + tmp=0 - # constructor - def __init__(self,*args): - if len(args) == 1: - if isNumber(args[0]): - self.tmp = args[0] - else: - print("ex3: __init__ has superfluous arguments") + # constructor + def __init__(self,*args): + if len(args) == 1: + if isNumber(args[0]): + self.tmp = args[0] + else: + print("ex3: __init__ has superfluous arguments") - # methods + # methods - def float_1(self,f): - self._outlet(1,self.tmp+f) + def float_1(self,f): + self._outlet(1,self.tmp+f) - def float_2(self,f): - self.tmp = f + def float_2(self,f): + self.tmp = f - # handlers for MaxMSP int type - def int_1(self,f): - self.float_1(f) + # handlers for MaxMSP int type + def int_1(self,f): + self.float_1(f) - def int_2(self,f): - self.float_2(f) + def int_2(self,f): + self.float_2(f) From 36416a5dfc487c39b13bfd51cd1b708fb3dad2ae Mon Sep 17 00:00:00 2001 From: miranda kastemaa Date: Mon, 6 Apr 2020 19:37:09 +0300 Subject: [PATCH 25/40] define pyext class using PyTypeObject --- source/bound.cpp | 12 ++-- source/clmeth.cpp | 175 +++++++++++++++++++--------------------------- source/pyext.cpp | 103 +++++++++++++++++++++------ source/pyext.h | 43 +++++++----- 4 files changed, 182 insertions(+), 151 deletions(-) diff --git a/source/bound.cpp b/source/bound.cpp index 01cf681..4ba7700 100644 --- a/source/bound.cpp +++ b/source/bound.cpp @@ -81,10 +81,10 @@ bool pyext::boundmeth(flext_base *th,t_symbol *sym,int argc,t_atom *argv,void *d return true; } -PyObject *pyext::pyext_bind(PyObject *,PyObject *args) +PyObject *pyext::pyext_bind(PyObject *self, PyObject *args) { - PyObject *self,*meth,*name; - if(!PyArg_ParseTuple(args, "OOO:pyext_bind", &self,&name,&meth)) // borrowed references + PyObject *meth,*name; + if(!PyArg_ParseTuple(args, "OO:pyext_bind",&name,&meth)) // borrowed references post("py/pyext - Wrong arguments!"); else if(!PyCallable_Check(meth)) { post("py/pyext - Wrong argument types!"); @@ -126,10 +126,10 @@ PyObject *pyext::pyext_bind(PyObject *,PyObject *args) return Py_None; } -PyObject *pyext::pyext_unbind(PyObject *,PyObject *args) +PyObject *pyext::pyext_unbind(PyObject *self, PyObject *args) { - PyObject *self,*meth,*name; - if(!PyArg_ParseTuple(args, "OOO:pyext_bind", &self,&name,&meth)) // borrowed references + PyObject *meth,*name; + if(!PyArg_ParseTuple(args, "OO:pyext_bind",&name,&meth)) // borrowed references post("py/pyext - Wrong arguments!"); else if(!PyCallable_Check(meth)) { post("py/pyext - Wrong argument types!"); diff --git a/source/clmeth.cpp b/source/clmeth.cpp index a28ca1d..f59fb4d 100644 --- a/source/clmeth.cpp +++ b/source/clmeth.cpp @@ -63,33 +63,8 @@ const char *pyext::pyext_doc = #endif ; -/* -PyObject* pyext::pyext__init__(PyObject *,PyObject *args) -{ -// post("pyext.__init__ called"); - - Py_INCREF(Py_None); - return Py_None; -} - -PyObject* pyext::pyext__del__(PyObject *,PyObject *args) -{ -// post("pyext.__del__ called"); - - Py_INCREF(Py_None); - return Py_None; -} -*/ - -PyObject* pyext::pyext__str__(PyObject *,PyObject *args) -{ - PyObject *self; - if(!PyArg_ParseTuple(args, "O:pyext__str__",&self)) { - // handle error - ERRINTERNAL(); - return NULL; - } - +PyObject* pyext::pyext__str__(PyObject *self, PyObject *args) +{ return #if PY_MAJOR_VERSION < 3 PyString_FromFormat @@ -99,16 +74,17 @@ PyObject* pyext::pyext__str__(PyObject *,PyObject *args) ("", self); } -PyObject* pyext::pyext_setattr(PyObject *,PyObject *args) +PyObject* pyext::pyext_setattr(PyObject *self, PyObject *args) { - PyObject *self,*name,*val; - if(!PyArg_ParseTuple(args, "OOO:pyext_setattr", &self,&name,&val)) + PyObject *name,*val; + if(!PyArg_ParseTuple(args, "OO:pyext_setattr",&name,&val)) { // handle error ERRINTERNAL(); return NULL; } - + post("setattr %s", PyUnicode_AsUTF8(name)); + bool handled = false; /* @@ -124,7 +100,7 @@ PyObject* pyext::pyext_setattr(PyObject *,PyObject *args) #if PY_MAJOR_VERSION < 3 PyDict_SetItem(self, name, val) #else - PyObject_SetAttr(self, name, val) + PyObject_GenericSetAttr(self, name, val) #endif < 0 ) { @@ -137,21 +113,21 @@ PyObject* pyext::pyext_setattr(PyObject *,PyObject *args) return Py_None; } -PyObject* pyext::pyext_getattr(PyObject *,PyObject *args) +PyObject* pyext::pyext_getattr(PyObject *self, PyObject *args) { - PyObject *self, *name, *ret = NULL; - if(!PyArg_ParseTuple(args, "OO:pyext_getattr", &self, &name)) + PyObject *name, *ret = NULL; + if(!PyArg_ParseTuple(args, "O:pyext_getattr", &name)) { // handle error ERRINTERNAL(); return NULL; } + post("getattr %s", PyUnicode_AsUTF8(name)); #if PY_MAJOR_VERSION < 3 if(PyString_Check(name)) #else if(PyUnicode_Check(name)) - #endif { #if PY_MAJOR_VERSION < 3 @@ -198,7 +174,7 @@ PyObject* pyext::pyext_getattr(PyObject *,PyObject *args) } //! Send message to outlet -PyObject *pyext::pyext_outlet(PyObject *,PyObject *args) +PyObject *pyext::pyext_outlet(PyObject *self, PyObject *args) { bool ok = false; @@ -208,12 +184,11 @@ PyObject *pyext::pyext_outlet(PyObject *,PyObject *args) int sz = PyTuple_GET_SIZE(args); // borrowed references! - PyObject *self,*outl; + PyObject *outl; if( - sz >= 2 && - (self = PyTuple_GET_ITEM(args,0)) != NULL && - (outl = PyTuple_GET_ITEM(args,1)) != NULL && + sz >= 1 && + (outl = PyTuple_GET_ITEM(args,0)) != NULL && #if PY_MAJOR_VERSION < 3 PyInt_Check(outl) #else @@ -228,8 +203,8 @@ PyObject *pyext::pyext_outlet(PyObject *,PyObject *args) PyObject *val; #if 0 - if(sz == 3) { - val = PyTuple_GET_ITEM(args,2); // borrow reference + if(sz == 2) { + val = PyTuple_GET_ITEM(args,1); // borrow reference Py_INCREF(val); tp = PySequence_Check(val); } @@ -237,14 +212,14 @@ PyObject *pyext::pyext_outlet(PyObject *,PyObject *args) tp = false; if(!tp) - val = PySequence_GetSlice(args,2,sz); // new ref + val = PySequence_GetSlice(args,1,sz); // new ref #else - if(sz == 3) { - val = PyTuple_GET_ITEM(args,2); // borrow reference + if(sz == 2) { + val = PyTuple_GET_ITEM(args,1); // borrow reference Py_INCREF(val); } else - val = PyTuple_GetSlice(args,2,sz); // new ref + val = PyTuple_GetSlice(args,1,sz); // new ref #endif int o; @@ -279,12 +254,11 @@ PyObject *pyext::pyext_outlet(PyObject *,PyObject *args) #ifdef FLEXT_THREADS //! Detach threads -PyObject *pyext::pyext_detach(PyObject *,PyObject *args) +PyObject *pyext::pyext_detach(PyObject *self, PyObject *args) { - PyObject *self; int val; if( - !PyArg_ParseTuple(args, "Oi:pyext_detach",&self,&val) + !PyArg_ParseTuple(args, "i:pyext_detach",&val) ) { // handle error PyErr_SetString(PyExc_SyntaxError,"pyext - Syntax: _detach(self,[0/1/2])"); @@ -309,12 +283,11 @@ PyObject *pyext::pyext_detach(PyObject *,PyObject *args) } //! Stop running threads -PyObject *pyext::pyext_stop(PyObject *,PyObject *args) +PyObject *pyext::pyext_stop(PyObject *self, PyObject *args) { - PyObject *self; int val = -1; if( - !PyArg_ParseTuple(args, "O|i:pyext_stop",&self,&val) + !PyArg_ParseTuple(args, "|i:pyext_stop",&val) ) { // handle error PyErr_SetString(PyExc_SyntaxError,"pyext - Syntax: _stop(self,{wait time})"); @@ -347,73 +320,66 @@ PyObject *pyext::pyext_stop(PyObject *,PyObject *args) #if FLEXT_SYS == FLEXT_SYS_PD //! Send message to canvas -PyObject *pyext::pyext_tocanvas(PyObject *,PyObject *args) +PyObject *pyext::pyext_tocanvas(PyObject *self, PyObject *args) { FLEXT_ASSERT(PyTuple_Check(args)); int sz = PyTuple_GET_SIZE(args); bool ok = false; - PyObject *self; // borrowed ref - if( - sz >= 1 && - (self = PyTuple_GET_ITEM(args,0)) != NULL - ) { - pyext *ext = GetThis(self); - if(!ext) { - PyErr_SetString(PyExc_RuntimeError,"pyext - _tocanvas: instance not associated with pd object"); - return NULL; - } - - PyObject *val; - - bool tp = - sz == 2 && - PySequence_Check( - val = PyTuple_GET_ITEM(args,1) // borrowed ref - ); - - if(!tp) - val = PyTuple_GetSlice(args,1,sz); // new ref + pyext *ext = GetThis(self); + if(!ext) { + PyErr_SetString(PyExc_RuntimeError,"pyext - _tocanvas: instance not associated with pd object"); + return NULL; + } - flext::AtomListStatic<16> lst; - const t_symbol *sym = GetPyArgs(lst,val); - if(sym) { - t_glist *gl = ext->thisCanvas(); - if(gl) { - // \TODO find a flext-based non-locking method - sys_lock(); - pd_forwardmess((t_class **)gl,lst.Count(),lst.Atoms()); - sys_unlock(); - } + PyObject *val; + + bool tp = + sz == 2 && + PySequence_Check( + val = PyTuple_GET_ITEM(args,0) // borrowed ref + ); + + if(!tp) + val = PyTuple_GetSlice(args,0,sz); // new ref + + flext::AtomListStatic<16> lst; + const t_symbol *sym = GetPyArgs(lst,val); + if(sym) { + t_glist *gl = ext->thisCanvas(); + if(gl) { + // \TODO find a flext-based non-locking method + sys_lock(); + pd_forwardmess((t_class **)gl,lst.Count(),lst.Atoms()); + sys_unlock(); + } #ifdef FLEXT_DEBUG - else - post("pyext - no parent canvas?!"); + else + post("pyext - no parent canvas?!"); #endif - ok = true; - } - else - post("py/pyext - No data to send"); - - if(!tp) Py_DECREF(val); + ok = true; } + else + post("py/pyext - No data to send"); - if(!ok) { - PyErr_SetString(PyExc_SyntaxError,"pyext - Syntax: _tocanvas(self,args...)"); - return NULL; - } + if(!tp) Py_DECREF(val); - Py_INCREF(Py_None); - return Py_None; +if(!ok) { + PyErr_SetString(PyExc_SyntaxError,"pyext - Syntax: _tocanvas(self,args...)"); + return NULL; + } + +Py_INCREF(Py_None); +return Py_None; } #endif -PyObject *pyext::pyext_invec(PyObject *,PyObject *args) +PyObject *pyext::pyext_invec(PyObject *self, PyObject *args) { - PyObject *self; int val = -1; if( - !PyArg_ParseTuple(args, "O|i:pyext_invec",&self,&val) + !PyArg_ParseTuple(args, "|i:pyext_invec",&val) ) { // handle error PyErr_SetString(PyExc_SyntaxError,"pyext - Syntax: _invec(self,inlet)"); @@ -439,12 +405,11 @@ PyObject *pyext::pyext_invec(PyObject *,PyObject *args) return Py_None; } -PyObject *pyext::pyext_outvec(PyObject *,PyObject *args) +PyObject *pyext::pyext_outvec(PyObject *self, PyObject *args) { - PyObject *self; int val = -1; if( - !PyArg_ParseTuple(args, "O|i:pyext_outvec",&self,&val) + !PyArg_ParseTuple(args, "|i:pyext_outvec",&val) ) { // handle error PyErr_SetString(PyExc_SyntaxError,"pyext - Syntax: _outvec(self,inlet)"); diff --git a/source/pyext.cpp b/source/pyext.cpp index 6e9636b..c8756ec 100644 --- a/source/pyext.cpp +++ b/source/pyext.cpp @@ -9,6 +9,14 @@ WARRANTIES, see the file, "license.txt," in this distribution. #include "pyext.h" #include +// undefine PD's T_OBJECT to avoid conflict with Python's +#undef T_OBJECT + +#ifdef PY_USE_FRAMEWORK +#include "Python/structmember.h" +#else +#include "structmember.h" +#endif FLEXT_LIB_V("pyext pyext. pyx pyx.",pyext) @@ -47,6 +55,13 @@ void pyext::Setup(t_classid c) // ---------------------------------------------------- // register/initialize pyext base class along with module + + if(PyType_Ready(&pyPyext_Type) < 0) { + PyErr_Print(); + return; + } + + /* class_dict = PyDict_New(); PyObject *className; #if PY_MAJOR_VERSION < 3 @@ -88,8 +103,10 @@ void pyext::Setup(t_classid c) // make pyext functions available in class scope PyDict_Merge(class_dict,module_dict,0); #endif + */ + // after merge so that it's not in class_dict as well... - PyDict_SetItemString(module_dict, PYEXT_CLASS,class_obj); // increases class_obj ref count by 1 + PyDict_SetItemString(module_dict, PYEXT_CLASS, (PyObject *) &pyPyext_Type); // increases class_obj ref count by 1 PyObject *str; #if PY_MAJOR_VERSION < 3 @@ -97,12 +114,13 @@ void pyext::Setup(t_classid c) #else str = PyUnicode_FromString(pyext_doc); #endif - PyDict_SetItemString(class_dict, "__doc__", str); + //PyDict_SetItemString(class_dict, "__doc__", str); } pyext *pyext::GetThis(PyObject *self) { PyObject *th = PyObject_GetAttrString(self,"_this"); + if(th) { pyext *ret = static_cast(PyLong_AsVoidPtr(th)); Py_DECREF(th); @@ -127,9 +145,6 @@ void pyext::ClearThis() FLEXT_ASSERT(ret != -1); } -PyObject *pyext::class_obj = NULL; -PyObject *pyext::class_dict = NULL; - pyext::pyext(int argc,const t_atom *argv,bool sig): methname(NULL), pyobj(NULL), @@ -216,7 +231,6 @@ bool pyext::Init() if(methname) { MakeInstance(); if(pyobj) { - SetThis(); InitInOut(inlets,outlets); } } @@ -287,8 +301,13 @@ bool pyext::DoInit() bool ok = true; SetThis(); + + if(pyobj->ob_type->tp_init(pyobj, pargs, NULL) < 0) { // need subtype + ok = false; + PyErr_Print(); + } - PyObject *init = PyObject_GetAttrString(pyobj,"__init__"); // get ref + /*PyObject *init = PyObject_GetAttrString(pyobj,"__init__"); // get ref if(init) { if(PyMethod_Check(init)) { PyObject *res = PyObject_CallObject(init,pargs); @@ -305,7 +324,7 @@ bool pyext::DoInit() } else // __init__ has not been found - don't care - PyErr_Clear(); + PyErr_Clear();*/ Py_DECREF(pargs); return ok; @@ -370,11 +389,9 @@ bool pyext::InitInOut(int &inl,int &outl) FLEXT_ASSERT(!ret); } -#if PY_MAJOR_VERSION < 3 // __init__ can override the number of inlets and outlets if(!DoInit()) // call __init__ constructor return false; -#endif if(inl < 0) { // get number of inlets @@ -438,25 +455,17 @@ bool pyext::MakeInstance() if(!pref) PyErr_Print(); else { -#if PY_MAJOR_VERSION < 3 - if(PyClass_Check(pref)) { - // make instance, but don't call __init__ - pyobj = PyInstance_NewRaw(pref, NULL); -#else - if(PyObject_IsInstance(pref, (PyObject *)&PyType_Type)) { - // pyobj = PyBaseObject_Type.tp_new() - // TODO: correctly initialize instance - + if(PyType_Check(pref) && PyType_IsSubtype((PyTypeObject *) pref, &pyPyext_Type)) { + PyTypeObject *pytypeobj = (PyTypeObject *) pref; PyObject *pargs = MakePyArgs(NULL, initargs.Count(), initargs.Atoms()); if(!pargs) { PyErr_Print(); } else { - pyobj = PyObject_CallObject(pref, pargs); - + pyobj = pytypeobj->tp_new(pytypeobj, pargs, NULL); + Py_DECREF(pargs); } -#endif if(!pyobj) PyErr_Print(); } else @@ -503,6 +512,7 @@ void pyext::Unload() void pyext::m_get(const t_symbol *s) { + post("m_get %s", GetString(s)); ThrLockSys lock; PyObject *pvar = PyObject_GetAttrString(pyobj,const_cast(GetString(s))); /* fetch bound method */ @@ -531,6 +541,7 @@ void pyext::m_set(int argc,const t_atom *argv) else if(*GetString(argv[0]) == '_') post("%s - set: variables with leading _ are reserved and can't be set",thisName()); else { + post("m_set %s", GetString(argv[0])); char *ch = const_cast(GetString(argv[0])); if(PyObject_HasAttrString(pyobj,ch)) { PyObject *pval = MakePyArgs(NULL,argc-1,argv+1); @@ -710,3 +721,51 @@ void pyext::DumpOut(const t_symbol *sym,int argc,const t_atom *argv) { ToOutAnything(GetOutAttr(),sym?sym:thisTag(),argc,argv); } + +static PyObject *pyext_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + post("pyext_new"); + pyPyext *self = (pyPyext *) type->tp_alloc(type, 0); + + self->this_ptr = 0; + + PyObject *ret = (PyObject *) self; + + if(self) { + PyObject *this_long = kwds != NULL + ? PyDict_GetItemString(kwds, "_pyext_this") + : NULL; + + if(this_long) { + if(!PyLong_Check(this_long)) { + self->this_ptr = PyLong_AsLong(this_long); + } else { + Py_DECREF(self); + ret = NULL; + } + + Py_DECREF(this_long); + } + } else { + ret = NULL; + } + + return (PyObject *) ret; +} + +static PyMemberDef pyPyext_members[] = { + {"_this", T_OBJECT_EX, offsetof(pyPyext, this_ptr), 0, "pointer to pyext object"}, + {NULL} +}; + +PyTypeObject pyPyext_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = PYEXT_MODULE "." PYEXT_CLASS, + .tp_doc = pyext::pyext_doc, + .tp_basicsize = sizeof(pyPyext), + .tp_itemsize = 0, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .tp_new = pyext_new, + .tp_members = pyPyext_members, + .tp_methods = pyext::meth_tbl +}; diff --git a/source/pyext.h b/source/pyext.h index f27b8f3..9fbb1f2 100644 --- a/source/pyext.h +++ b/source/pyext.h @@ -20,30 +20,32 @@ class pyext public: pyext(int argc,const t_atom *argv,bool sig = false); - static PyObject *pyext__str__(PyObject *,PyObject *args); + static PyObject *pyext__str__(PyObject *self, PyObject *args); - static PyObject *pyext_outlet(PyObject *,PyObject *args); + static PyObject *pyext_outlet(PyObject *self, PyObject *args); #if FLEXT_SYS == FLEXT_SYS_PD - static PyObject *pyext_tocanvas(PyObject *,PyObject *args); + static PyObject *pyext_tocanvas(PyObject *self, PyObject *args); #endif - static PyObject *pyext_setattr(PyObject *,PyObject *args); - static PyObject *pyext_getattr(PyObject *,PyObject *args); + static PyObject *pyext_setattr(PyObject *self, PyObject *args); + static PyObject *pyext_getattr(PyObject *self, PyObject *args); - static PyObject *pyext_detach(PyObject *,PyObject *args); - static PyObject *pyext_stop(PyObject *,PyObject *args); - static PyObject *pyext_isthreaded(PyObject *,PyObject *); + static PyObject *pyext_detach(PyObject *self, PyObject *args); + static PyObject *pyext_stop(PyObject *self, PyObject *args); - static PyObject *pyext_inbuf(PyObject *,PyObject *args); - static PyObject *pyext_invec(PyObject *,PyObject *args); - static PyObject *pyext_outbuf(PyObject *,PyObject *args); - static PyObject *pyext_outvec(PyObject *,PyObject *args); + static PyObject *pyext_inbuf(PyObject *self, PyObject *args); + static PyObject *pyext_invec(PyObject *self, PyObject *args); + static PyObject *pyext_outbuf(PyObject *self, PyObject *args); + static PyObject *pyext_outvec(PyObject *self, PyObject *args); int Inlets() const { return inlets; } int Outlets() const { return outlets; } static pyext *GetThis(PyObject *self); + static PyMethodDef attr_tbl[],meth_tbl[]; + static const char *pyext_doc; + protected: virtual bool Init(); @@ -98,13 +100,9 @@ class pyext bool MakeInstance(); bool InitInOut(int &inlets,int &outlets); - static PyObject *class_obj,*class_dict; - static PyMethodDef attr_tbl[],meth_tbl[]; - static const char *pyext_doc; - // -------- bind stuff ------------------ - static PyObject *pyext_bind(PyObject *,PyObject *args); - static PyObject *pyext_unbind(PyObject *,PyObject *args); + static PyObject *pyext_bind(PyObject *self, PyObject *args); + static PyObject *pyext_unbind(PyObject *self, PyObject *args); // --------------------------- @@ -151,4 +149,13 @@ class pyext #endif }; +typedef struct { + PyObject_HEAD + long this_ptr; +} pyPyext; + +PY_EXPORT extern PyTypeObject pyPyext_Type; + +#define pyPyext_Check(op) PyObject_TypeCheck((op), &pyPyext_Type) + #endif From f0a6ee1e13f059db9d52577fbe7b9f178015b9d6 Mon Sep 17 00:00:00 2001 From: miranda kastemaa Date: Tue, 7 Apr 2020 11:28:28 +0300 Subject: [PATCH 26/40] string.swapcase is gone in py3, use capwords as example instead --- pd/methods-1.pd | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/pd/methods-1.pd b/pd/methods-1.pd index 5b85c43..253fa6a 100644 --- a/pd/methods-1.pd +++ b/pd/methods-1.pd @@ -1,24 +1,24 @@ -#N canvas 540 469 734 369 12; -#X obj 16 13 cnv 15 650 40 empty empty py/pyext 10 22 0 24 -260818 --1 0; -#X text 235 32 http://grrrr.org/ext; -#X symbolatom 21 139 10 0 0 0 - - -; -#X symbolatom 25 298 10 0 0 0 - - -; -#X obj 22 179 py .str @py 1; -#X text 145 170 convert the symbol to a Python string; -#X text 35 216 pass it as a true Python object; -#X symbolatom 364 295 10 0 0 0 - - -; -#X text 462 269 use module function; -#X text 23 119 enter some text; -#X text 145 187 using the built-in str function; -#X obj 25 252 pym swapcase; -#X text 63 270 use swapcase method; -#X obj 363 250 py string.swapcase; -#X text 235 16 Python script objects \, (C)2003-2006 Thomas Grill; -#X text 21 73 Py can act on Python objects in an object-oriented manner -; -#X connect 2 0 4 1; -#X connect 4 0 11 1; -#X connect 4 0 13 1; -#X connect 11 0 3 0; -#X connect 13 0 7 0; +#N canvas 540 469 734 369 12; +#X obj 16 13 cnv 15 650 40 empty empty py/pyext 10 22 0 24 -260818 +-1 0; +#X text 235 32 http://grrrr.org/ext; +#X symbolatom 21 139 10 0 0 0 - - -; +#X symbolatom 25 298 10 0 0 0 - - -; +#X obj 22 179 py .str @py 1; +#X text 145 170 convert the symbol to a Python string; +#X text 35 216 pass it as a true Python object; +#X symbolatom 364 295 10 0 0 0 - - -; +#X text 462 269 use module function; +#X text 23 119 enter some text; +#X text 145 187 using the built-in str function; +#X obj 25 252 pym swapcase; +#X text 63 270 use swapcase method; +#X text 235 16 Python script objects \, (C)2003-2006 Thomas Grill; +#X text 21 73 Py can act on Python objects in an object-oriented manner +; +#X obj 363 250 py string.capwords; +#X connect 2 0 4 1; +#X connect 4 0 11 1; +#X connect 4 0 15 1; +#X connect 11 0 3 0; +#X connect 15 0 7 0; From a6e7a7664330278b6e65447d12f6c7ad72cdafb5 Mon Sep 17 00:00:00 2001 From: miranda kastemaa Date: Tue, 7 Apr 2020 12:04:04 +0300 Subject: [PATCH 27/40] compat fixes to examples --- scripts/sendrecv.py | 14 +++++++------- scripts/sig.py | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/sendrecv.py b/scripts/sendrecv.py index 0c58a5e..25596a7 100644 --- a/scripts/sendrecv.py +++ b/scripts/sendrecv.py @@ -58,7 +58,7 @@ def recv(self,*arg): print("CLASS",self.recvname,arg) # send data to specified send address - self._send(self.sendname,arg) + pyext._send(self.sendname,arg) def __init__(self,*args): @@ -147,31 +147,31 @@ def bang_1(self): num = 12 # number of objects ori = complex(150,180) # origin rad = 100 # radius - l = range(num) # initialize list + l = list(range(num)) # initialize list # make flower self._tocanvas("obj",ori.real,ori.imag,"bng",20,250,50,0,"empty","yeah","empty",0,-6,64,8,-24198,-1,-1) - for i in xrange(num): + for i in range(num): l[i] = ori+rad*exp(complex(0,i*2*pi/num)) self._tocanvas("obj",l[i].real,l[i].imag,"bng",15,250,50,0,"empty","yeah"+str(i),"empty",0,-6,64,8,0,-1,-1) self._tocanvas("connect",6,0,7+i,0) # blink for i in range(10): - self._send("yeah","bang") + pyext._send("yeah","bang") sleep(1./(i+1)) # move objects around - for i in xrange(200): + for i in range(200): ix = randint(0,num-1) l[ix] = ori+rad*complex(2*random()-1,2*random()-1) - self._send("yeah"+str(ix),"pos",l[ix].real,l[ix].imag) + pyext._send("yeah"+str(ix),"pos",l[ix].real,l[ix].imag) sleep(0.02) # now delete # this is not well-done... from time to time an object remains self._tocanvas("editmode",1) - for i in xrange(num): + for i in range(num): self._tocanvas("mouse",l[i].real,l[i].imag,0,0) self._tocanvas("cut") diff --git a/scripts/sig.py b/scripts/sig.py index 075721f..a7de91e 100644 --- a/scripts/sig.py +++ b/scripts/sig.py @@ -55,7 +55,7 @@ def __init__(self): self.gain = 0 def _dsp(self): - if not self._arraysupport(): + if not pyext._arraysupport(): print("No DSP support") return False From 27020c42bd216126f047731dff54ed960938d088 Mon Sep 17 00:00:00 2001 From: miranda kastemaa Date: Tue, 7 Apr 2020 12:04:17 +0300 Subject: [PATCH 28/40] cleanup --- source/clmeth.cpp | 2 -- source/pyext.cpp | 66 ++--------------------------------------------- 2 files changed, 2 insertions(+), 66 deletions(-) diff --git a/source/clmeth.cpp b/source/clmeth.cpp index f59fb4d..aacacbf 100644 --- a/source/clmeth.cpp +++ b/source/clmeth.cpp @@ -83,7 +83,6 @@ PyObject* pyext::pyext_setattr(PyObject *self, PyObject *args) ERRINTERNAL(); return NULL; } - post("setattr %s", PyUnicode_AsUTF8(name)); bool handled = false; @@ -122,7 +121,6 @@ PyObject* pyext::pyext_getattr(PyObject *self, PyObject *args) ERRINTERNAL(); return NULL; } - post("getattr %s", PyUnicode_AsUTF8(name)); #if PY_MAJOR_VERSION < 3 if(PyString_Check(name)) diff --git a/source/pyext.cpp b/source/pyext.cpp index c8756ec..fd29ab2 100644 --- a/source/pyext.cpp +++ b/source/pyext.cpp @@ -61,51 +61,12 @@ void pyext::Setup(t_classid c) return; } - /* - class_dict = PyDict_New(); - PyObject *className; -#if PY_MAJOR_VERSION < 3 - className = PyString_FromString(PYEXT_CLASS); -#else - className = PyUnicode_FromString(PYEXT_CLASS); -#endif - PyMethodDef *def; - - // add setattr/getattr to class - for(def = attr_tbl; def->ml_name; def++) { - PyObject *func = PyCFunction_New(def, NULL); - PyDict_SetItemString(class_dict, def->ml_name, func); - Py_DECREF(func); - } - - //class_obj = PyClass_New(NULL, class_dict, className); - PyObject *classBases = PyTuple_New(0); - class_obj = PyObject_CallFunctionObjArgs((PyObject *)&PyType_Type, className, classBases, class_dict, NULL); - Py_DECREF(classBases); - Py_DECREF(className); - - // add methods to class - for (def = meth_tbl; def->ml_name != NULL; def++) { - PyObject *func = PyCFunction_New(def, NULL); -#if PY_MAJOR_VERSION < 3 - PyObject *method = PyMethod_New(func, NULL, class_obj); // increases class_obj ref count by 1 - PyDict_SetItemString(class_dict, def->ml_name, method); -#else - PyObject *method = PyMethod_New(func, class_obj); - PyObject_SetAttrString(class_obj, def->ml_name, method); -#endif - Py_DECREF(func); - Py_DECREF(method); - } - -#if PY_VERSION_HEX >= 0x02020000 +#if 0 && PY_VERSION_HEX >= 0x02020000 // not absolutely necessary, existent in python 2.2 upwards // make pyext functions available in class scope PyDict_Merge(class_dict,module_dict,0); #endif - */ - // after merge so that it's not in class_dict as well... PyDict_SetItemString(module_dict, PYEXT_CLASS, (PyObject *) &pyPyext_Type); // increases class_obj ref count by 1 PyObject *str; @@ -114,7 +75,6 @@ void pyext::Setup(t_classid c) #else str = PyUnicode_FromString(pyext_doc); #endif - //PyDict_SetItemString(class_dict, "__doc__", str); } pyext *pyext::GetThis(PyObject *self) @@ -302,29 +262,10 @@ bool pyext::DoInit() SetThis(); - if(pyobj->ob_type->tp_init(pyobj, pargs, NULL) < 0) { // need subtype + if(pyobj->ob_type->tp_init(pyobj, pargs, NULL) < 0) { ok = false; PyErr_Print(); } - - /*PyObject *init = PyObject_GetAttrString(pyobj,"__init__"); // get ref - if(init) { - if(PyMethod_Check(init)) { - PyObject *res = PyObject_CallObject(init,pargs); - if(!res) { - // exception is set - ok = false; - // we want to know why __init__ failed... - PyErr_Print(); - } - else - Py_DECREF(res); - } - Py_DECREF(init); - } - else - // __init__ has not been found - don't care - PyErr_Clear();*/ Py_DECREF(pargs); return ok; @@ -512,7 +453,6 @@ void pyext::Unload() void pyext::m_get(const t_symbol *s) { - post("m_get %s", GetString(s)); ThrLockSys lock; PyObject *pvar = PyObject_GetAttrString(pyobj,const_cast(GetString(s))); /* fetch bound method */ @@ -541,7 +481,6 @@ void pyext::m_set(int argc,const t_atom *argv) else if(*GetString(argv[0]) == '_') post("%s - set: variables with leading _ are reserved and can't be set",thisName()); else { - post("m_set %s", GetString(argv[0])); char *ch = const_cast(GetString(argv[0])); if(PyObject_HasAttrString(pyobj,ch)) { PyObject *pval = MakePyArgs(NULL,argc-1,argv+1); @@ -724,7 +663,6 @@ void pyext::DumpOut(const t_symbol *sym,int argc,const t_atom *argv) static PyObject *pyext_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { - post("pyext_new"); pyPyext *self = (pyPyext *) type->tp_alloc(type, 0); self->this_ptr = 0; From 5a2bca73edb9d7d6a78811db1e5fde3494b1015a Mon Sep 17 00:00:00 2001 From: miranda kastemaa Date: Tue, 7 Apr 2020 12:37:37 +0300 Subject: [PATCH 29/40] cleanup --- source/clmeth.cpp | 9 --------- source/pybase.h | 2 +- source/pyext.h | 2 +- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/source/clmeth.cpp b/source/clmeth.cpp index aacacbf..a8778f2 100644 --- a/source/clmeth.cpp +++ b/source/clmeth.cpp @@ -36,15 +36,6 @@ PyMethodDef pyext::meth_tbl[] = {NULL, NULL, 0, NULL} /* Sentinel */ }; -PyMethodDef pyext::attr_tbl[] = -{ -#if PY_MAJOR_VERSION < 3 - { "__setattr__", pyext::pyext_setattr, METH_VARARGS,"Set class attribute" }, - { "__getattr__", pyext::pyext_getattr, METH_VARARGS,"Get class attribute" }, -#endif - { NULL, NULL,0,NULL }, -}; - const char *pyext::pyext_doc = "py/pyext - python external object for PD and Max/MSP, (C)2002-2008 Thomas Grill\n" "\n" diff --git a/source/pybase.h b/source/pybase.h index e13d672..bb12b07 100644 --- a/source/pybase.h +++ b/source/pybase.h @@ -131,7 +131,7 @@ class pybase static PyObject *module_obj,*module_dict; static PyObject *builtins_obj,*builtins_dict; - static PyMethodDef func_tbl[],attr_tbl[]; + static PyMethodDef func_tbl[]; #if PY_MAJOR_VERSION >= 3 static PyModuleDef pyext_module_def; #endif diff --git a/source/pyext.h b/source/pyext.h index 9fbb1f2..92e5649 100644 --- a/source/pyext.h +++ b/source/pyext.h @@ -43,7 +43,7 @@ class pyext static pyext *GetThis(PyObject *self); - static PyMethodDef attr_tbl[],meth_tbl[]; + static PyMethodDef meth_tbl[]; static const char *pyext_doc; protected: From 2737539c32f20d9ab3e41f6382296f70530ce7a6 Mon Sep 17 00:00:00 2001 From: miranda kastemaa Date: Tue, 7 Apr 2020 13:51:11 +0300 Subject: [PATCH 30/40] unbreak python 2 --- package.txt | 4 ++-- scripts/tcltk.py | 6 ++++-- source/clmeth.cpp | 12 ++---------- source/pybase.cpp | 3 ++- source/pybuffer.cpp | 11 ++--------- 5 files changed, 12 insertions(+), 24 deletions(-) diff --git a/package.txt b/package.txt index 9002aa8..2a19933 100644 --- a/package.txt +++ b/package.txt @@ -11,6 +11,6 @@ SRCS= \ py.cpp pyext.cpp modmeth.cpp clmeth.cpp \ register.cpp bound.cpp pyargs.cpp \ pysymbol.cpp pybuffer.cpp pybundle.cpp pydsp.cpp \ - pyatom.cpp pybase.cpp pymeth.cpp + pyatom.cpp pybase.cpp pymeth.cpp pycompat.cpp -HDRS= pyprefix.h main.h pyext.h pysymbol.h pybuffer.h pybundle.h pyatom.h pybase.h +HDRS= pyprefix.h main.h pyext.h pysymbol.h pybuffer.h pybundle.h pyatom.h pybase.h pycompat.h diff --git a/scripts/tcltk.py b/scripts/tcltk.py index 0813199..a02b7ef 100644 --- a/scripts/tcltk.py +++ b/scripts/tcltk.py @@ -7,10 +7,12 @@ """This is an example script for showing a nonsense tcl/tk application.""" +from __future__ import print_function + try: import pyext except: - print "ERROR: This script must be loaded by the PD/Max pyext external" + print("ERROR: This script must be loaded by the PD/Max pyext external") from Tkinter import * import random @@ -74,7 +76,7 @@ def __init__(self): self._detach(1) def bang_1(self): - self._priority(-3) + pyext._priority(-3) # display the tcl/tk dialog app = Application(self) app.mainloop() diff --git a/source/clmeth.cpp b/source/clmeth.cpp index a8778f2..312dad8 100644 --- a/source/clmeth.cpp +++ b/source/clmeth.cpp @@ -29,10 +29,8 @@ PyMethodDef pyext::meth_tbl[] = #endif { "_invec", pyext::pyext_invec, METH_VARARGS,"Get input vector" }, { "_outvec", pyext::pyext_outvec, METH_VARARGS,"Get output vector" }, -#if PY_MAJOR_VERSION >= 3 { "__setattr__", pyext::pyext_setattr, METH_VARARGS,"Set class attribute" }, { "__getattr__", pyext::pyext_getattr, METH_VARARGS,"Get class attribute" }, -#endif {NULL, NULL, 0, NULL} /* Sentinel */ }; @@ -75,6 +73,7 @@ PyObject* pyext::pyext_setattr(PyObject *self, PyObject *args) return NULL; } + bool handled = false; /* @@ -86,14 +85,7 @@ PyObject* pyext::pyext_setattr(PyObject *self, PyObject *args) } */ if(!handled) { - if( -#if PY_MAJOR_VERSION < 3 - PyDict_SetItem(self, name, val) -#else - PyObject_GenericSetAttr(self, name, val) -#endif - < 0 - ) { + if(PyObject_GenericSetAttr(self, name, val) < 0) { ERRINTERNAL(); return NULL; } diff --git a/source/pybase.cpp b/source/pybase.cpp index 7a5358a..846d918 100644 --- a/source/pybase.cpp +++ b/source/pybase.cpp @@ -106,7 +106,8 @@ void initbundle(); MOD_INIT(pyext) { - return MOD_SUCCESS_VAL(pybase::pyext_init()); + PyObject *init_ret = pybase::pyext_init(); + return MOD_SUCCESS_VAL(init_ret); } PyObject *pybase::pyext_init() diff --git a/source/pybuffer.cpp b/source/pybuffer.cpp index 463a6fd..9238d02 100644 --- a/source/pybuffer.cpp +++ b/source/pybuffer.cpp @@ -274,15 +274,8 @@ static void buffer_releasebuffer(PyObject *obj, Py_buffer *view) { } static PyBufferProcs buffer_as_buffer = { -#if PY_MAJOR_VERSION < 3 - buffer_readbuffer, - buffer_writebuffer, - buffer_segcount, - buffer_charbuffer -#else - buffer_getbuffer, - buffer_releasebuffer -#endif + .bf_getbuffer = buffer_getbuffer, + .bf_releasebuffer = buffer_releasebuffer }; static Py_ssize_t buffer_length(PyObject *s) From cc2ad778e81ab1ade379f9a94731db3f67eeba96 Mon Sep 17 00:00:00 2001 From: miranda kastemaa Date: Mon, 7 Sep 2020 18:50:33 +0300 Subject: [PATCH 31/40] add _canvas_dir attribute --- source/clmeth.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/source/clmeth.cpp b/source/clmeth.cpp index 312dad8..77f1a73 100644 --- a/source/clmeth.cpp +++ b/source/clmeth.cpp @@ -139,6 +139,16 @@ PyObject* pyext::pyext_getattr(PyObject *self, PyObject *args) ret = Py_False; #endif } + else if(!strcmp(sname, "_canvas_dir")) { + pyext *ext = GetThis(self); + char dir[1024]; + ext->GetCanvasDir(dir, sizeof(dir)); +#if PY_MAJOR_VERSION < 3 + ret = PyString_InternFromString(dir); +#else + ret = PyUnicode_InternFromString(dir); +#endif + } } } From 0e35ed1baa151aebc6e0c5296d558b1e27aa9e1d Mon Sep 17 00:00:00 2001 From: miranda kastemaa Date: Mon, 7 Sep 2020 18:50:53 +0300 Subject: [PATCH 32/40] fix(?) "pyext." behavior --- source/pyext.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/source/pyext.cpp b/source/pyext.cpp index fd29ab2..620424e 100644 --- a/source/pyext.cpp +++ b/source/pyext.cpp @@ -162,7 +162,13 @@ pyext::pyext(int argc,const t_atom *argv,bool sig): PyErr_SetString(PyExc_ValueError,"Invalid module name"); // check for alias creation names - if(dotted) clname = scr; + // ...this seems wrong? + // e.g. "pyext. a.b c" becomes the equivalent of: + // from a.b import a.b + // instead of + // from a.b import c + // so let's disable it for now. + //if(dotted) clname = scr; } Register(GetRegistry(REGNAME)); From 630fcdbbe77c39b745ddc021e0438f5edce34226 Mon Sep 17 00:00:00 2001 From: miranda kastemaa Date: Tue, 10 Nov 2020 16:27:22 +0200 Subject: [PATCH 33/40] make conda build work on linux --- build/config-lnx.def | 10 +++++++++- build/gnumake-lnx-gcc.inc | 15 +++++++++++++++ source/pyext.cpp | 6 +++--- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/build/config-lnx.def b/build/config-lnx.def index 09879a3..cfdbcd6 100644 --- a/build/config-lnx.def +++ b/build/config-lnx.def @@ -1,8 +1,13 @@ # what is the base prefix of the Python installation? +ifdef PY_CONDA_ROOT +PYTHONPREFIX=$(PY_CONDA_ROOT) +else PYTHONPREFIX=/usr +endif # which Python version do you want to compile against? -PYTHONVERSION=2.7 +PY_MAJOR_VERSION=3 +PY_MINOR_VERSION=7 # uncomment if numpy/numarray/numeric support should be compiled in # for info see http://numeric.scipy.org @@ -15,3 +20,6 @@ PY_USE_GIL=1 # use inofficial (pure data) functionality # PY_USE_INOFFICIAL=1 + +# use python with pymalloc (look for "pythonX.Ym" files) +PY_USE_PYMALLOC=1 diff --git a/build/gnumake-lnx-gcc.inc b/build/gnumake-lnx-gcc.inc index 7bbde1f..e9177b4 100644 --- a/build/gnumake-lnx-gcc.inc +++ b/build/gnumake-lnx-gcc.inc @@ -1,5 +1,19 @@ +ifdef PY_USE_PYMALLOC +PYTHONVERSION=$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION)m +else +PYTHONVERSION=$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION) +endif + DEFS += -DPY_EXPORTS + INCPATH += -I$(PYTHONPREFIX)/include/python$(PYTHONVERSION) + +ifdef PY_CONDA_ROOT +LIBPATH += -L$(PY_CONDA_ROOT)/lib +LDFLAGS += -Wl,-R$(PY_CONDA_ROOT)/lib +DEFS += -DPY_INTERPRETER=$(PY_CONDA_ROOT)/bin/python +endif + LIBS += -lpython$(PYTHONVERSION) ifdef PY_NUMARRAY @@ -7,6 +21,7 @@ DEFS += -DPY_NUMARRAY endif ifdef PY_NUMPY DEFS += -DPY_NUMPY +INCPATH += -I$(PYTHONPREFIX)/lib/python$(PY_MAJOR_VERSION).$(PY_MINOR_VERSION)/site-packages/numpy/core/include endif ifdef PY_NUMERIC DEFS += -DPY_NUMERIC diff --git a/source/pyext.cpp b/source/pyext.cpp index 620424e..9b19bd2 100644 --- a/source/pyext.cpp +++ b/source/pyext.cpp @@ -705,11 +705,11 @@ static PyMemberDef pyPyext_members[] = { PyTypeObject pyPyext_Type = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = PYEXT_MODULE "." PYEXT_CLASS, - .tp_doc = pyext::pyext_doc, .tp_basicsize = sizeof(pyPyext), .tp_itemsize = 0, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .tp_new = pyext_new, + .tp_doc = pyext::pyext_doc, + .tp_methods = pyext::meth_tbl, .tp_members = pyPyext_members, - .tp_methods = pyext::meth_tbl + .tp_new = pyext_new }; From bc0708f97d43058446cea0023ea98ccdd9c58fbb Mon Sep 17 00:00:00 2001 From: miranda kastemaa Date: Mon, 30 Nov 2020 17:17:16 +0200 Subject: [PATCH 34/40] fix some uninitialized memory bugs found by valgrind --- source/pybase.cpp | 1 + source/pydsp.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/source/pybase.cpp b/source/pybase.cpp index 846d918..340c920 100644 --- a/source/pybase.cpp +++ b/source/pybase.cpp @@ -291,6 +291,7 @@ FLEXT_LIB_SETUP(py,pybase::lib_setup) pybase::pybase() : module(NULL) , dict(NULL) + , respond(false) #ifdef FLEXT_THREADS , thrcount(0) , shouldexit(false),stoptick(0) diff --git a/source/pydsp.cpp b/source/pydsp.cpp index 7676557..db8a8ad 100644 --- a/source/pydsp.cpp +++ b/source/pydsp.cpp @@ -38,6 +38,7 @@ FLEXT_LIB_DSP_V("pyext~ pyext.~ pyx~ pyx.~",pydsp) pydsp::pydsp(int argc,const t_atom *argv) : pyext(argc,argv,true) , dspfun(NULL),sigfun(NULL) + , buffers(NULL) {} bool pydsp::DoInit() From a55581fdfb4beb8b6ae18fa810b3ea289348307d Mon Sep 17 00:00:00 2001 From: miranda kastemaa Date: Mon, 31 May 2021 15:38:50 +0300 Subject: [PATCH 35/40] add SOPI notice to readme --- readme.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/readme.txt b/readme.txt index fa2d42a..2aebe18 100644 --- a/readme.txt +++ b/readme.txt @@ -1,5 +1,8 @@ py/pyext - python script objects for PD and Max/MSP +This fork by SOPI research group (https://sopi.aalto.fi) implements support for Python 3 (tested with 3.7 and 3.8) as well as Conda Python installations. +It was developed for use with GANSpaceSynth (https://github.com/SopiMlab/GANSpaceSynth) and our Deep Learning with Audio course (https://github.com/SopiMlab/DeepLearningWithAudio). + Copyright (c)2002-2020 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. From 90d8d5539bb39c897b3891e79aee633c5de2b2ce Mon Sep 17 00:00:00 2001 From: SOPI Research Group Date: Tue, 1 Jun 2021 11:07:09 +0300 Subject: [PATCH 36/40] Update readme.txt --- readme.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.txt b/readme.txt index 2aebe18..694f48e 100644 --- a/readme.txt +++ b/readme.txt @@ -1,8 +1,8 @@ -py/pyext - python script objects for PD and Max/MSP - This fork by SOPI research group (https://sopi.aalto.fi) implements support for Python 3 (tested with 3.7 and 3.8) as well as Conda Python installations. It was developed for use with GANSpaceSynth (https://github.com/SopiMlab/GANSpaceSynth) and our Deep Learning with Audio course (https://github.com/SopiMlab/DeepLearningWithAudio). +py/pyext - python script objects for PD and Max/MSP + Copyright (c)2002-2020 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. From d1c67dfd96df011db09ede3533baf9a950cff45f Mon Sep 17 00:00:00 2001 From: miranda kastemaa Date: Tue, 1 Jun 2021 11:32:12 +0300 Subject: [PATCH 37/40] update readme --- readme.txt | 174 +++++------------------------------------------------ 1 file changed, 15 insertions(+), 159 deletions(-) diff --git a/readme.txt b/readme.txt index 2aebe18..7275291 100644 --- a/readme.txt +++ b/readme.txt @@ -1,27 +1,20 @@ py/pyext - python script objects for PD and Max/MSP -This fork by SOPI research group (https://sopi.aalto.fi) implements support for Python 3 (tested with 3.7 and 3.8) as well as Conda Python installations. +This fork by SOPI research group (https://sopi.aalto.fi) implements support for Python 3 as well as Conda Python installations. It was developed for use with GANSpaceSynth (https://github.com/SopiMlab/GANSpaceSynth) and our Deep Learning with Audio course (https://github.com/SopiMlab/DeepLearningWithAudio). +See also the original repository: https://github.com/grrrr/py + Copyright (c)2002-2020 Thomas Grill (gr@grrrr.org) For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "license.txt," in this distribution. -Donations for further development of the package are highly appreciated. -Visit https://www.paypal.com/xclick/business=gr%40grrrr.org&item_name=pyext&no_note=1&tax=0¤cy_code=EUR - ---------------------------------------------------------------------------- You need to have Python installed on your system for the py/pyext external to work. -For Windows pick an up-to-date package from http://www.python.org . -For linux use the package manager. -For OS X keep things as the are - it has Python installed by default. - +We recommend using Conda (https://conda.io). -The py/pyext package should run with Python version >= 2.1. -It has been tested with versions 2.2 to 2.7 - -The default build setting using PY_USE_GIL requires Python version >= 2.3. +SopiMlab/py has been tested with Python 3.7-3.8 and 2.7. Check out the sample patches and scripts @@ -30,11 +23,15 @@ Check out the sample patches and scripts INSTALLATION ============ -PD version >= 0.38 - Add "py" to the Startup items ("binaries to load") and add the folder "scripts" to the pd search path. -PD version < 0.38 - Load it as a library with e.g. "pd -lib py -path scripts" -Under Windows, py/pyext needs at least PD 0.43 +We have detailed tutorials for setting up GANSpaceSynth with Pd in a Conda environment: +https://github.com/SopiMlab/DeepLearningWithAudio/tree/master/utilities/pyext-setup + +Known issues: + +- We haven't been able to build on Windows +- Max/MSP is untested -Max/MSP - Copy py-objectmappings.txt into the init folder and py.mxe (Windows) or py.mxo (OSX) into the externals folder. +Contributions are welcome! ---------------------------------------------------------------------------- @@ -46,22 +43,16 @@ With the pyext you can use python classes to represent full-featured pd/Max mess Multithreading (detached methods) is supported for both objects. You can send messages to named objects or receive (with pyext) with Python methods. - -Known bugs: -- The TCL/TK help patch is not usable under OSX. -- With standard PD 0.37, threaded py scripts will cause "Stack overflows" under some circumstances - -> use PD 0.38 or the devel_0_37 cvs branch instead -- It has been reported that pyext crashes on AMD64 with SSE enabled (for these CPUs, disable the respective compiler flags) - ---------------------------------------------------------------------------- BUILDING from source ==================== You will need the flext C++ layer for PD and Max/MSP externals to compile this. -See http://grrrr.org/ext/flext +See https://github.com/SopiMlab/flext Download and unzip, or git-clone the package. +TODO: Document our changes to the build configuration. For now, the build.py script from https://github.com/SopiMlab/DeepLearningWithAudio/blob/master/utilities/pyext-setup/build.py can give you some hints Pure data - any platform supporting gcc-compatible compilers ------------------------------------------------------------ @@ -108,138 +99,3 @@ to typedef unsigned int UInt32; typedef signed int SInt32; to avoid a compile-time type definition clash. - ----------------------------------------------------------------------------- - -Version history: - -0.2.2: -- FIX: pyext._send(receiversym) sent an empty list to receiversym, now it sends a bang message (equivalent to pyext._send(receiversym,"bang",()) ). Thanks to Yvan Volochine for spotting that. -- FIX: A bug in Pd < 0.43 under Windows causes various errors on loading the modules. This is fixed, py/pyext for Windows now needs Pd 0.43 - -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 float vs. int ... first decision is made by tag, afterwards a conversion is tried -- ADD: pym: object-oriented object... Python methods for any object type -- ADD: py: allow all callables (also object constructors and builtins) -- ADD: py: enable Python built-in functions (like range, str etc.) -- ADD: sequence protocol for symbol type -- FIX: cleanup for outbound messages (e.g. symbol atoms instead of one-element general messages) -- FIX: better exception handling (no longer leaves reference to function object) and cleared misleading error message -- FIX: better definition of output values for atoms, lists and anythings -- FIX: much better detached method handling (one thread for all object instances!) -- ADD: open module file in editor on "edit" message (or shift-click (PD) or double click (Max)) -- FIX: _inlets and _outlets default to 0 if not given -- ADD: enable optimization of Python code in reease build -- CHG: _isthreaded is now a data member instead of a method -- FIX: more safety for calls where association python-pd has already been removed -- ADD: __str__ method for pyext, to enable print self calls -- ADD: enable symbol binding for all callables (not only functions and methods) -- ADD: Buffer.resize(frames,keep=1,zero=1) method -- ADD: py.Bundle class to support flext message bundles -- ADD: enable usage of compiled-only modules (.py[co]) -- ADD: enable usage of module packages (with module/__init__.py[co]) -- ADD: make use of the PyGILState_*() functions -- ADD: always run the Python interpreter in the background (to keep alive Python threads) -- ADD: added PY_USE_INOFFICIAL to enable usage of s_stuff.h PD header, to have access to search and help paths -- ADD: pyext: _init method is now called after __init__ (after inlets/outlets have been created) -- FIX: buffer protocol adapted to Python 2.5 -- FIX: subpath support for modules (tested for Pd) - -0.2.0: -- ADD: handling of Python threads -- FIX: output of single atoms instead of 1-element lists -- ADD: new detach mechanism (call queue) -- ADD: support for Max/MSP @ OSX and Windows -- DEL: eliminated meaningless inchannels and outchannels methods -- ADD: enabled "int"-tags for pyext class functions -- ADD: py: when no function is given on the command line, let it be selected by message tag -- FIX: __init__ wasn't called on reload -- FIX: bound instance methods weren't correctly decref'd -- ADD: Python symbol type -- ADD: _del method in pyext-derived class can be used to clean up things on exit -- FIX: solved py->py messaging problem with lock count instead of message queuing -- ADD: buffer handling with optional numarray support (if present) -- ADD: new objects for dsp processing: pyext~,pyx~,pyext.~,pyx.~ -- FIX: correctly report Python errors while contructing the object - -0.1.4: -- ADD: better (and independent) handling of inlet and outlet count (as class variables or dynamically initialized in __init__) -- FIX: many memory leaks associated to ***GetItem stuff (big thanks to sven!) -- FIX: set "this" memory in object after reloading script -- ADD: _getvalue,_setvalue to access PD values -- FIX: don't shout when Python script returns PyNone -- ADD: alias creation names pyext. and pyx. take the script name also for the class name - -0.1.3: -- FIX: class variables are now set atomic if parameter list has only 1 element -- ADD: introduced shortcut "pyx" for pyext. -- ADD: arguments to the pyext class are now exposed as attributes "args" -- FIX: parameters to Python functions are treated as integers when they can be. -- ADD: inlet and outlet count can be given for pyext, python _inlet and _outlet members are ignored then -- FIX: crash if script or class names are non-strings -- FIX: long multi-line doc strings are now printed correctly -- FIX: message "doc+" for class/instance __doc__ now working -- FIX: improved/debugged handling of reference counts -- FIX: _pyext._send will now send anythings if feasible -- CHANGE: no more finalization - it's really not necessary... -- FIX: calling from unregistered threads (like flext helper thread) now works - -0.1.2: -- CHANGE: updates for flext 0.4.1 - method registering within class scope -- FIX: bugs in bound.cpp (object_free calls) -- FIX: bug with threaded methods along with flext bug fix. -- ADD: map Python threads to system threads -- ADD: shut down the Python interpreter appropriately -- CHANGE: use flext timer and bind functionality -- ADD: attribute functionality -- ADD: dir and dir+ methods for Python dictionary listing -- ADD: get and set methods for Python class attributes - -0.1.1: -- CHANGE: updates for flext 0.4.0 -- FIX: crash when module couldn't be loaded -- FIX: GetBound method (modmeth.cpp, line 138) doesn't exist in flext any more -- FIX: deadlock occured when connecting two py/pyext boxes in non-detached mode -- ADD: current path and path of the canvas is added to the python path -- FIX: path is not added to python path if already included - -0.1.0: -- completely reworked all code -- added class functionality for full-featured objects and renamed the merge to pyext -- enabled threads and made everything thread-safe ... phew! -- using flext 0.3.2 -- pyext now gets full python path -- python's argv[0] is now "py" or "pyext" -- etc.etc. - -0.0.2: -- fixed bug when calling script with no function defined (thanks to Ben Saylor) -- cleaner gcc makefile - -0.0.1: -- using flext 0.2.1 - ---------------------------------------------------------------------------- - -TODO list: - -bugs: -- crashes with long Python printouts - -general: -- Documentation and better example patches -- better error reporting for runtime errors -- we should pre-scan and cache class methods - -features: -- enable multiple interpreters? ( -> not possible within one thread) -- options to fully detach a script (included initialization and finalization) -- stop individual threads -- support named (keyword) arguments (like attributes for messages) - -tests: -- compile-time check for python threading support From eb442e986868ef39d53f22602090fd8686cc7b6b Mon Sep 17 00:00:00 2001 From: miranda kastemaa Date: Tue, 1 Jun 2021 11:38:18 +0300 Subject: [PATCH 38/40] update readme --- readme.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/readme.txt b/readme.txt index 7275291..58317e3 100644 --- a/readme.txt +++ b/readme.txt @@ -50,7 +50,6 @@ BUILDING from source You will need the flext C++ layer for PD and Max/MSP externals to compile this. See https://github.com/SopiMlab/flext -Download and unzip, or git-clone the package. TODO: Document our changes to the build configuration. For now, the build.py script from https://github.com/SopiMlab/DeepLearningWithAudio/blob/master/utilities/pyext-setup/build.py can give you some hints From bff49387f9aa19b13343cee6d41fe66e1f0642aa Mon Sep 17 00:00:00 2001 From: SOPI Research Group Date: Tue, 1 Jun 2021 11:46:30 +0300 Subject: [PATCH 39/40] Update readme.txt --- readme.txt | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/readme.txt b/readme.txt index 58317e3..8e7b939 100644 --- a/readme.txt +++ b/readme.txt @@ -70,31 +70,11 @@ CFLAGS="-mmacosx-version-min=10.9" LDFLAGS="-mmacosx-version-min=10.9" pd/Max - Windows - Microsoft Visual C, Borland C++, MinGW: ---------------------------------------------------------- -Start a command shell with your eventual build environment -(e.g. run vcvars32.bat for Microsoft Visual Studio) - -then run - ..\flext\build.bat -(you would have to substitute ..\flext with the respective path to the flext package) - +Please see further setup instructions at https://github.com/grrrr/py pd/Max - OSX/Linux - GCC: ------------------------- -From a shell run -bash ../flext/build.sh -(you would have to substitute ../flext with the respective path to the flext package) - - +Please see further setup instructions at https://github.com/grrrr/py ---------------------------------------------------------------------------- +Please see further setup instructions at https://github.com/grrrr/py -Python array support for py/pyext@Max/MSP: - -In the Max/MSP SDK change the file -4.5 headers\c74support\max-includes\ext_types.h, line 45 -from - typedef unsigned long UInt32; - typedef signed long SInt32; -to - typedef unsigned int UInt32; - typedef signed int SInt32; -to avoid a compile-time type definition clash. From 35138a0a7e0917849170a5d3003ed60fb80fa450 Mon Sep 17 00:00:00 2001 From: SOPI Research Group Date: Tue, 1 Jun 2021 11:48:08 +0300 Subject: [PATCH 40/40] Update readme.txt --- readme.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.txt b/readme.txt index 8e7b939..83d3626 100644 --- a/readme.txt +++ b/readme.txt @@ -1,6 +1,7 @@ py/pyext - python script objects for PD and Max/MSP This fork by SOPI research group (https://sopi.aalto.fi) implements support for Python 3 as well as Conda Python installations. + It was developed for use with GANSpaceSynth (https://github.com/SopiMlab/GANSpaceSynth) and our Deep Learning with Audio course (https://github.com/SopiMlab/DeepLearningWithAudio). See also the original repository: https://github.com/grrrr/py