From 03c7468fcc51888c8271b904e4d6400ed5c1cbb1 Mon Sep 17 00:00:00 2001 From: Thomas Grill Date: Wed, 7 Mar 2007 13:40:14 +0000 Subject: multiply inlets for py (hot and cold inlets) small optimizations and fixes use PyGILState_\*() functionality (enabled with PY_USE_GIL) updates for DSP processing __str__ method for pyext, to enable print self calls added message bundle functionality (pyext.Bundle class) enable compiled-only scripts (without .py) enable optimization of Python code in reease build let _inlets and _outlets default to 0 fix for numpy some ASSERTs for explicitly created pyext classes (should be runtime checks i guess) open editor for script under OS X fixing numpy initialization quirks enable symbol binding for all callables (not only functions and methods) _isthreaded is now a data member instead of a method fix for gcc4 added pyext._list and pyext._tuple to convert input lists to Python sequence objects enable module packages (module/__init__.py[co]), now also for Max python-like dotted module.function syntax cleaned up float vs. int pyext tags compiler flag to exclude DSP objects some optimizations and py reload fix more safety for calls where association python-pd has already been removed always run Python interpreter in the background svn path=/trunk/; revision=7474 --- externals/grill/py/build/config-lnx.def | 3 + externals/grill/py/build/config-mac.def | 3 + externals/grill/py/build/config-win.def | 3 + externals/grill/py/build/gnumake-lnx-gcc.inc | 3 + externals/grill/py/build/gnumake-mac-gcc.inc | 3 + externals/grill/py/build/gnumake-win-cygwin.inc | 3 + externals/grill/py/build/nmake-win-msvc.inc | 3 + externals/grill/py/package.txt | 32 +- externals/grill/py/py.vcproj | 6 +- externals/grill/py/py.xcodeproj/project.pbxproj | 14 +- externals/grill/py/readme.txt | 8 +- externals/grill/py/scripts/buffer.py | 120 +- externals/grill/py/scripts/sig.py | 200 +-- externals/grill/py/scripts/simple.py | 4 +- externals/grill/py/source/bound.cpp | 2 +- externals/grill/py/source/clmeth.cpp | 4 +- externals/grill/py/source/modmeth.cpp | 4 +- externals/grill/py/source/py.cpp | 12 +- externals/grill/py/source/pyatom.cpp | 122 +- externals/grill/py/source/pyatom.h | 38 +- externals/grill/py/source/pybase.cpp | 1793 ++++++++++++----------- externals/grill/py/source/pybase.h | 479 +++--- externals/grill/py/source/pybundle.cpp | 460 +++--- externals/grill/py/source/pybundle.h | 66 +- externals/grill/py/source/pydsp.cpp | 380 ++--- externals/grill/py/source/pyext.cpp | 18 +- externals/grill/py/source/pyext.h | 4 +- externals/grill/py/source/pymeth.cpp | 856 +++++------ externals/grill/py/source/pyprefix.h | 86 +- externals/grill/py/source/pysymbol.cpp | 508 +++---- externals/grill/py/source/pysymbol.h | 152 +- 31 files changed, 2741 insertions(+), 2648 deletions(-) (limited to 'externals/grill/py') diff --git a/externals/grill/py/build/config-lnx.def b/externals/grill/py/build/config-lnx.def index e19f03bc..10e518f5 100644 --- a/externals/grill/py/build/config-lnx.def +++ b/externals/grill/py/build/config-lnx.def @@ -9,3 +9,6 @@ PYTHONVERSION=2.4 PY_NUMPY=1 # PY_NUMARRAY=1 # PY_NUMERIC=1 + +# use thread-safe GIL functionality (do this for python version >= 2.3!) +PY_USE_GIL=1 \ No newline at end of file diff --git a/externals/grill/py/build/config-mac.def b/externals/grill/py/build/config-mac.def index 57f6e3d5..752ee335 100644 --- a/externals/grill/py/build/config-mac.def +++ b/externals/grill/py/build/config-mac.def @@ -3,3 +3,6 @@ PY_NUMPY=1 # PY_NUMARRAY=1 # PY_NUMERIC=1 + +# use thread-safe GIL functionality (do this for python version >= 2.3!) +PY_USE_GIL=1 \ No newline at end of file diff --git a/externals/grill/py/build/config-win.def b/externals/grill/py/build/config-win.def index fee70e7b..98ab5552 100644 --- a/externals/grill/py/build/config-win.def +++ b/externals/grill/py/build/config-win.def @@ -9,3 +9,6 @@ PYTHONPATH=%programfiles%/python$(PYTHONVER) PY_NUMPY=1 # PY_NUMARRAY=1 # PY_NUMERIC=1 + +# use thread-safe GIL functionality (do this for python version >= 2.3!) +PY_USE_GIL=1 \ No newline at end of file diff --git a/externals/grill/py/build/gnumake-lnx-gcc.inc b/externals/grill/py/build/gnumake-lnx-gcc.inc index 483bb514..2babf197 100644 --- a/externals/grill/py/build/gnumake-lnx-gcc.inc +++ b/externals/grill/py/build/gnumake-lnx-gcc.inc @@ -12,3 +12,6 @@ ifdef PY_NUMERIC DEFS += -DPY_NUMERIC endif +ifdef PY_USE_GIL +DEFS += -DPY_USE_GIL +endif diff --git a/externals/grill/py/build/gnumake-mac-gcc.inc b/externals/grill/py/build/gnumake-mac-gcc.inc index c7080d47..8f8591af 100644 --- a/externals/grill/py/build/gnumake-mac-gcc.inc +++ b/externals/grill/py/build/gnumake-mac-gcc.inc @@ -12,3 +12,6 @@ ifdef PY_NUMERIC DEFS += -DPY_NUMERIC endif +ifdef PY_USE_GIL +DEFS += -DPY_USE_GIL +endif \ No newline at end of file diff --git a/externals/grill/py/build/gnumake-win-cygwin.inc b/externals/grill/py/build/gnumake-win-cygwin.inc index 102c2ecd..d9487833 100644 --- a/externals/grill/py/build/gnumake-win-cygwin.inc +++ b/externals/grill/py/build/gnumake-win-cygwin.inc @@ -13,3 +13,6 @@ ifdef PY_NUMERIC DEFS += -DPY_NUMERIC endif +ifdef PY_USE_GIL +DEFS += -DPY_USE_GIL +endif \ No newline at end of file diff --git a/externals/grill/py/build/nmake-win-msvc.inc b/externals/grill/py/build/nmake-win-msvc.inc index 0820daf9..a23e03ca 100644 --- a/externals/grill/py/build/nmake-win-msvc.inc +++ b/externals/grill/py/build/nmake-win-msvc.inc @@ -14,3 +14,6 @@ DEFS = $(DEFS) /DPY_NUMPY DEFS = $(DEFS) /DPY_NUMERIC !endif +!ifdef PY_USE_GIL +DEFS = $(DEFS) /DPY_USE_GIL +!endif diff --git a/externals/grill/py/package.txt b/externals/grill/py/package.txt index 001023bf..9002aa86 100644 --- a/externals/grill/py/package.txt +++ b/externals/grill/py/package.txt @@ -1,16 +1,16 @@ -NAME=py - -BUILDTYPE=multi -BUILDDIR=build - -SRCDIR=source -PRECOMPILE=pyprefix.h - -SRCS= \ - main.cpp \ - 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 - -HDRS= pyprefix.h main.h pyext.h pysymbol.h pybuffer.h pybundle.h pyatom.h pybase.h +NAME=py + +BUILDTYPE=multi +BUILDDIR=build + +SRCDIR=source +PRECOMPILE=pyprefix.h + +SRCS= \ + main.cpp \ + 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 + +HDRS= pyprefix.h main.h pyext.h pysymbol.h pybuffer.h pybundle.h pyatom.h pybase.h diff --git a/externals/grill/py/py.vcproj b/externals/grill/py/py.vcproj index 74c3954c..0a42243a 100644 --- a/externals/grill/py/py.vcproj +++ b/externals/grill/py/py.vcproj @@ -86,7 +86,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="C:\data\prog\packs\pthreads\include;"c:\data\pd\pd-cvs\src";..\flext\source;C:\Programme\prog\Python24\include;"C:\Programme\prog\Python24\Lib\site-packages\numpy\core\include"" - PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;PY_EXPORTS;FLEXT_SYS=2;FLEXT_THREADS;xPY_NUMPY;xFLEXT_USECMEM" + PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;PY_EXPORTS;FLEXT_SYS=2;FLEXT_THREADS;PY_NUMPY;FLEXT_USECMEM;PY_USE_GIL" BasicRuntimeChecks="3" RuntimeLibrary="1" RuntimeTypeInfo="TRUE" @@ -152,7 +152,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="c:\programme\audio\pd\src;c:\data\pdmax\flext\source;C:\Programme\prog\Python24\include" - PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;FLEXT_SYS=2;FLEXT_THREADS;PY_EXPORTS;PY_NUMARRAY" + PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;FLEXT_SYS=2;FLEXT_THREADS;PY_EXPORTS;PY_NUMARRAY;PY_USE_GIL" StringPooling="TRUE" RuntimeLibrary="0" EnableFunctionLevelLinking="TRUE" @@ -167,7 +167,7 @@ = 2.1. -It has been thoroughly tested with versions 2.2 to 2.4 +It has been thoroughly tested with versions 2.2 to 2.5 +The default build setting using PY_USE_GIL requires Python version >= 2.3. Check out the sample patches and scripts @@ -47,7 +48,6 @@ Known bugs: - 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) -- Threading in pyext obviously crashes under linux with Python version 2.4.2 (only) ---------------------------------------------------------------------------- @@ -120,6 +120,8 @@ Version history: - 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) 0.2.0: - ADD: handling of Python threads diff --git a/externals/grill/py/scripts/buffer.py b/externals/grill/py/scripts/buffer.py index 2bd36203..46c47991 100644 --- a/externals/grill/py/scripts/buffer.py +++ b/externals/grill/py/scripts/buffer.py @@ -1,60 +1,60 @@ -# py/pyext - python script objects for PD and MaxMSP -# -# Copyright (c) 2002-2005 Thomas Grill (gr@grrrr.org) -# For information on usage and redistribution, and for a DISCLAIMER OF ALL -# WARRANTIES, see the file, "license.txt," in this distribution. -# - -"""This is an example script for the py/pyext object's buffer support. - -PD/Max buffers can be mapped to Python arrays. -Currently, there are three implementations: -Numeric, numarray and Numeric3 (for all of them see http://numeric.scipy.org) -""" - -import sys - -try: - import pyext -except: - print "ERROR: This script must be loaded by the PD/Max py/pyext external" - -try: - from numarray import * -except: - print "Failed importing numarray module:",sys.exc_value - -def mul(*args): - # create buffer objects - # as long as these variables live the underlying buffers are locked - c = pyext.Buffer(args[0]) - a = pyext.Buffer(args[1]) - b = pyext.Buffer(args[2]) - - # slicing causes Python arrays (mapped to buffers) to be created - # note the c[:] - to assign contents you must assign to a slice of the buffer - c[:] = a[:]*b[:] - -def add(*args): - c = pyext.Buffer(args[0]) - a = pyext.Buffer(args[1]) - b = pyext.Buffer(args[2]) - - # this is also possible, but is probably slower - # the + converts a into a Python array, the argument b is taken as a sequence - # depending on the implementation this may be as fast - # as above or not - c[:] = a+b - -def fadein(target): - a = pyext.Buffer(target) - # in place operations are ok - a *= arange(len(a),type=Float32)/len(a) - -def neg(target): - a = pyext.Buffer(target) - # in place transformation (see Python array ufuncs) - negative(a[:],a[:]) - # must mark buffer content as dirty to update graph - # (no explicit assignment occurred) - a.dirty() +# py/pyext - python script objects for PD and MaxMSP +# +# Copyright (c) 2002-2005 Thomas Grill (gr@grrrr.org) +# For information on usage and redistribution, and for a DISCLAIMER OF ALL +# WARRANTIES, see the file, "license.txt," in this distribution. +# + +"""This is an example script for the py/pyext object's buffer support. + +PD/Max buffers can be mapped to Python arrays. +Currently, there are three implementations: +Numeric, numarray and Numeric3 (for all of them see http://numeric.scipy.org) +""" + +import sys + +try: + import pyext +except: + print "ERROR: This script must be loaded by the PD/Max py/pyext external" + +try: + from numarray import * +except: + print "Failed importing numarray module:",sys.exc_value + +def mul(*args): + # create buffer objects + # as long as these variables live the underlying buffers are locked + c = pyext.Buffer(args[0]) + a = pyext.Buffer(args[1]) + b = pyext.Buffer(args[2]) + + # slicing causes Python arrays (mapped to buffers) to be created + # note the c[:] - to assign contents you must assign to a slice of the buffer + c[:] = a[:]*b[:] + +def add(*args): + c = pyext.Buffer(args[0]) + a = pyext.Buffer(args[1]) + b = pyext.Buffer(args[2]) + + # this is also possible, but is probably slower + # the + converts a into a Python array, the argument b is taken as a sequence + # depending on the implementation this may be as fast + # as above or not + c[:] = a+b + +def fadein(target): + a = pyext.Buffer(target) + # in place operations are ok + a *= arange(len(a),type=Float32)/len(a) + +def neg(target): + a = pyext.Buffer(target) + # in place transformation (see Python array ufuncs) + negative(a[:],a[:]) + # must mark buffer content as dirty to update graph + # (no explicit assignment occurred) + a.dirty() diff --git a/externals/grill/py/scripts/sig.py b/externals/grill/py/scripts/sig.py index 8aa1ce65..09be7b66 100644 --- a/externals/grill/py/scripts/sig.py +++ b/externals/grill/py/scripts/sig.py @@ -1,100 +1,100 @@ -# py/pyext - python script objects for PD and MaxMSP -# -# Copyright (c) 2002-2005 Thomas Grill (gr@grrrr.org) -# For information on usage and redistribution, and for a DISCLAIMER OF ALL -# WARRANTIES, see the file, "license.txt," in this distribution. -# - -"""This is an example script for the py/pyext signal support. - -For numarray see http://numeric.scipy.org -It will probably once be replaced by Numeric(3) -""" - -try: - import pyext -except: - print "ERROR: This script must be loaded by the PD/Max py/pyext external" - -try: - import psyco - psyco.full() - print "Using JIT compilation" -except: - # don't care - pass - -import sys,math - -try: - import numarray -except: - print "Failed importing numarray module:",sys.exc_value - - -class gain(pyext._class): - """Just a simple gain stage""" - - gain = 0 - - def _signal(self): - # Multiply input vector by gain and copy to output - try: - self._outvec(0)[:] = self._invec(0)*self.gain - except: - pass - - -class gain2(pyext._class): - """More optimized version""" - - gain = 0 - - def _dsp(self): - if not self._arraysupport(): - print "No DSP support" - return False - - # cache vectors in this scope - self.invec = self._invec(0) - self.outvec = self._outvec(0) - # initialize _signal method here for optimized version - if self.invec is self.outvec: - self._signal = self.signal1 - else: - self._signal = self.signal2 - return True - - def signal1(self): - # Multiply signal vector in place - self.outvec *= self.gain - - def signal2(self): - # Multiply input vector by gain and copy to output - self.outvec[:] = self.invec*self.gain - - -class pan(pyext._class): - """Stereo panning""" - - def __init__(self): - self.float_1(0.5) - - def float_1(self,pos): - """pos ranges from 0 to 1""" - x = pos*math.pi/2 - self.fl = math.cos(x) - self.fr = math.sin(x) - - def _dsp(self): - # if _dsp is present it must return True to enable DSP - return pyext._arraysupport() - - def _signal(self): - # Multiply input vector by gain and copy to output - iv = self._invec(0) - # first process right output channel because left one could be - # identical to input - # we could also test with 'self._outvec(1)[:] is iv' - self._outvec(1)[:] = iv*self.fr - self._outvec(0)[:] = iv*self.fl +# py/pyext - python script objects for PD and MaxMSP +# +# Copyright (c) 2002-2005 Thomas Grill (gr@grrrr.org) +# For information on usage and redistribution, and for a DISCLAIMER OF ALL +# WARRANTIES, see the file, "license.txt," in this distribution. +# + +"""This is an example script for the py/pyext signal support. + +For numarray see http://numeric.scipy.org +It will probably once be replaced by Numeric(3) +""" + +try: + import pyext +except: + print "ERROR: This script must be loaded by the PD/Max py/pyext external" + +try: + import psyco + psyco.full() + print "Using JIT compilation" +except: + # don't care + pass + +import sys,math + +try: + import numarray +except: + print "Failed importing numarray module:",sys.exc_value + + +class gain(pyext._class): + """Just a simple gain stage""" + + gain = 0 + + def _signal(self): + # Multiply input vector by gain and copy to output + try: + self._outvec(0)[:] = self._invec(0)*self.gain + except: + pass + + +class gain2(pyext._class): + """More optimized version""" + + gain = 0 + + def _dsp(self): + if not self._arraysupport(): + print "No DSP support" + return False + + # cache vectors in this scope + self.invec = self._invec(0) + self.outvec = self._outvec(0) + # initialize _signal method here for optimized version + if self.invec is self.outvec: + self._signal = self.signal1 + else: + self._signal = self.signal2 + return True + + def signal1(self): + # Multiply signal vector in place + self.outvec *= self.gain + + def signal2(self): + # Multiply input vector by gain and copy to output + self.outvec[:] = self.invec*self.gain + + +class pan(pyext._class): + """Stereo panning""" + + def __init__(self): + self.float_1(0.5) + + def float_1(self,pos): + """pos ranges from 0 to 1""" + x = pos*math.pi/2 + self.fl = math.cos(x) + self.fr = math.sin(x) + + def _dsp(self): + # if _dsp is present it must return True to enable DSP + return pyext._arraysupport() + + def _signal(self): + # Multiply input vector by gain and copy to output + iv = self._invec(0) + # first process right output channel because left one could be + # identical to input + # we could also test with 'self._outvec(1)[:] is iv' + self._outvec(1)[:] = iv*self.fr + self._outvec(0)[:] = iv*self.fl diff --git a/externals/grill/py/scripts/simple.py b/externals/grill/py/scripts/simple.py index 04bea7ac..1aa211c5 100644 --- a/externals/grill/py/scripts/simple.py +++ b/externals/grill/py/scripts/simple.py @@ -1,6 +1,6 @@ # py/pyext - python script objects for PD and MaxMSP # -# Copyright (c) 2002-2005 Thomas Grill (gr@grrrr.org) +# Copyright (c) 2002-2007 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. # @@ -57,6 +57,8 @@ pyext Usage: 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. diff --git a/externals/grill/py/source/bound.cpp b/externals/grill/py/source/bound.cpp index 3e7bc386..de81bf1c 100644 --- a/externals/grill/py/source/bound.cpp +++ b/externals/grill/py/source/bound.cpp @@ -64,7 +64,7 @@ bool pyext::boundmeth(flext_base *th,t_symbol *sym,int argc,t_atom *argv,void *d bounddata *obj = (bounddata *)data; pyext *pyth = static_cast(th); - PyThreadState *state = pyth->PyLock(); + ThrState state = pyth->PyLock(); PyObject *args = MakePyArgs(sym,argc,argv); diff --git a/externals/grill/py/source/clmeth.cpp b/externals/grill/py/source/clmeth.cpp index be003291..e1e01977 100644 --- a/externals/grill/py/source/clmeth.cpp +++ b/externals/grill/py/source/clmeth.cpp @@ -2,7 +2,7 @@ py/pyext - python external object for PD and Max/MSP -Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) +Copyright (c)2002-2007 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. @@ -43,7 +43,7 @@ PyMethodDef pyext::attr_tbl[] = const char *pyext::pyext_doc = - "py/pyext - python external object for PD and Max/MSP, (C)2002-2005 Thomas Grill\n" + "py/pyext - python external object for PD and Max/MSP, (C)2002-2007 Thomas Grill\n" "\n" "This is the pyext base class. Available methods:\n" "_outlet(self,ix,args...): Send a message to an indexed outlet\n" diff --git a/externals/grill/py/source/modmeth.cpp b/externals/grill/py/source/modmeth.cpp index 3d44d99e..52f5b964 100644 --- a/externals/grill/py/source/modmeth.cpp +++ b/externals/grill/py/source/modmeth.cpp @@ -2,7 +2,7 @@ py/pyext - python external object for PD and Max/MSP -Copyright (c)2002-2006 Thomas Grill (gr@grrrr.org) +Copyright (c)2002-2007 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. @@ -35,7 +35,7 @@ PyMethodDef pybase::func_tbl[] = }; const char *pybase::py_doc = - "py/pyext - python external object for PD and Max/MSP, (C)2002-2006 Thomas Grill\n" + "py/pyext - python external object for PD and Max/MSP, (C)2002-2007 Thomas Grill\n" "\n" "This is the pyext module. Available function:\n" "_send(args...): Send a message to a send symbol\n" diff --git a/externals/grill/py/source/py.cpp b/externals/grill/py/source/py.cpp index c773ffcb..f19ec36a 100644 --- a/externals/grill/py/source/py.cpp +++ b/externals/grill/py/source/py.cpp @@ -2,7 +2,7 @@ py/pyext - python script object for PD and Max/MSP -Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) +Copyright (c)2002-2007 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. @@ -117,7 +117,7 @@ pyobj::pyobj(int argc,const t_atom *argv) FLEXT_ADDTIMER(stoptmr,tick); #endif - PyThreadState *state = PyLockSys(); + ThrState state = PyLockSys(); int inlets; if(argc && CanbeInt(*argv)) { @@ -193,7 +193,7 @@ pyobj::~pyobj() delete[] objects; } - PyThreadState *state = PyLockSys(); + ThrState state = PyLockSys(); Unregister(GetRegistry(REGNAME)); Report(); PyUnlock(state); @@ -207,7 +207,7 @@ void pyobj::Exit() void pyobj::m_set(int argc,const t_atom *argv) { - PyThreadState *state = PyLockSys(); + ThrState state = PyLockSys(); // function name has precedence if(argc >= 2) { @@ -241,7 +241,7 @@ void pyobj::m_set(int argc,const t_atom *argv) void pyobj::m_help() { post(""); - post("%s %s - python script object, (C)2002-2006 Thomas Grill",thisName(),PY__VERSION); + post("%s %s - python script object, (C)2002-2007 Thomas Grill",thisName(),PY__VERSION); #ifdef FLEXT_DEBUG post("DEBUG VERSION, compiled on " __DATE__ " " __TIME__); #endif @@ -346,7 +346,7 @@ bool pyobj::CbMethodResort(int n,const t_symbol *s,int argc,const t_atom *argv) if(n == 0 && s != sym_bang) return flext_base::CbMethodResort(n,s,argc,argv); - PyThreadState *state = PyLockSys(); + ThrState state = PyLockSys(); bool ret = false; diff --git a/externals/grill/py/source/pyatom.cpp b/externals/grill/py/source/pyatom.cpp index 07712f30..0af1b5e0 100644 --- a/externals/grill/py/source/pyatom.cpp +++ b/externals/grill/py/source/pyatom.cpp @@ -1,61 +1,61 @@ -/* - -py/pyext - python script object for PD and Max/MSP - -Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) -For information on usage and redistribution, and for a DISCLAIMER OF ALL -WARRANTIES, see the file, "license.txt," in this distribution. - -*/ - -#include "pyatom.h" -#include - -#define INTV 0.01 - -typedef std::map ObjMap; - -static ObjMap objmap; -static size_t collix = 0,curix = 0; -static double last = 0; - -size_t PyAtom::Register(PyObject *obj) -{ - Collect(); - - Py_INCREF(obj); - objmap[++curix] = obj; - -#ifdef _DEBUG -// post("REG %p (%i)\n",obj,objmap.size()); -#endif - return curix; -} - -PyObject *PyAtom::Retrieve(size_t id) -{ - ObjMap::iterator it = objmap.find(id); - PyObject *ret = it == objmap.end()?NULL:it->second; - Collect(); - return ret; -} - -void PyAtom::Collect() -{ - for(;;) { - ObjMap::iterator it = objmap.begin(); - if(it == objmap.end() || it->first > collix) break; - - PyObject *obj = it->second; - Py_DECREF(obj); - objmap.erase(it); - -#ifdef _DEBUG -// post("DEL %p\n",obj); -#endif - } - - // schedule next collect time - double tm = flext::GetTime(); - if(tm > last+INTV) last = tm,collix = curix; -} +/* + +py/pyext - python script object for PD and Max/MSP + +Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) +For information on usage and redistribution, and for a DISCLAIMER OF ALL +WARRANTIES, see the file, "license.txt," in this distribution. + +*/ + +#include "pyatom.h" +#include + +#define INTV 0.01 + +typedef std::map ObjMap; + +static ObjMap objmap; +static size_t collix = 0,curix = 0; +static double last = 0; + +size_t PyAtom::Register(PyObject *obj) +{ + Collect(); + + Py_INCREF(obj); + objmap[++curix] = obj; + +#ifdef _DEBUG +// post("REG %p (%i)\n",obj,objmap.size()); +#endif + return curix; +} + +PyObject *PyAtom::Retrieve(size_t id) +{ + ObjMap::iterator it = objmap.find(id); + PyObject *ret = it == objmap.end()?NULL:it->second; + Collect(); + return ret; +} + +void PyAtom::Collect() +{ + for(;;) { + ObjMap::iterator it = objmap.begin(); + if(it == objmap.end() || it->first > collix) break; + + PyObject *obj = it->second; + Py_DECREF(obj); + objmap.erase(it); + +#ifdef _DEBUG +// post("DEL %p\n",obj); +#endif + } + + // schedule next collect time + double tm = flext::GetTime(); + if(tm > last+INTV) last = tm,collix = curix; +} diff --git a/externals/grill/py/source/pyatom.h b/externals/grill/py/source/pyatom.h index e96514c6..3c20c66b 100644 --- a/externals/grill/py/source/pyatom.h +++ b/externals/grill/py/source/pyatom.h @@ -1,19 +1,19 @@ -/* - -py/pyext - python script object for PD and Max/MSP - -Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) -For information on usage and redistribution, and for a DISCLAIMER OF ALL -WARRANTIES, see the file, "license.txt," in this distribution. - -*/ - -#include "main.h" - -class PyAtom -{ -public: - static size_t Register(PyObject *obj); - static PyObject *Retrieve(size_t id); - static void Collect(); -}; +/* + +py/pyext - python script object for PD and Max/MSP + +Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) +For information on usage and redistribution, and for a DISCLAIMER OF ALL +WARRANTIES, see the file, "license.txt," in this distribution. + +*/ + +#include "main.h" + +class PyAtom +{ +public: + static size_t Register(PyObject *obj); + static PyObject *Retrieve(size_t id); + static void Collect(); +}; diff --git a/externals/grill/py/source/pybase.cpp b/externals/grill/py/source/pybase.cpp index 5c9dd40c..a74df81e 100644 --- a/externals/grill/py/source/pybase.cpp +++ b/externals/grill/py/source/pybase.cpp @@ -1,881 +1,912 @@ -/* - -py/pyext - python external object for PD and MaxMSP - -Copyright (c)2002-2006 Thomas Grill (gr@grrrr.org) -For information on usage and redistribution, and for a DISCLAIMER OF ALL -WARRANTIES, see the file, "license.txt," in this distribution. - -*/ - -#include "pybase.h" -#include - -#if FLEXT_OS == FLEXT_OS_WIN -#include -#elif FLEXT_OS == FLEXT_OS_MAC -#include -#endif - -static PyMethodDef StdOut_Methods[] = -{ - { "write", pybase::StdOut_Write, 1 }, - { NULL, NULL, } -}; - -static PyObject *gcollect = NULL; - -#ifdef FLEXT_THREADS - -class ThrCmp -{ -public: - inline bool operator()(const flext::thrid_t &a,const flext::thrid_t &b) const - { - if(sizeof(a) == sizeof(size_t)) - return *(size_t *)&a < *(size_t *)&b; - else - return memcmp(&a,&b,sizeof(a)) < 0; - } -}; - -typedef std::map PyThrMap; - -static PyInterpreterState *pymain = NULL; -static PyThrMap pythrmap; -PyThreadState *pybase::pythrsys = NULL; - -int pybase::lockcount = 0; - -PyThreadState *pybase::FindThreadState() -{ - flext::thrid_t id = flext::GetThreadId(); - PyThrMap::iterator it = pythrmap.find(id); - if(it == pythrmap.end()) { - // Make new thread state - PyThreadState *st = PyThreadState_New(pymain); - pythrmap[id] = st; - return st; - } - else - return it->second; -} - -void pybase::FreeThreadState() -{ - flext::thrid_t id = flext::GetThreadId(); - PyThrMap::iterator it = pythrmap.find(id); - if(it != pythrmap.end()) { - // clear out any cruft from thread state object - PyThreadState_Clear(it->second); - // delete my thread state object - PyThreadState_Delete(it->second); - // delete from map - pythrmap.erase(it); - } -} - -PyFifo pybase::qufifo; -flext::ThrCond pybase::qucond; -#endif - - -PyObject *pybase::module_obj = NULL; -PyObject *pybase::module_dict = NULL; - -PyObject *pybase::builtins_obj = NULL; -PyObject *pybase::builtins_dict = NULL; - -const t_symbol *pybase::sym_fint = NULL; -const t_symbol *pybase::sym_response = NULL; - -// ----------------------------------------------------------------------------------------------------------- - - -void initsymbol(); -void initsamplebuffer(); -void initbundle(); - -void pybase::lib_setup() -{ - post(""); - post("------------------------------------------------"); - post("py/pyext %s - python script objects",PY__VERSION); - post("(C)2002-2006 Thomas Grill - http://grrrr.org/ext"); - post(""); - post("using Python %s",Py_GetVersion()); - -#ifdef FLEXT_DEBUG - post(""); - post("DEBUG version compiled on %s %s",__DATE__,__TIME__); -#endif - - // ------------------------------------------------------------- - - sym_response = flext::MakeSymbol("response"); - -#if FLEXT_SYS == FLEXT_SYS_PD - sym_fint = sym_float; -#else - sym_fint = sym_int; -#endif - - // ------------------------------------------------------------- - - Py_Initialize(); - -#ifdef FLEXT_DEBUG - Py_DebugFlag = 1; -// Py_VerboseFlag = 1; -#else - Py_OptimizeFlag = 1; -#endif - -#ifdef FLEXT_THREADS - // enable thread support and acquire the global thread lock - PyEval_InitThreads(); - - // get thread state - pythrsys = PyThreadState_Get(); - // get main interpreter state - pymain = pythrsys->interp; - - // add thread state of main thread to map - pythrmap[GetThreadId()] = pythrsys; -#endif - - // sys.argv must be set to empty tuple - char *nothing = ""; - PySys_SetArgv(0,¬hing); - - // register/initialize pyext module only once! - module_obj = Py_InitModule(PYEXT_MODULE, func_tbl); - module_dict = PyModule_GetDict(module_obj); // borrowed reference - - PyModule_AddStringConstant(module_obj,"__doc__",(char *)py_doc); - - // redirect stdout - PyObject* py_out; - py_out = Py_InitModule("stdout", StdOut_Methods); - PySys_SetObject("stdout", py_out); - py_out = Py_InitModule("stderr", StdOut_Methods); - PySys_SetObject("stderr", py_out); - - // get garbage collector function - PyObject *gcobj = PyImport_ImportModule("gc"); - if(gcobj) { - gcollect = PyObject_GetAttrString(gcobj,"collect"); - Py_DECREF(gcobj); - } - - builtins_obj = PyImport_ImportModule("__builtin__"); - 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); - - // ------------------------------------------------------------- - - FLEXT_SETUP(pyobj); - FLEXT_SETUP(pymeth); - FLEXT_SETUP(pyext); -#ifndef PY_NODSP - FLEXT_DSP_SETUP(pydsp); -#endif - -#ifdef FLEXT_THREADS - // release global lock - PyEval_ReleaseLock(); - - // launch thread worker - LaunchThread(quworker,NULL); -#endif - - post("------------------------------------------------"); - post(""); -} - -FLEXT_LIB_SETUP(py,pybase::lib_setup) - - -// ----------------------------------------------------------------------------------------------------------- - - -pybase::pybase() - : module(NULL) - , dict(NULL) -#ifdef FLEXT_THREADS - , thrcount(0) - , shouldexit(false),stoptick(0) -#endif - , detach(0) - , pymsg(false) -{ - PyThreadState *state = PyLock(); - Py_INCREF(module_obj); - PyUnlock(state); -} - -pybase::~pybase() -{ - PyThreadState *state = PyLock(); - Py_XDECREF(module_obj); - PyUnlock(state); -} - -void pybase::Exit() -{ -#ifdef FLEXT_THREADS - erasethreads(); - - shouldexit = true; - qucond.Signal(); - - if(thrcount) { - // Wait for a certain time - for(int i = 0; i < (PY_STOP_WAIT/PY_STOP_TICK) && thrcount; ++i) - Sleep(PY_STOP_TICK*0.001f); - if(thrcount) { - // Wait forever - post("py/pyext - Waiting for thread termination!"); - while(thrcount) Sleep(PY_STOP_TICK*0.001f); - post("py/pyext - Okay, all threads have terminated"); - } - } -#endif -} - -void pybase::GetDir(PyObject *obj,AtomList &lst) -{ - if(obj) { - PyThreadState *state = PyLock(); - - PyObject *pvar = PyObject_Dir(obj); - if(!pvar) - PyErr_Print(); // no method found - else { - const t_symbol *sym = GetPyArgs(lst,pvar); - if(!sym) - post("py/pyext - Argument list could not be created"); - else - FLEXT_ASSERT(sym == sym_list); - Py_DECREF(pvar); - } - - PyUnlock(state); - } -} - -void pybase::m__dir(PyObject *obj) -{ - AtomList lst; - GetDir(obj,lst); - // dump dir to attribute outlet - DumpOut(NULL,lst.Count(),lst.Atoms()); -} - -void pybase::m__doc(PyObject *obj) -{ - if(obj) { - PyThreadState *state = PyLock(); - - PyObject *docf = PyDict_GetItemString(obj,"__doc__"); // borrowed!!! - if(docf && PyString_Check(docf)) { - post(""); - const char *s = PyString_AS_STRING(docf); - - // FIX: Python doc strings can easily be larger than 1k characters - // -> split into separate lines - for(;;) { - char buf[1024]; - char *nl = strchr((char *)s,'\n'); // the cast is for Borland C++ - if(!nl) { - // no more newline found - post(s); - break; - } - else { - // copy string before newline to temp buffer and post - unsigned int l = nl-s; - if(l >= sizeof(buf)) l = sizeof buf-1; - strncpy(buf,s,l); // copy all but newline - buf[l] = 0; - post(buf); - s = nl+1; // set after newline - } - } - } - - PyUnlock(state); - } -} - -void pybase::OpenEditor() -{ - if(!module) return; - const char *mname = PyModule_GetFilename(module); - if(!mname) { - PyErr_Clear(); - return; - } - - char fname[1024]; - strcpy(fname,mname); - - // replacing .pyc or .pyo for source file name - char *dt = strrchr(fname,'.'); - if(dt && !strncmp(dt,".py",2)) strcpy(dt,".py"); - - // this should open the editor.... -#if FLEXT_OS == FLEXT_OS_WIN - int err = (int)ShellExecute(NULL,"edit",fname,NULL,NULL,SW_SHOW); - if(err == SE_ERR_NOASSOC) { - // no association found - try notepad - err = (int)ShellExecute(NULL,NULL,"notepad.exe",fname,NULL,SW_SHOW); - } - else if(err == ERROR_FILE_NOT_FOUND || err == SE_ERR_FNF) - post("py/pyext - File not %s found",fname); - else if(err <= 32) - post("py/pyext - Unknown error opening %s",fname); - -#elif FLEXT_OS == FLEXT_OS_MAC - FSRef ref; - OSStatus err = FSPathMakeRef((unsigned char *)fname,&ref,NULL); - if(err) - post("py/pyext - Error interpreting path %s",fname); - else { - FSRef editor; - err = LSGetApplicationForItem(&ref,kLSRolesEditor,&editor,NULL); - if(err) { - // Can't find associated application... try Textedit - err = FSPathMakeRef((unsigned char *)"/Applications/TextEdit.app",&editor,NULL); - if(err) - post("py/pyext - Can't find Textedit application"); - } - - if(!err) { - LSLaunchFSRefSpec lspec; - lspec.appRef = &editor; - lspec.numDocs = 1; - lspec.itemRefs = &ref; - lspec.passThruParams = NULL; - lspec.launchFlags = kLSLaunchDefaults; - lspec.asyncRefCon = NULL; - err = LSOpenFromRefSpec(&lspec,NULL); - if(err) - post("py/pyext - Couldn't launch editor"); - } - } -#else - // thanks to Tim Blechmann - - char *editor = getenv("EDITOR"); - - if(!editor) { // || !strcmp(editor, "/usr/bin/nano") || !strcmp(editor, "/usr/bin/pico") || !strcmp(editor, "/usr/bin/vi")) { - // no environment variable or console text editor found ... use idle instead (should have come with Python) - editor = "idle"; - } - - pid_t child = fork(); - if(!child) { - char cmd[80]; - strcpy(cmd,editor); - strcat(cmd," "); - strcat(cmd,fname); - execl("/bin/sh", "sh", "-c", cmd, (char *) NULL); - } -#endif -} - -void pybase::SetArgs() -{ - // script arguments - int argc = args.Count(); - const t_atom *argv = args.Atoms(); - char **sargv = new char *[argc+1]; - for(int i = 0; i <= argc; ++i) { - sargv[i] = new char[256]; - if(!i) - strcpy(sargv[i],"py/pyext"); - else - GetAString(argv[i-1],sargv[i],255); - } - - // the arguments to the module are only recognized once! (at first use in a patcher) - PySys_SetArgv(argc+1,sargv); - - for(int j = 0; j <= argc; ++j) delete[] sargv[j]; - delete[] sargv; -} - -static bool getmodulesub(const char *mod,char *dir,int len,char *ext) -{ -#if FLEXT_SYS == FLEXT_SYS_PD - char *name; - int fd = open_via_path("",mod,ext,dir,&name,len,0); - if(fd > 0) { - FLEXT_ASSERT(name && *name); - close(fd); - } - else { - // look for mod/__init__.py - std::string tmp(mod); - int l = tmp.size(); - tmp += "/__init__"; - fd = open_via_path("",tmp.c_str(),ext,dir,&name,len,0); - if(fd > 0) { - FLEXT_ASSERT(name && *name); - close(fd); - // we must remove the module name from dir - char *t = dir+strlen(dir)-l; - FLEXT_ASSERT(!strcmp(mod,t) && t[-1] == '/'); - t[-1] = 0; - } - else - name = NULL; - } - - // if dir is current working directory... name points to dir - if(dir == name) strcpy(dir,"."); - return name != NULL; -#elif FLEXT_SYS == FLEXT_SYS_MAX - short path; - long type; - char smod[1024]; - strcpy(smod,mod); - strcat(smod,ext); - bool ok = !locatefile_extended(smod,&path,&type,&type,0); - if(ok) - // convert pathname to unix style - path_topathname(path,NULL,smod); - else { - // do path/file.ext combinations work at all under Max? - strcpy(smod,mod); - - short path; - type = 'fold'; - ok = !locatefile_extended(smod,&path,&type,&type,1); - if(ok) { - // convert pathname to unix style (including trailing slash) - path_topathname(path,NULL,smod); - char *end = smod+strlen(smod); - strcpy(end,mod); - strcat(end,"/__init__"); - strcat(end,ext); - - // check if file is really existing: try to open it - FILE *f = fopen(smod,"r"); - if(f) { - *end = 0; // clear module part ... we only need base path - fclose(f); - } - else - ok = false; - } - } - - if(ok) { - // convert path into slash style needed for Python -#if 0 - // Max API function uses Volume:/Path notation - path_nameconform(smod,dir,PATH_STYLE_SLASH,PATH_TYPE_ABSOLUTE); -#else -#if FLEXT_OS == FLEXT_OS_WIN - char *colon = NULL; -#else - char *colon = strchr(smod,':'); -#endif - if(colon) { - *colon = 0; - strcpy(dir,"/Volumes/"); - strcat(dir,smod); - strcat(dir,colon+1); - } - else - strcpy(dir,smod); -#endif - return true; - } - else - // not found - return false; -#else -#pragma message("Not implemented"); - return false; -#endif -} - -static bool getmodulepath(const char *mod,char *dir,int len) -{ - return - getmodulesub(mod,dir,len,".py") || - getmodulesub(mod,dir,len,".pyc") || - getmodulesub(mod,dir,len,".pyo"); -} - -bool pybase::ImportModule(const char *name) -{ - if(name) { - if(modname == name) { - // module with the same name is already loaded - if(module) return true; - } - else - modname = name; - } - else - modname.clear(); - - UnimportModule(); - return ReloadModule(); -} - -void pybase::UnimportModule() -{ - if(module) { - FLEXT_ASSERT(dict && module_obj && module_dict); - - Py_DECREF(module); - - // reference count to module is not 0 here, altough probably the last instance was unloaded - // Python retains one reference to the module all the time - // we don't care - - module = NULL; - dict = NULL; - } -} - -bool pybase::ReloadModule() -{ - SetArgs(); - PyObject *newmod; - - if(modname.length()) { - if(module) - newmod = PyImport_ReloadModule(module); - else { - // search in module path (TODO: check before if module is already present to avoid costly searching) - char dir[1024]; - if(!getmodulepath(modname.c_str(),dir,sizeof(dir))) - PyErr_SetString(PyExc_ImportError,"Module not found in path"); - else - AddToPath(dir); - - // module could also be loaded ok, even if it's not in the path (e.g. for internal stuff) - newmod = PyImport_ImportModule((char *)modname.c_str()); - } - } - else { - // if no module name given, take py module - newmod = module_obj; - Py_INCREF(newmod); - } - - if(!newmod) { - // unload faulty module - UnimportModule(); - return false; - } - else { - Py_XDECREF(module); - module = newmod; - dict = PyModule_GetDict(module); // borrowed - return true; - } -} - -void pybase::AddToPath(const char *dir) -{ - if(dir && *dir) { - PyObject *pobj = PySys_GetObject("path"); - if(pobj && PyList_Check(pobj)) { - PyObject *ps = PyString_FromString(dir); - if(!PySequence_Contains(pobj,ps)) - PyList_Append(pobj,ps); // makes new reference - Py_DECREF(ps); - } - PySys_SetObject("path",pobj); // steals reference to pobj - } -} - -void pybase::AddCurrentPath(flext_base *o) -{ - char dir[1024]; - - // add dir of current patch to path - o->GetCanvasDir(dir,sizeof(dir)); - if(*dir) AddToPath(dir); - - // add current dir to path -#if FLEXT_SYS == FLEXT_SYS_PD - AddToPath(GetString(canvas_getcurrentdir())); -#elif FLEXT_SYS == FLEXT_SYS_MAX - short path = path_getdefault(); - path_topathname(path,NULL,dir); - AddToPath(dir); -#endif -} - -bool pybase::OutObject(flext_base *ext,int o,PyObject *obj) -{ - flext::AtomListStatic<16> lst; - const t_symbol *sym = pymsg?GetPyAtom(lst,obj):GetPyArgs(lst,obj); - if(sym) { - // call to outlet _outside_ the Mutex lock! - // otherwise (if not detached) deadlock will occur - ext->ToOutAnything(o,sym,lst.Count(),lst.Atoms()); - return true; - } - else - return false; -} - -void pybase::Reload() -{ - PyThreadState *state = PyLock(); - - PyObject *reg = GetRegistry(REGNAME); - - if(reg) { - PyObject *key; - int pos = 0; - while(PyDict_Next(reg,&pos,&key,NULL)) { - pybase *th = (pybase *)PyLong_AsLong(key); - FLEXT_ASSERT(th); - th->Unload(); - } - - UnloadModule(); - } - - bool ok = ReloadModule(); - - if(ok) { - LoadModule(); - - if(reg) { - SetRegistry(REGNAME,reg); - - PyObject *key; - int pos = 0; - while(PyDict_Next(reg,&pos,&key,NULL)) { - pybase *th = (pybase *)PyLong_AsLong(key); - FLEXT_ASSERT(th); - th->Load(); - } - } - else - Load(); - } - - Report(); - PyUnlock(state); -} - -static PyObject *output = NULL; - -// post to the console -PyObject* pybase::StdOut_Write(PyObject* self, PyObject* args) -{ - // should always be a tuple - FLEXT_ASSERT(PyTuple_Check(args)); - - const int sz = PyTuple_GET_SIZE(args); - - for(int i = 0; i < sz; ++i) { - PyObject *val = PyTuple_GET_ITEM(args,i); // borrowed reference - PyObject *str = PyObject_Str(val); // new reference - char *cstr = PyString_AS_STRING(str); - char *lf = strchr(cstr,'\n'); - - // line feed in string - if(!lf) { - // no -> just append - if(output) - PyString_ConcatAndDel(&output,str); // str is decrefd - else - output = str; // take str reference - } - else { - // yes -> append up to line feed, reset output buffer to string remainder - PyObject *part = PyString_FromStringAndSize(cstr,lf-cstr); // new reference - if(output) - PyString_ConcatAndDel(&output,part); // str is decrefd - else - output = part; // take str reference - - // output concatenated string - post(PyString_AS_STRING(output)); - - Py_DECREF(output); - output = PyString_FromString(lf+1); // new reference - } - } - - Py_INCREF(Py_None); - return Py_None; -} - - -class work_data -{ -public: - work_data(PyObject *f,PyObject *a): fun(f),args(a) {} - ~work_data() { Py_DECREF(fun); Py_DECREF(args); } - - PyObject *fun,*args; -}; - -bool pybase::gencall(PyObject *pmeth,PyObject *pargs) -{ - bool ret = false; - - // Now call method - switch(detach) { - case 0: - ret = docall(pmeth,pargs); - Py_DECREF(pargs); - Py_DECREF(pmeth); - break; -#ifdef FLEXT_THREADS - case 1: - // put call into queue - ret = qucall(pmeth,pargs); - break; - case 2: - // each call a new thread - if(!shouldexit) { - thr_params *p = new thr_params; - p->cl = (flext_base *)this; - p->var->_ext = new work_data(pmeth,pargs); - ret = LaunchThread(thrworker,p); - if(!ret) post("py/pyext - Failed to launch thread!"); - } - break; -#endif - default: - post("py/pyext - Unknown detach mode"); - } - return ret; -} - -void pybase::exchandle() -{ -#if 0 - // want to use that, but exception keeps a reference to the object - // might be a Python bug! - PyErr_Print(); -#else - // must use that instead... clear the exception - PyObject *type,*value,*traceback; - PyErr_Fetch(&type,&value,&traceback); - PyErr_NormalizeException(&type,&value,&traceback); - PyErr_Display(type,value,traceback); - - Py_XDECREF(type); - Py_XDECREF(value); - Py_XDECREF(traceback); -#endif -} - -#ifdef FLEXT_THREADS -void pybase::thrworker(thr_params *p) -{ - FLEXT_ASSERT(p); - pybase *th = (pybase *)p->cl; - work_data *w = (work_data *)p->var->_ext; - - ++th->thrcount; // \todo this should be atomic - PyThreadState *state = PyLock(); - - // call worker - th->docall(w->fun,w->args); - delete w; - - PyUnlock(state); - --th->thrcount; // \todo this should be atomic -} - -void pybase::quworker(thr_params *) -{ - FifoEl *el; - PyThreadState *my = FindThreadState(),*state; - - for(;;) { - while(el = qufifo.Get()) { - ++el->th->thrcount; // \todo this should be atomic - state = PyLock(my); - el->th->docall(el->fun,el->args); - Py_XDECREF(el->fun); - Py_XDECREF(el->args); - PyUnlock(state); - --el->th->thrcount; // \todo this should be atomic - qufifo.Free(el); - } - qucond.Wait(); - } - - // we never end -#if 0 - state = PyLock(my); - // unref remaining Python objects - while(el = qufifo.Get()) { - Py_XDECREF(el->fun); - Py_XDECREF(el->args); - qufifo.Free(el); - } - PyUnlock(state); -#endif -} - -void pybase::erasethreads() -{ - PyFifo tmp; - FifoEl *el; - while(el = qufifo.Get()) { - if(el->th == this) { - Py_XDECREF(el->fun); - Py_XDECREF(el->args); - qufifo.Free(el); - } - else - tmp.Put(el); - } - // Push back - while(el = tmp.Get()) qufifo.Put(el); -} -#endif - -bool pybase::collect() -{ - if(gcollect) { - PyObject *ret = PyObject_CallObject(gcollect,NULL); - if(ret) { -#ifdef FLEXT_DEBUG - int refs = PyInt_AsLong(ret); - if(refs) post("py/pyext - Garbage collector reports %i unreachable objects",refs); -#endif - Py_DECREF(ret); - return false; - } - } - return true; -} +/* + +py/pyext - python external object for PD and MaxMSP + +Copyright (c)2002-2007 Thomas Grill (gr@grrrr.org) +For information on usage and redistribution, and for a DISCLAIMER OF ALL +WARRANTIES, see the file, "license.txt," in this distribution. + +*/ + +#include "pybase.h" +#include + +#if FLEXT_OS == FLEXT_OS_WIN +#include +#elif FLEXT_OS == FLEXT_OS_MAC +#include +#endif + +static PyMethodDef StdOut_Methods[] = +{ + { "write", pybase::StdOut_Write, 1 }, + { NULL, NULL, } +}; + +static PyObject *gcollect = NULL; + +#ifdef FLEXT_THREADS + +class ThrCmp +{ +public: + inline bool operator()(const flext::thrid_t &a,const flext::thrid_t &b) const + { + if(sizeof(a) == sizeof(size_t)) + return *(size_t *)&a < *(size_t *)&b; + else + return memcmp(&a,&b,sizeof(a)) < 0; + } +}; + + +int pybase::lockcount = 0; + +#ifndef PY_USE_GIL +static PyInterpreterState *pymain = NULL; + +typedef std::map PyThrMap; + +static PyThrMap pythrmap; +ThrState pybase::pythrsys = NULL; + +ThrState pybase::FindThreadState() +{ + flext::thrid_t id = flext::GetThreadId(); + PyThrMap::iterator it = pythrmap.find(id); + if(it == pythrmap.end()) { + // Make new thread state + ThrState st = PyThreadState_New(pymain); + pythrmap[id] = st; + return st; + } + else + return it->second; +} + +void pybase::FreeThreadState() +{ + flext::thrid_t id = flext::GetThreadId(); + PyThrMap::iterator it = pythrmap.find(id); + if(it != pythrmap.end()) { + // clear out any cruft from thread state object + PyThreadState_Clear(it->second); + // delete my thread state object + PyThreadState_Delete(it->second); + // delete from map + pythrmap.erase(it); + } +} +#endif // PY_USE_GIL + +PyFifo pybase::qufifo; +flext::ThrCond pybase::qucond; +#endif + + +PyObject *pybase::module_obj = NULL; +PyObject *pybase::module_dict = NULL; + +PyObject *pybase::builtins_obj = NULL; +PyObject *pybase::builtins_dict = NULL; + +const t_symbol *pybase::sym_fint = NULL; +const t_symbol *pybase::sym_response = NULL; + +// ----------------------------------------------------------------------------------------------------------- + + +void initsymbol(); +void initsamplebuffer(); +void initbundle(); + +void pybase::lib_setup() +{ + post(""); + post("------------------------------------------------"); + post("py/pyext %s - python script objects",PY__VERSION); + post("(C)2002-2007 Thomas Grill - http://grrrr.org/ext"); + post(""); + post("using Python %s",Py_GetVersion()); + +#ifdef FLEXT_DEBUG + post(""); + post("DEBUG version compiled on %s %s",__DATE__,__TIME__); +#endif + + // ------------------------------------------------------------- + + sym_response = flext::MakeSymbol("response"); + +#if FLEXT_SYS == FLEXT_SYS_PD + sym_fint = sym_float; +#else + sym_fint = sym_int; +#endif + + // ------------------------------------------------------------- + + Py_Initialize(); + +#ifdef FLEXT_DEBUG + Py_DebugFlag = 1; +// Py_VerboseFlag = 1; +#else + Py_OptimizeFlag = 1; +#endif + +#ifdef FLEXT_THREADS + // enable thread support and acquire the global thread lock + PyEval_InitThreads(); + +#ifndef PY_USE_GIL + // get thread state + pythrsys = PyThreadState_Get(); + // get main interpreter state + pymain = pythrsys->interp; + + // add thread state of main thread to map + pythrmap[GetThreadId()] = pythrsys; +#endif // PY_USE_GIL + +#endif + + // sys.argv must be set to empty tuple + char *nothing = ""; + PySys_SetArgv(0,¬hing); + + // register/initialize pyext module only once! + module_obj = Py_InitModule(PYEXT_MODULE, func_tbl); + module_dict = PyModule_GetDict(module_obj); // borrowed reference + + PyModule_AddStringConstant(module_obj,"__doc__",(char *)py_doc); + + // redirect stdout + PyObject* py_out; + py_out = Py_InitModule("stdout", StdOut_Methods); + PySys_SetObject("stdout", py_out); + py_out = Py_InitModule("stderr", StdOut_Methods); + PySys_SetObject("stderr", py_out); + + // get garbage collector function + PyObject *gcobj = PyImport_ImportModule("gc"); + if(gcobj) { + gcollect = PyObject_GetAttrString(gcobj,"collect"); + Py_DECREF(gcobj); + } + + builtins_obj = PyImport_ImportModule("__builtin__"); + 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); + + // ------------------------------------------------------------- + + FLEXT_SETUP(pyobj); + FLEXT_SETUP(pymeth); + FLEXT_SETUP(pyext); +#ifndef PY_NODSP + FLEXT_DSP_SETUP(pydsp); +#endif + +#ifdef FLEXT_THREADS + // release global lock + PyEval_ReleaseLock(); + + // launch thread worker + LaunchThread(quworker,NULL); + + // launch python worker + LaunchThread(pyworker,NULL); +#endif + + post("------------------------------------------------"); + post(""); +} + +FLEXT_LIB_SETUP(py,pybase::lib_setup) + + +// ----------------------------------------------------------------------------------------------------------- + + +pybase::pybase() + : module(NULL) + , dict(NULL) +#ifdef FLEXT_THREADS + , thrcount(0) + , shouldexit(false),stoptick(0) +#endif + , detach(0) + , pymsg(false) +{ + ThrState state = PyLock(); + Py_INCREF(module_obj); + PyUnlock(state); +} + +pybase::~pybase() +{ + ThrState state = PyLock(); + Py_XDECREF(module_obj); + PyUnlock(state); +} + +void pybase::Exit() +{ +#ifdef FLEXT_THREADS + erasethreads(); + + shouldexit = true; + qucond.Signal(); + + if(thrcount) { + // Wait for a certain time + for(int i = 0; i < (PY_STOP_WAIT/PY_STOP_TICK) && thrcount; ++i) + Sleep(PY_STOP_TICK*0.001f); + if(thrcount) { + // Wait forever + post("py/pyext - Waiting for thread termination!"); + while(thrcount) Sleep(PY_STOP_TICK*0.001f); + post("py/pyext - Okay, all threads have terminated"); + } + } +#endif +} + +void pybase::GetDir(PyObject *obj,AtomList &lst) +{ + if(obj) { + ThrState state = PyLock(); + + PyObject *pvar = PyObject_Dir(obj); + if(!pvar) + PyErr_Print(); // no method found + else { + const t_symbol *sym = GetPyArgs(lst,pvar); + if(!sym) + post("py/pyext - Argument list could not be created"); + else + FLEXT_ASSERT(sym == sym_list); + Py_DECREF(pvar); + } + + PyUnlock(state); + } +} + +void pybase::m__dir(PyObject *obj) +{ + AtomList lst; + GetDir(obj,lst); + // dump dir to attribute outlet + DumpOut(NULL,lst.Count(),lst.Atoms()); +} + +void pybase::m__doc(PyObject *obj) +{ + if(obj) { + ThrState state = PyLock(); + + PyObject *docf = PyDict_GetItemString(obj,"__doc__"); // borrowed!!! + if(docf && PyString_Check(docf)) { + post(""); + const char *s = PyString_AS_STRING(docf); + + // FIX: Python doc strings can easily be larger than 1k characters + // -> split into separate lines + for(;;) { + char buf[1024]; + char *nl = strchr((char *)s,'\n'); // the cast is for Borland C++ + if(!nl) { + // no more newline found + post(s); + break; + } + else { + // copy string before newline to temp buffer and post + unsigned int l = nl-s; + if(l >= sizeof(buf)) l = sizeof buf-1; + strncpy(buf,s,l); // copy all but newline + buf[l] = 0; + post(buf); + s = nl+1; // set after newline + } + } + } + + PyUnlock(state); + } +} + +void pybase::OpenEditor() +{ + if(!module) return; + const char *mname = PyModule_GetFilename(module); + if(!mname) { + PyErr_Clear(); + return; + } + + char fname[1024]; + strcpy(fname,mname); + + // replacing .pyc or .pyo for source file name + char *dt = strrchr(fname,'.'); + if(dt && !strncmp(dt,".py",2)) strcpy(dt,".py"); + + // this should open the editor.... +#if FLEXT_OS == FLEXT_OS_WIN + int err = (int)ShellExecute(NULL,"edit",fname,NULL,NULL,SW_SHOW); + if(err == SE_ERR_NOASSOC) { + // no association found - try notepad + err = (int)ShellExecute(NULL,NULL,"notepad.exe",fname,NULL,SW_SHOW); + } + else if(err == ERROR_FILE_NOT_FOUND || err == SE_ERR_FNF) + post("py/pyext - File not %s found",fname); + else if(err <= 32) + post("py/pyext - Unknown error opening %s",fname); + +#elif FLEXT_OS == FLEXT_OS_MAC + FSRef ref; + OSStatus err = FSPathMakeRef((unsigned char *)fname,&ref,NULL); + if(err) + post("py/pyext - Error interpreting path %s",fname); + else { + FSRef editor; + err = LSGetApplicationForItem(&ref,kLSRolesEditor,&editor,NULL); + if(err) { + // Can't find associated application... try Textedit + err = FSPathMakeRef((unsigned char *)"/Applications/TextEdit.app",&editor,NULL); + if(err) + post("py/pyext - Can't find Textedit application"); + } + + if(!err) { + LSLaunchFSRefSpec lspec; + lspec.appRef = &editor; + lspec.numDocs = 1; + lspec.itemRefs = &ref; + lspec.passThruParams = NULL; + lspec.launchFlags = kLSLaunchDefaults; + lspec.asyncRefCon = NULL; + err = LSOpenFromRefSpec(&lspec,NULL); + if(err) + post("py/pyext - Couldn't launch editor"); + } + } +#else + // thanks to Tim Blechmann + + char *editor = getenv("EDITOR"); + + if(!editor) { // || !strcmp(editor, "/usr/bin/nano") || !strcmp(editor, "/usr/bin/pico") || !strcmp(editor, "/usr/bin/vi")) { + // no environment variable or console text editor found ... use idle instead (should have come with Python) + editor = "idle"; + } + + pid_t child = fork(); + if(!child) { + char cmd[80]; + strcpy(cmd,editor); + strcat(cmd," "); + strcat(cmd,fname); + execl("/bin/sh", "sh", "-c", cmd, (char *) NULL); + } +#endif +} + +void pybase::SetArgs() +{ + // script arguments + int argc = args.Count(); + const t_atom *argv = args.Atoms(); + char **sargv = new char *[argc+1]; + for(int i = 0; i <= argc; ++i) { + sargv[i] = new char[256]; + if(!i) + strcpy(sargv[i],"py/pyext"); + else + GetAString(argv[i-1],sargv[i],255); + } + + // the arguments to the module are only recognized once! (at first use in a patcher) + PySys_SetArgv(argc+1,sargv); + + for(int j = 0; j <= argc; ++j) delete[] sargv[j]; + delete[] sargv; +} + +static bool getmodulesub(const char *mod,char *dir,int len,char *ext) +{ +#if FLEXT_SYS == FLEXT_SYS_PD + char *name; + int fd = open_via_path("",mod,ext,dir,&name,len,0); + if(fd > 0) { + FLEXT_ASSERT(name && *name); + close(fd); + } + else { + // look for mod/__init__.py + std::string tmp(mod); + int l = tmp.size(); + tmp += "/__init__"; + fd = open_via_path("",tmp.c_str(),ext,dir,&name,len,0); + if(fd > 0) { + FLEXT_ASSERT(name && *name); + close(fd); + // we must remove the module name from dir + char *t = dir+strlen(dir)-l; + FLEXT_ASSERT(!strcmp(mod,t) && t[-1] == '/'); + t[-1] = 0; + } + else + name = NULL; + } + + // if dir is current working directory... name points to dir + if(dir == name) strcpy(dir,"."); + return name != NULL; +#elif FLEXT_SYS == FLEXT_SYS_MAX + short path; + long type; + char smod[1024]; + strcpy(smod,mod); + strcat(smod,ext); + bool ok = !locatefile_extended(smod,&path,&type,&type,0); + if(ok) + // convert pathname to unix style + path_topathname(path,NULL,smod); + else { + // do path/file.ext combinations work at all under Max? + strcpy(smod,mod); + + short path; + type = 'fold'; + ok = !locatefile_extended(smod,&path,&type,&type,1); + if(ok) { + // convert pathname to unix style (including trailing slash) + path_topathname(path,NULL,smod); + char *end = smod+strlen(smod); + strcpy(end,mod); + strcat(end,"/__init__"); + strcat(end,ext); + + // check if file is really existing: try to open it + FILE *f = fopen(smod,"r"); + if(f) { + *end = 0; // clear module part ... we only need base path + fclose(f); + } + else + ok = false; + } + } + + if(ok) { + // convert path into slash style needed for Python +#if 0 + // Max API function uses Volume:/Path notation + path_nameconform(smod,dir,PATH_STYLE_SLASH,PATH_TYPE_ABSOLUTE); +#else +#if FLEXT_OS == FLEXT_OS_WIN + char *colon = NULL; +#else + char *colon = strchr(smod,':'); +#endif + if(colon) { + *colon = 0; + strcpy(dir,"/Volumes/"); + strcat(dir,smod); + strcat(dir,colon+1); + } + else + strcpy(dir,smod); +#endif + return true; + } + else + // not found + return false; +#else +#pragma message("Not implemented"); + return false; +#endif +} + +static bool getmodulepath(const char *mod,char *dir,int len) +{ + return + getmodulesub(mod,dir,len,".py") || + getmodulesub(mod,dir,len,".pyc") || + getmodulesub(mod,dir,len,".pyo"); +} + +bool pybase::ImportModule(const char *name) +{ + if(name) { + if(modname == name) { + // module with the same name is already loaded + if(module) return true; + } + else + modname = name; + } + else + modname.clear(); + + UnimportModule(); + return ReloadModule(); +} + +void pybase::UnimportModule() +{ + if(module) { + FLEXT_ASSERT(dict && module_obj && module_dict); + + Py_DECREF(module); + + // reference count to module is not 0 here, altough probably the last instance was unloaded + // Python retains one reference to the module all the time + // we don't care + + module = NULL; + dict = NULL; + } +} + +bool pybase::ReloadModule() +{ + SetArgs(); + PyObject *newmod; + + if(modname.length()) { + + if(module) + newmod = PyImport_ReloadModule(module); + else { + // search in module path (TODO: check before if module is already present to avoid costly searching) + char dir[1024]; + if(!getmodulepath(modname.c_str(),dir,sizeof(dir))) + PyErr_SetString(PyExc_ImportError,"Module not found in path"); + else + AddToPath(dir); + + // module could also be loaded ok, even if it's not in the path (e.g. for internal stuff) + newmod = PyImport_ImportModule((char *)modname.c_str()); + } + } + else { + // if no module name given, take py module + newmod = module_obj; + Py_INCREF(newmod); + } + + if(!newmod) { + // unload faulty module + UnimportModule(); + return false; + } + else { + Py_XDECREF(module); + module = newmod; + dict = PyModule_GetDict(module); // borrowed + return true; + } +} + +void pybase::AddToPath(const char *dir) +{ + if(dir && *dir) { + PyObject *pobj = PySys_GetObject("path"); + if(pobj && PyList_Check(pobj)) { + PyObject *ps = PyString_FromString(dir); + if(!PySequence_Contains(pobj,ps)) + PyList_Append(pobj,ps); // makes new reference + Py_DECREF(ps); + } + PySys_SetObject("path",pobj); // steals reference to pobj + } +} + +void pybase::AddCurrentPath(flext_base *o) +{ + char dir[1024]; + + // add dir of current patch to path + o->GetCanvasDir(dir,sizeof(dir)); + if(*dir) AddToPath(dir); + + // add current dir to path +#if FLEXT_SYS == FLEXT_SYS_PD + AddToPath(GetString(canvas_getcurrentdir())); +#elif FLEXT_SYS == FLEXT_SYS_MAX + short path = path_getdefault(); + path_topathname(path,NULL,dir); + AddToPath(dir); +#endif +} + +bool pybase::OutObject(flext_base *ext,int o,PyObject *obj) +{ + flext::AtomListStatic<16> lst; + const t_symbol *sym = pymsg?GetPyAtom(lst,obj):GetPyArgs(lst,obj); + if(sym) { + // call to outlet _outside_ the Mutex lock! + // otherwise (if not detached) deadlock will occur + ext->ToOutAnything(o,sym,lst.Count(),lst.Atoms()); + return true; + } + else + return false; +} + +void pybase::Reload() +{ + ThrState state = PyLock(); + + PyObject *reg = GetRegistry(REGNAME); + + if(reg) { + PyObject *key; + int pos = 0; + while(PyDict_Next(reg,&pos,&key,NULL)) { + pybase *th = (pybase *)PyLong_AsLong(key); + FLEXT_ASSERT(th); + th->Unload(); + } + + UnloadModule(); + } + + bool ok = ReloadModule(); + + if(ok) { + LoadModule(); + + if(reg) { + SetRegistry(REGNAME,reg); + + PyObject *key; + int pos = 0; + while(PyDict_Next(reg,&pos,&key,NULL)) { + pybase *th = (pybase *)PyLong_AsLong(key); + FLEXT_ASSERT(th); + th->Load(); + } + } + else + Load(); + } + + Report(); + PyUnlock(state); +} + +static PyObject *output = NULL; + +// post to the console +PyObject* pybase::StdOut_Write(PyObject* self, PyObject* args) +{ + // should always be a tuple + FLEXT_ASSERT(PyTuple_Check(args)); + + const int sz = PyTuple_GET_SIZE(args); + + for(int i = 0; i < sz; ++i) { + PyObject *val = PyTuple_GET_ITEM(args,i); // borrowed reference + PyObject *str = PyObject_Str(val); // new reference + char *cstr = PyString_AS_STRING(str); + char *lf = strchr(cstr,'\n'); + + // line feed in string + if(!lf) { + // no -> just append + if(output) + PyString_ConcatAndDel(&output,str); // str is decrefd + else + output = str; // take str reference + } + else { + // yes -> append up to line feed, reset output buffer to string remainder + PyObject *part = PyString_FromStringAndSize(cstr,lf-cstr); // new reference + if(output) + PyString_ConcatAndDel(&output,part); // str is decrefd + else + output = part; // take str reference + + // output concatenated string + post(PyString_AS_STRING(output)); + + Py_DECREF(output); + output = PyString_FromString(lf+1); // new reference + } + } + + Py_INCREF(Py_None); + return Py_None; +} + + +class work_data +{ +public: + work_data(PyObject *f,PyObject *a): fun(f),args(a) {} + ~work_data() { Py_DECREF(fun); Py_DECREF(args); } + + PyObject *fun,*args; +}; + +bool pybase::gencall(PyObject *pmeth,PyObject *pargs) +{ + bool ret = false; + + // Now call method + switch(detach) { + case 0: + ret = docall(pmeth,pargs); + Py_DECREF(pargs); + Py_DECREF(pmeth); + break; +#ifdef FLEXT_THREADS + case 1: + // put call into queue + ret = qucall(pmeth,pargs); + break; + case 2: + // each call a new thread + if(!shouldexit) { + thr_params *p = new thr_params; + p->cl = (flext_base *)this; + p->var->_ext = new work_data(pmeth,pargs); + ret = LaunchThread(thrworker,p); + if(!ret) post("py/pyext - Failed to launch thread!"); + } + break; +#endif + default: + post("py/pyext - Unknown detach mode"); + } + return ret; +} + +void pybase::exchandle() +{ +#if 0 + // want to use that, but exception keeps a reference to the object + // might be a Python bug! + PyErr_Print(); +#else + // must use that instead... clear the exception + PyObject *type,*value,*traceback; + PyErr_Fetch(&type,&value,&traceback); + PyErr_NormalizeException(&type,&value,&traceback); + PyErr_Display(type,value,traceback); + + Py_XDECREF(type); + Py_XDECREF(value); + Py_XDECREF(traceback); +#endif +} + +#ifdef FLEXT_THREADS +void pybase::thrworker(thr_params *p) +{ + FLEXT_ASSERT(p); + pybase *th = (pybase *)p->cl; + work_data *w = (work_data *)p->var->_ext; + + ++th->thrcount; // \todo this should be atomic + ThrState state = PyLock(); + + // call worker + th->docall(w->fun,w->args); + delete w; + + PyUnlock(state); + --th->thrcount; // \todo this should be atomic +} + +/*! This thread function basically keeps alive the Python interpreter in the background + It's good for threads that have been started from scripted functions +*/ +void pybase::pyworker(thr_params *) +{ + ThrState state = PyLock(); + + PyObject *timemod = PyImport_ImportModule("time"); + PyObject *sleep = PyObject_GetAttrString(timemod,"sleep"); + PyObject *args = PyTuple_New(1); + PyTuple_SET_ITEM(args,0,PyFloat_FromDouble(1000000)); + + for(;;) { + PyObject *res = PyObject_CallObject(sleep,args); + Py_DECREF(res); + } + + PyUnlock(state); +} + +void pybase::quworker(thr_params *) +{ + FifoEl *el; + ThrState my = FindThreadState(),state; + + for(;;) { + while(el = qufifo.Get()) { + ++el->th->thrcount; // \todo this should be atomic + state = PyLock(my); + el->th->docall(el->fun,el->args); + Py_XDECREF(el->fun); + Py_XDECREF(el->args); + PyUnlock(state); + --el->th->thrcount; // \todo this should be atomic + qufifo.Free(el); + } + qucond.Wait(); + } + + // we never end +#if 0 + state = PyLock(my); + // unref remaining Python objects + while(el = qufifo.Get()) { + Py_XDECREF(el->fun); + Py_XDECREF(el->args); + qufifo.Free(el); + } + PyUnlock(state); +#endif +} + +void pybase::erasethreads() +{ + PyFifo tmp; + FifoEl *el; + while(el = qufifo.Get()) { + if(el->th == this) { + Py_XDECREF(el->fun); + Py_XDECREF(el->args); + qufifo.Free(el); + } + else + tmp.Put(el); + } + // Push back + while(el = tmp.Get()) qufifo.Put(el); +} +#endif + +bool pybase::collect() +{ + if(gcollect) { + PyObject *ret = PyObject_CallObject(gcollect,NULL); + if(ret) { +#ifdef FLEXT_DEBUG + int refs = PyInt_AsLong(ret); + if(refs) post("py/pyext - Garbage collector reports %i unreachable objects",refs); +#endif + Py_DECREF(ret); + return false; + } + } + return true; +} diff --git a/externals/grill/py/source/pybase.h b/externals/grill/py/source/pybase.h index b5cc8220..61518a2a 100644 --- a/externals/grill/py/source/pybase.h +++ b/externals/grill/py/source/pybase.h @@ -1,228 +1,251 @@ -/* - -py/pyext - python script object for PD and MaxMSP - -Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) -For information on usage and redistribution, and for a DISCLAIMER OF ALL -WARRANTIES, see the file, "license.txt," in this distribution. - -*/ - -#ifndef __PYBASE_H -#define __PYBASE_H - -#include "main.h" -#include "pysymbol.h" -#include "pybuffer.h" -#include "pybundle.h" - -class pybase - : public flext -{ -public: - pybase(); - virtual ~pybase(); - - void Exit(); - - static PyObject *MakePyArgs(const t_symbol *s,int argc,const t_atom *argv,int inlet = -1); - static PyObject *MakePyArg(const t_symbol *s,int argc,const t_atom *argv); - static const t_symbol *GetPyArgs(AtomList &lst,PyObject *pValue,int offs = 0); - static const t_symbol *GetPyAtom(AtomList &lst,PyObject *pValue); - - static void lib_setup(); - -protected: - - virtual void DumpOut(const t_symbol *sym,int argc,const t_atom *argv) = 0; - - void m__dir(PyObject *obj); - void m__doc(PyObject *obj); - - void m_dir() { m__dir(module); } - void mg_dir(AtomList &lst) { m__dir(module); } - void m_doc() { m__doc(dict); } - - std::string modname; // module name - PyObject *module,*dict; // object module and associated dictionary - - static const char *py_doc; - - void GetDir(PyObject *obj,AtomList &lst); - - AtomList args; - - void AddCurrentPath(flext_base *o); - void SetArgs(); - - bool OutObject(flext_base *ext,int o,PyObject *obj); - - // reload module and all connected objects - void Reload(); - - bool ImportModule(const char *name); - void UnimportModule(); - bool ReloadModule(); - - // Get module registry - PyObject *GetRegistry(const char *regname); - // Set module registry - void SetRegistry(const char *regname,PyObject *reg); - - // Register object - void Register(PyObject *reg); - // Unregister object - void Unregister(PyObject *reg); - - virtual void LoadModule() = 0; - virtual void UnloadModule() = 0; - - virtual void Load() = 0; - virtual void Unload() = 0; - - void OpenEditor(); - - void Respond(bool b) - { - if(respond) { - t_atom a; - SetBool(a,b); - DumpOut(sym_response,1,&a); - } - } - - void Report() { while(PyErr_Occurred()) PyErr_Print(); } - - static bool IsAnything(const t_symbol *s) { return s && s != sym_float && s != sym_int && s != sym_symbol && s != sym_list && s != sym_pointer; } - static bool IsAtom(const t_symbol *s) { return s == sym_float || s == sym_int || s == sym_symbol || s == sym_pointer; } - -// enum retval { nothing,atom,sequ }; - - // --- module stuff ----- - - static PyObject *module_obj,*module_dict; - static PyObject *builtins_obj,*builtins_dict; - static PyMethodDef func_tbl[]; - - static PyObject *py__doc__(PyObject *,PyObject *args); - static PyObject *py_send(PyObject *,PyObject *args); -#ifdef FLEXT_THREADS - static PyObject *py_priority(PyObject *,PyObject *args); -#endif - - static PyObject *py_arraysupport(PyObject *,PyObject *args); - static PyObject *py_samplerate(PyObject *,PyObject *args); - static PyObject *py_blocksize(PyObject *,PyObject *args); - -#if FLEXT_SYS == FLEXT_SYS_PD - static PyObject *py_getvalue(PyObject *,PyObject *args); - static PyObject *py_setvalue(PyObject *,PyObject *args); -#endif - - static PyObject *py_list(PyObject *,PyObject *args); - static PyObject *py_tuple(PyObject *,PyObject *args); - - // ----thread stuff ------------ - - virtual void m_stop(int argc,const t_atom *argv); - - bool respond; -#ifdef FLEXT_THREADS - int thrcount; - bool shouldexit; - int stoptick; - Timer stoptmr; - - void tick(void *); -#endif - - int detach; - bool pymsg; - - bool gencall(PyObject *fun,PyObject *args); - - bool docall(PyObject *fun,PyObject *args) - { - callpy(fun,args); - if(PyErr_Occurred()) { - exchandle(); - return false; - } - else - return true; - } - - virtual void callpy(PyObject *fun,PyObject *args) = 0; - - void exchandle(); - - static bool collect(); - -protected: - -#ifdef FLEXT_THREADS - static void thrworker(thr_params *data); - - bool qucall(PyObject *fun,PyObject *args) - { - FifoEl *el = qufifo.New(); - el->Set(this,fun,args); - qufifo.Put(el); - qucond.Signal(); - return true; - } - - static void quworker(thr_params *); - void erasethreads(); - - static PyFifo qufifo; - static ThrCond qucond; - static PyThreadState *pythrsys; -#endif - - static const t_symbol *sym_fint; // float or int symbol, depending on native number message type - static const t_symbol *sym_response; - - static const t_symbol *getone(t_atom &at,PyObject *arg); - static const t_symbol *getlist(t_atom *lst,PyObject *seq,int cnt,int offs = 0); - -public: - - static void AddToPath(const char *dir); - -#ifdef FLEXT_THREADS - static PyThreadState *FindThreadState(); - static void FreeThreadState(); - - // this is especially needed when one py/pyext object calls another one - // we don't want the message to be queued, but otoh we have to avoid deadlock - // (recursive calls can only happen in the system thread) - static int lockcount; - - static PyThreadState *PyLock(PyThreadState *st = FindThreadState()) - { - if(!IsSystemThread() || !lockcount++) PyEval_AcquireLock(); - return PyThreadState_Swap(st); - } - - static PyThreadState *PyLockSys() - { - if(!lockcount++) PyEval_AcquireLock(); - return PyThreadState_Swap(pythrsys); - } - - static void PyUnlock(PyThreadState *st) - { - PyThreadState *old = PyThreadState_Swap(st); - if(old != pythrsys || !--lockcount) PyEval_ReleaseLock(); - } -#else - static PyThreadState *PyLock(PyThreadState * = NULL) { return NULL; } - static PyThreadState *PyLockSys() { return NULL; } - static void PyUnlock(PyThreadState *st) {} -#endif - - static PyObject* StdOut_Write(PyObject* Self, PyObject* Args); -}; - -#endif +/* + +py/pyext - python script object for PD and MaxMSP + +Copyright (c)2002-2007 Thomas Grill (gr@grrrr.org) +For information on usage and redistribution, and for a DISCLAIMER OF ALL +WARRANTIES, see the file, "license.txt," in this distribution. + +*/ + +#ifndef __PYBASE_H +#define __PYBASE_H + +#include "main.h" +#include "pysymbol.h" +#include "pybuffer.h" +#include "pybundle.h" + +#ifdef PY_USE_GIL + typedef PyGILState_STATE ThrState; +#else + typedef PyThreadState *ThrState; +#endif + +class pybase + : public flext +{ +public: + pybase(); + virtual ~pybase(); + + void Exit(); + + static PyObject *MakePyArgs(const t_symbol *s,int argc,const t_atom *argv,int inlet = -1); + static PyObject *MakePyArg(const t_symbol *s,int argc,const t_atom *argv); + static const t_symbol *GetPyArgs(AtomList &lst,PyObject *pValue,int offs = 0); + static const t_symbol *GetPyAtom(AtomList &lst,PyObject *pValue); + + static void lib_setup(); + +protected: + + virtual void DumpOut(const t_symbol *sym,int argc,const t_atom *argv) = 0; + + void m__dir(PyObject *obj); + void m__doc(PyObject *obj); + + void m_dir() { m__dir(module); } + void mg_dir(AtomList &lst) { m__dir(module); } + void m_doc() { m__doc(dict); } + + std::string modname; // module name + PyObject *module,*dict; // object module and associated dictionary + + static const char *py_doc; + + void GetDir(PyObject *obj,AtomList &lst); + + AtomList args; + + void AddCurrentPath(flext_base *o); + void SetArgs(); + + bool OutObject(flext_base *ext,int o,PyObject *obj); + + // reload module and all connected objects + void Reload(); + + bool ImportModule(const char *name); + void UnimportModule(); + bool ReloadModule(); + + // Get module registry + PyObject *GetRegistry(const char *regname); + // Set module registry + void SetRegistry(const char *regname,PyObject *reg); + + // Register object + void Register(PyObject *reg); + // Unregister object + void Unregister(PyObject *reg); + + virtual void LoadModule() = 0; + virtual void UnloadModule() = 0; + + virtual void Load() = 0; + virtual void Unload() = 0; + + void OpenEditor(); + + void Respond(bool b) + { + if(respond) { + t_atom a; + SetBool(a,b); + DumpOut(sym_response,1,&a); + } + } + + void Report() { while(PyErr_Occurred()) PyErr_Print(); } + + static bool IsAnything(const t_symbol *s) { return s && s != sym_float && s != sym_int && s != sym_symbol && s != sym_list && s != sym_pointer; } + static bool IsAtom(const t_symbol *s) { return s == sym_float || s == sym_int || s == sym_symbol || s == sym_pointer; } + +// enum retval { nothing,atom,sequ }; + + // --- module stuff ----- + + static PyObject *module_obj,*module_dict; + static PyObject *builtins_obj,*builtins_dict; + static PyMethodDef func_tbl[]; + + static PyObject *py__doc__(PyObject *,PyObject *args); + static PyObject *py_send(PyObject *,PyObject *args); +#ifdef FLEXT_THREADS + static PyObject *py_priority(PyObject *,PyObject *args); +#endif + + static PyObject *py_arraysupport(PyObject *,PyObject *args); + static PyObject *py_samplerate(PyObject *,PyObject *args); + static PyObject *py_blocksize(PyObject *,PyObject *args); + +#if FLEXT_SYS == FLEXT_SYS_PD + static PyObject *py_getvalue(PyObject *,PyObject *args); + static PyObject *py_setvalue(PyObject *,PyObject *args); +#endif + + static PyObject *py_list(PyObject *,PyObject *args); + static PyObject *py_tuple(PyObject *,PyObject *args); + + // ----thread stuff ------------ + + virtual void m_stop(int argc,const t_atom *argv); + + bool respond; +#ifdef FLEXT_THREADS + int thrcount; + bool shouldexit; + int stoptick; + Timer stoptmr; + + void tick(void *); +#endif + + int detach; + bool pymsg; + + bool gencall(PyObject *fun,PyObject *args); + + bool docall(PyObject *fun,PyObject *args) + { + callpy(fun,args); + if(PyErr_Occurred()) { + exchandle(); + return false; + } + else + return true; + } + + virtual void callpy(PyObject *fun,PyObject *args) = 0; + + void exchandle(); + + static bool collect(); + +protected: + +#ifdef FLEXT_THREADS + static void thrworker(thr_params *data); + + bool qucall(PyObject *fun,PyObject *args) + { + FifoEl *el = qufifo.New(); + el->Set(this,fun,args); + qufifo.Put(el); + qucond.Signal(); + return true; + } + + static void quworker(thr_params *); + static void pyworker(thr_params *); + void erasethreads(); + + static PyFifo qufifo; + static ThrCond qucond; + +#ifndef PY_USE_GIL + static ThrState pythrsys; +#endif +#endif + + static const t_symbol *sym_fint; // float or int symbol, depending on native number message type + static const t_symbol *sym_response; + + static const t_symbol *getone(t_atom &at,PyObject *arg); + static const t_symbol *getlist(t_atom *lst,PyObject *seq,int cnt,int offs = 0); + +public: + + static void AddToPath(const char *dir); + +#ifdef FLEXT_THREADS + // this is especially needed when one py/pyext object calls another one + // we don't want the message to be queued, but otoh we have to avoid deadlock + // (recursive calls can only happen in the system thread) + static int lockcount; + +#ifdef PY_USE_GIL + static inline ThrState FindThreadState() { return ThrState(); } + + static inline ThrState PyLock(ThrState = ThrState()) { return PyGILState_Ensure(); } + static inline ThrState PyLockSys() { return PyLock(); } + static inline void PyUnlock(ThrState st) { PyGILState_Release(st); } +#else // PY_USE_GIL + static ThrState FindThreadState(); + static void FreeThreadState(); + + static ThrState PyLock(ThrState st = FindThreadState()) + { + if(!IsSystemThread() || !lockcount++) PyEval_AcquireLock(); + return PyThreadState_Swap(st); + } + +#if 1 + static inline ThrState PyLockSys() { return PyLock(); } +#else + static ThrState PyLockSys() + { + if(!lockcount++) PyEval_AcquireLock(); + return PyThreadState_Swap(pythrsys); + } +#endif + + static void PyUnlock(ThrState st) + { + ThrState old = PyThreadState_Swap(st); + if(old != pythrsys || !--lockcount) PyEval_ReleaseLock(); + } +#endif // PY_USE_GIL + +#else // FLEXT_THREADS + static inline ThrState PyLock(ThrState = NULL) { return NULL; } + static inline ThrState PyLockSys() { return NULL; } + static inline void PyUnlock(ThrState st) {} +#endif + + static PyObject* StdOut_Write(PyObject* Self, PyObject* Args); +}; + +#endif diff --git a/externals/grill/py/source/pybundle.cpp b/externals/grill/py/source/pybundle.cpp index 69f5d90c..ee2e16c0 100644 --- a/externals/grill/py/source/pybundle.cpp +++ b/externals/grill/py/source/pybundle.cpp @@ -1,232 +1,232 @@ -/* - -py/pyext - python script object for PD and Max/MSP - -Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) -For information on usage and redistribution, and for a DISCLAIMER OF ALL -WARRANTIES, see the file, "license.txt," in this distribution. - -*/ - -#include "pyprefix.h" -#include "pybundle.h" -#include "pyext.h" - -static PyObject *bundle_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - pyBundle *self = (pyBundle *)pyBundle_Type.tp_alloc(&pyBundle_Type, 0); - if(self) self->bundle = flext::MsgNew(); - return (PyObject *)self; -} - -static int bundle_init(PyObject *self, PyObject *args, PyObject *kwds) -{ - FLEXT_ASSERT(pyBundle_Check(self)); - - int len = PySequence_Length(args); - if(len) { - PyErr_SetString(PyExc_TypeError,"no arguments expected"); - return -1; - } - - return 0; -} - -static void bundle_dealloc(PyObject *obj) -{ - pyBundle *self = (pyBundle *)obj; - if(self->bundle) flext::MsgFree(self->bundle); - obj->ob_type->tp_free(obj); -} - -static PyObject *bundle_send(PyObject *obj) -{ - pyBundle *self = (pyBundle *)obj; - if(self->bundle) { - flext::ToOutMsg(self->bundle); - self->bundle = NULL; - - Py_INCREF(obj); - return obj; - } - else { - PyErr_SetString(PyExc_RuntimeError,"already sent"); - return NULL; - } -} - -static PyObject *bundle_repr(PyObject *self) -{ - FLEXT_ASSERT(pyBundle_Check(self)); - return (PyObject *)PyString_FromFormat("",pyBundle_AS_BUNDLE(self)); -} - -static PyObject *bundle_str(PyObject *self) -{ - return bundle_repr(self); -} - -static PyObject *bundle_richcompare(PyObject *a,PyObject *b,int cmp) -{ - if(pyBundle_Check(a) && pyBundle_Check(b)) { - const flext::MsgBundle *abnd = pyBundle_AS_BUNDLE(a); - const flext::MsgBundle *bbnd = pyBundle_AS_BUNDLE(b); - bool ret; - switch(cmp) { - case Py_LT: ret = abnd < bbnd; break; - case Py_LE: ret = abnd <= bbnd; break; - case Py_EQ: ret = abnd == bbnd; break; - case Py_NE: ret = abnd != bbnd; break; - case Py_GT: ret = abnd > bbnd; break; - case Py_GE: ret = abnd >= bbnd; break; - } - return PyBool_FromLong(ret); - } +/* + +py/pyext - python script object for PD and Max/MSP + +Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) +For information on usage and redistribution, and for a DISCLAIMER OF ALL +WARRANTIES, see the file, "license.txt," in this distribution. + +*/ + +#include "pyprefix.h" +#include "pybundle.h" +#include "pyext.h" + +static PyObject *bundle_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + pyBundle *self = (pyBundle *)pyBundle_Type.tp_alloc(&pyBundle_Type, 0); + if(self) self->bundle = flext::MsgNew(); + return (PyObject *)self; +} + +static int bundle_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + FLEXT_ASSERT(pyBundle_Check(self)); + + int len = PySequence_Length(args); + if(len) { + PyErr_SetString(PyExc_TypeError,"no arguments expected"); + return -1; + } + + return 0; +} + +static void bundle_dealloc(PyObject *obj) +{ + pyBundle *self = (pyBundle *)obj; + if(self->bundle) flext::MsgFree(self->bundle); + obj->ob_type->tp_free(obj); +} + +static PyObject *bundle_send(PyObject *obj) +{ + pyBundle *self = (pyBundle *)obj; + if(self->bundle) { + flext::ToOutMsg(self->bundle); + self->bundle = NULL; + + Py_INCREF(obj); + return obj; + } + else { + PyErr_SetString(PyExc_RuntimeError,"already sent"); + return NULL; + } +} + +static PyObject *bundle_repr(PyObject *self) +{ + FLEXT_ASSERT(pyBundle_Check(self)); + return (PyObject *)PyString_FromFormat("",pyBundle_AS_BUNDLE(self)); +} + +static PyObject *bundle_str(PyObject *self) +{ + return bundle_repr(self); +} + +static PyObject *bundle_richcompare(PyObject *a,PyObject *b,int cmp) +{ + if(pyBundle_Check(a) && pyBundle_Check(b)) { + const flext::MsgBundle *abnd = pyBundle_AS_BUNDLE(a); + const flext::MsgBundle *bbnd = pyBundle_AS_BUNDLE(b); + bool ret; + switch(cmp) { + case Py_LT: ret = abnd < bbnd; break; + case Py_LE: ret = abnd <= bbnd; break; + case Py_EQ: ret = abnd == bbnd; break; + case Py_NE: ret = abnd != bbnd; break; + case Py_GT: ret = abnd > bbnd; break; + case Py_GE: ret = abnd >= bbnd; break; + } + return PyBool_FromLong(ret); + } Py_INCREF(Py_NotImplemented); return Py_NotImplemented; -} - -static long bundle_hash(PyObject *self) -{ - FLEXT_ASSERT(pyBundle_Check(self)); - return (long)pyBundle_AS_BUNDLE(self); -} - - -static PyObject *bundle_append(PyObject *self,PyObject *args) -{ - flext::MsgBundle *b = pyBundle_AS_BUNDLE(self); - if(b) { - int sz = PyTuple_GET_SIZE(args),offs = 0; - PyObject *tg,*outl; - pyext *ext = NULL; - const t_symbol *recv; - int o; - - if(sz > 2 && - (tg = PyTuple_GET_ITEM(args,0)) != NULL && PyInstance_Check(tg) && - (outl = PyTuple_GET_ITEM(args,1)) != NULL && PyInt_Check(outl) - ) { - // Sending to outlet - ext = pyext::GetThis(tg); - o = PyInt_AS_LONG(outl); - - if(o < 1 || o > ext->Outlets()) { - PyErr_SetString(PyExc_ValueError,"Outlet index out of range"); - return NULL; - } - - offs += 2; - } - else if(sz > 1 && - (tg = PyTuple_GET_ITEM(args,0)) != NULL && (recv = pyObject_AsSymbol(tg)) != NULL - ) { - // Sending to receiver - offs++; - } - else { - // not recognized - PyErr_SetString(PyExc_SyntaxError,"Unrecognized arguments"); - return NULL; - } - - PyObject *val; - if(sz-offs == 1) { - val = PyTuple_GET_ITEM(args,offs); // borrow reference - Py_INCREF(val); - } - else - val = PyTuple_GetSlice(args,offs,sz); // new ref - - flext::AtomListStatic<16> lst; - const t_symbol *sym = pybase::GetPyArgs(lst,val); - Py_DECREF(val); - - if(sym) { - if(ext) { - FLEXT_ASSERT(outl); - ext->MsgAddAnything(b,o-1,sym,lst.Count(),lst.Atoms()); - } - else { - FLEXT_ASSERT(sym); - if(!flext::MsgForward(b,recv,sym,lst.Count(),lst.Atoms())) { - PyErr_SetString(PyExc_ValueError,"Receiver not found"); - return NULL; - } - } - - Py_INCREF(Py_None); - return Py_None; - } - else { - FLEXT_ASSERT(PyErr_Occurred()); - return NULL; - } - - Py_INCREF(self); - return self; - } - else { - PyErr_SetString(PyExc_RuntimeError,"Invalid bundle"); - return NULL; - } -} - -static PyMethodDef bundle_methods[] = { - {"append", (PyCFunction)bundle_append,METH_VARARGS,"Append message to bundle"}, - {"send", (PyCFunction)bundle_send,METH_NOARGS,"Send bundle"}, - {NULL} /* Sentinel */ -}; - - - -PyTypeObject pyBundle_Type = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - "Bundle", /*tp_name*/ - sizeof(pyBundle), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - bundle_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - bundle_repr, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - bundle_hash, /*tp_hash */ - 0, /*tp_call*/ - bundle_str, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT /*| Py_TPFLAGS_BASETYPE*/, /*tp_flags*/ - "Bundle objects", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - bundle_richcompare, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - bundle_methods, /* 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 */ - bundle_init, /* tp_init */ - 0, /* tp_alloc */ - bundle_new, /* tp_new */ -}; - - -void initbundle() -{ - if(PyType_Ready(&pyBundle_Type) < 0) - FLEXT_ASSERT(false); - else - Py_INCREF(&pyBundle_Type); -} +} + +static long bundle_hash(PyObject *self) +{ + FLEXT_ASSERT(pyBundle_Check(self)); + return (long)pyBundle_AS_BUNDLE(self); +} + + +static PyObject *bundle_append(PyObject *self,PyObject *args) +{ + flext::MsgBundle *b = pyBundle_AS_BUNDLE(self); + if(b) { + int sz = PyTuple_GET_SIZE(args),offs = 0; + PyObject *tg,*outl; + pyext *ext = NULL; + const t_symbol *recv; + int o; + + if(sz > 2 && + (tg = PyTuple_GET_ITEM(args,0)) != NULL && PyInstance_Check(tg) && + (outl = PyTuple_GET_ITEM(args,1)) != NULL && PyInt_Check(outl) + ) { + // Sending to outlet + ext = pyext::GetThis(tg); + o = PyInt_AS_LONG(outl); + + if(o < 1 || o > ext->Outlets()) { + PyErr_SetString(PyExc_ValueError,"Outlet index out of range"); + return NULL; + } + + offs += 2; + } + else if(sz > 1 && + (tg = PyTuple_GET_ITEM(args,0)) != NULL && (recv = pyObject_AsSymbol(tg)) != NULL + ) { + // Sending to receiver + offs++; + } + else { + // not recognized + PyErr_SetString(PyExc_SyntaxError,"Unrecognized arguments"); + return NULL; + } + + PyObject *val; + if(sz-offs == 1) { + val = PyTuple_GET_ITEM(args,offs); // borrow reference + Py_INCREF(val); + } + else + val = PyTuple_GetSlice(args,offs,sz); // new ref + + flext::AtomListStatic<16> lst; + const t_symbol *sym = pybase::GetPyArgs(lst,val); + Py_DECREF(val); + + if(sym) { + if(ext) { + FLEXT_ASSERT(outl); + ext->MsgAddAnything(b,o-1,sym,lst.Count(),lst.Atoms()); + } + else { + FLEXT_ASSERT(sym); + if(!flext::MsgForward(b,recv,sym,lst.Count(),lst.Atoms())) { + PyErr_SetString(PyExc_ValueError,"Receiver not found"); + return NULL; + } + } + + Py_INCREF(Py_None); + return Py_None; + } + else { + FLEXT_ASSERT(PyErr_Occurred()); + return NULL; + } + + Py_INCREF(self); + return self; + } + else { + PyErr_SetString(PyExc_RuntimeError,"Invalid bundle"); + return NULL; + } +} + +static PyMethodDef bundle_methods[] = { + {"append", (PyCFunction)bundle_append,METH_VARARGS,"Append message to bundle"}, + {"send", (PyCFunction)bundle_send,METH_NOARGS,"Send bundle"}, + {NULL} /* Sentinel */ +}; + + + +PyTypeObject pyBundle_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Bundle", /*tp_name*/ + sizeof(pyBundle), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + bundle_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + bundle_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + bundle_hash, /*tp_hash */ + 0, /*tp_call*/ + bundle_str, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT /*| Py_TPFLAGS_BASETYPE*/, /*tp_flags*/ + "Bundle objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + bundle_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + bundle_methods, /* 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 */ + bundle_init, /* tp_init */ + 0, /* tp_alloc */ + bundle_new, /* tp_new */ +}; + + +void initbundle() +{ + if(PyType_Ready(&pyBundle_Type) < 0) + FLEXT_ASSERT(false); + else + Py_INCREF(&pyBundle_Type); +} diff --git a/externals/grill/py/source/pybundle.h b/externals/grill/py/source/pybundle.h index 5978cc38..1e840eb5 100644 --- a/externals/grill/py/source/pybundle.h +++ b/externals/grill/py/source/pybundle.h @@ -1,16 +1,16 @@ -/* - -py/pyext - python script object for PD and Max/MSP - -Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) -For information on usage and redistribution, and for a DISCLAIMER OF ALL -WARRANTIES, see the file, "license.txt," in this distribution. - -*/ - -#ifndef __PYBUNDLE_H -#define __PYBUNDLE_H - +/* + +py/pyext - python script object for PD and Max/MSP + +Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) +For information on usage and redistribution, and for a DISCLAIMER OF ALL +WARRANTIES, see the file, "license.txt," in this distribution. + +*/ + +#ifndef __PYBUNDLE_H +#define __PYBUNDLE_H + #include #if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 500) @@ -33,28 +33,28 @@ WARRANTIES, see the file, "license.txt," in this distribution. #else #define PY_EXPORT #endif - -typedef struct { - PyObject_HEAD - /* Type-specific fields go here. */ - flext::MsgBundle *bundle; -} pyBundle; - -PY_EXPORT extern PyTypeObject pyBundle_Type; - + +typedef struct { + PyObject_HEAD + /* Type-specific fields go here. */ + flext::MsgBundle *bundle; +} pyBundle; + +PY_EXPORT extern PyTypeObject pyBundle_Type; + #define pyBundle_Check(op) PyObject_TypeCheck(op, &pyBundle_Type) #define pyBundle_CheckExact(op) ((op)->ob_type == &pyBundle_Type) -inline flext::MsgBundle *pyBundle_AS_BUNDLE(PyObject *op) -{ - return ((pyBundle *)op)->bundle; -} - -inline flext::MsgBundle *pyBundle_AsBundle(PyObject *op) -{ - return pyBundle_Check(op)?pyBundle_AS_BUNDLE(op):NULL; -} - +inline flext::MsgBundle *pyBundle_AS_BUNDLE(PyObject *op) +{ + return ((pyBundle *)op)->bundle; +} + +inline flext::MsgBundle *pyBundle_AsBundle(PyObject *op) +{ + return pyBundle_Check(op)?pyBundle_AS_BUNDLE(op):NULL; +} -#endif + +#endif diff --git a/externals/grill/py/source/pydsp.cpp b/externals/grill/py/source/pydsp.cpp index a88a3956..07fa75af 100644 --- a/externals/grill/py/source/pydsp.cpp +++ b/externals/grill/py/source/pydsp.cpp @@ -1,190 +1,190 @@ -/* - -py/pyext - python script object for PD and Max/MSP - -Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) -For information on usage and redistribution, and for a DISCLAIMER OF ALL -WARRANTIES, see the file, "license.txt," in this distribution. - -*/ - -#ifndef PY_NODSP - -#include "pyext.h" - -class pydsp - : public pyext -{ - FLEXT_HEADER(pydsp,pyext) -public: - pydsp(int argc,const t_atom *argv); - -protected: - virtual bool CbDsp(); - virtual void CbSignal(); - - virtual bool DoInit(); - virtual void DoExit(); - - virtual PyObject *GetSig(int ix,bool in); - - void NewBuffers(); - void FreeBuffers(); - - PyObject *dspfun,*sigfun; - PyObject **buffers; -}; - -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) -{} - -bool pydsp::DoInit() -{ - if(!pyext::DoInit()) return false; - - if(pyobj) - { - dspfun = PyObject_GetAttrString(pyobj,"_dsp"); // get ref - if(!dspfun) - PyErr_Clear(); - else if(!PyMethod_Check(dspfun)) { - Py_DECREF(dspfun); - dspfun = NULL; - } - } - return true; -} - -void pydsp::DoExit() -{ - if(dspfun) { Py_DECREF(dspfun); dspfun = NULL; } - if(sigfun) { Py_DECREF(sigfun); sigfun = NULL; } - - FreeBuffers(); -} - -PyObject *arrayfrombuffer(PyObject *buf,int c,int n); - -void pydsp::NewBuffers() -{ - int i,n = Blocksize(); - const int ins = CntInSig(),outs = CntOutSig(); - t_sample *const *insigs = InSig(); - t_sample *const *outsigs = OutSig(); - - // inlet/outlet count can't change so we don't have to deallocate - if(!buffers) { - int cnt = ins+outs; - buffers = new PyObject *[cnt]; - memset(buffers,0,cnt*sizeof(*buffers)); - } - - for(i = 0; i < ins; ++i) { - Py_XDECREF(buffers[i]); - PyObject *b = PyBuffer_FromReadWriteMemory(insigs[i],n*sizeof(t_sample)); - buffers[i] = arrayfrombuffer(b,1,n); - Py_DECREF(b); - } - for(i = 0; i < outs; ++i) { - Py_XDECREF(buffers[ins+i]); - if(i < ins && outsigs[i] == insigs[i]) { - // same vectors - share the objects! - buffers[ins+i] = buffers[i]; - Py_XINCREF(buffers[i]); - } - else { - PyObject *b = PyBuffer_FromReadWriteMemory(outsigs[i],n*sizeof(t_sample)); - buffers[ins+i] = arrayfrombuffer(b,1,n); - Py_DECREF(b); - } - } -} - -void pydsp::FreeBuffers() -{ - if(buffers) { - int cnt = CntInSig()+CntOutSig(); - for(int i = 0; i < cnt; ++i) Py_XDECREF(buffers[i]); - delete[] buffers; - buffers = NULL; - } -} - -bool pydsp::CbDsp() -{ - if(pyobj && (CntInSig() || CntOutSig())) - { - PyThreadState *state = PyLockSys(); - - NewBuffers(); - - bool dodsp = true; - if(dspfun) { - PyObject *ret = PyObject_CallObject(dspfun,NULL); - if(ret) { - dodsp = PyObject_IsTrue(ret) != 0; - Py_DECREF(ret); - } - else { -#ifdef FLEXT_DEBUG - PyErr_Print(); -#else - PyErr_Clear(); -#endif - } - } - - // do that here instead of where dspfun is initialized, so that - // _signal can be assigned in _dsp - // optimizations may be done there to assign the right _signal version - Py_XDECREF(sigfun); - - if(dodsp) { - sigfun = PyObject_GetAttrString(pyobj,"_signal"); // get ref - if(!sigfun) - PyErr_Clear(); - else if(!PyMethod_Check(sigfun)) { - Py_DECREF(sigfun); - sigfun = NULL; - } - } - else - sigfun = NULL; - - PyUnlock(state); - return sigfun != NULL; - } - else - // switch on dsp only if there are signal inlets or outlets - return false; -} - -void pydsp::CbSignal() -{ - PyThreadState *state = PyLockSys(); - PyObject *ret = PyObject_CallObject(sigfun,NULL); - - if(ret) - Py_DECREF(ret); - else { -#ifdef FLEXT_DEBUG - PyErr_Print(); -#else - PyErr_Clear(); -#endif - } - PyUnlock(state); -} - -PyObject *pydsp::GetSig(int ix,bool in) -{ - PyObject *r = buffers[in?ix:CntInSig()+ix]; - Py_XINCREF(r); - return r; -} - -#endif +/* + +py/pyext - python script object for PD and Max/MSP + +Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) +For information on usage and redistribution, and for a DISCLAIMER OF ALL +WARRANTIES, see the file, "license.txt," in this distribution. + +*/ + +#ifndef PY_NODSP + +#include "pyext.h" + +class pydsp + : public pyext +{ + FLEXT_HEADER(pydsp,pyext) +public: + pydsp(int argc,const t_atom *argv); + +protected: + virtual bool CbDsp(); + virtual void CbSignal(); + + virtual bool DoInit(); + virtual void DoExit(); + + virtual PyObject *GetSig(int ix,bool in); + + void NewBuffers(); + void FreeBuffers(); + + PyObject *dspfun,*sigfun; + PyObject **buffers; +}; + +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) +{} + +bool pydsp::DoInit() +{ + if(!pyext::DoInit()) return false; + + if(pyobj) + { + dspfun = PyObject_GetAttrString(pyobj,"_dsp"); // get ref + if(!dspfun) + PyErr_Clear(); + else if(!PyMethod_Check(dspfun)) { + Py_DECREF(dspfun); + dspfun = NULL; + } + } + return true; +} + +void pydsp::DoExit() +{ + if(dspfun) { Py_DECREF(dspfun); dspfun = NULL; } + if(sigfun) { Py_DECREF(sigfun); sigfun = NULL; } + + FreeBuffers(); +} + +PyObject *arrayfrombuffer(PyObject *buf,int c,int n); + +void pydsp::NewBuffers() +{ + int i,n = Blocksize(); + const int ins = CntInSig(),outs = CntOutSig(); + t_sample *const *insigs = InSig(); + t_sample *const *outsigs = OutSig(); + + // inlet/outlet count can't change so we don't have to deallocate + if(!buffers) { + int cnt = ins+outs; + buffers = new PyObject *[cnt]; + memset(buffers,0,cnt*sizeof(*buffers)); + } + + for(i = 0; i < ins; ++i) { + Py_XDECREF(buffers[i]); + PyObject *b = PyBuffer_FromReadWriteMemory(insigs[i],n*sizeof(t_sample)); + buffers[i] = arrayfrombuffer(b,1,n); + Py_DECREF(b); + } + for(i = 0; i < outs; ++i) { + Py_XDECREF(buffers[ins+i]); + if(i < ins && outsigs[i] == insigs[i]) { + // same vectors - share the objects! + buffers[ins+i] = buffers[i]; + Py_XINCREF(buffers[i]); + } + else { + PyObject *b = PyBuffer_FromReadWriteMemory(outsigs[i],n*sizeof(t_sample)); + buffers[ins+i] = arrayfrombuffer(b,1,n); + Py_DECREF(b); + } + } +} + +void pydsp::FreeBuffers() +{ + if(buffers) { + int cnt = CntInSig()+CntOutSig(); + for(int i = 0; i < cnt; ++i) Py_XDECREF(buffers[i]); + delete[] buffers; + buffers = NULL; + } +} + +bool pydsp::CbDsp() +{ + if(pyobj && (CntInSig() || CntOutSig())) + { + ThrState state = PyLockSys(); + + NewBuffers(); + + bool dodsp = true; + if(dspfun) { + PyObject *ret = PyObject_CallObject(dspfun,NULL); + if(ret) { + dodsp = PyObject_IsTrue(ret) != 0; + Py_DECREF(ret); + } + else { +#ifdef FLEXT_DEBUG + PyErr_Print(); +#else + PyErr_Clear(); +#endif + } + } + + // do that here instead of where dspfun is initialized, so that + // _signal can be assigned in _dsp + // optimizations may be done there to assign the right _signal version + Py_XDECREF(sigfun); + + if(dodsp) { + sigfun = PyObject_GetAttrString(pyobj,"_signal"); // get ref + if(!sigfun) + PyErr_Clear(); + else if(!PyMethod_Check(sigfun)) { + Py_DECREF(sigfun); + sigfun = NULL; + } + } + else + sigfun = NULL; + + PyUnlock(state); + return sigfun != NULL; + } + else + // switch on dsp only if there are signal inlets or outlets + return false; +} + +void pydsp::CbSignal() +{ + ThrState state = PyLockSys(); + PyObject *ret = PyObject_CallObject(sigfun,NULL); + + if(ret) + Py_DECREF(ret); + else { +#ifdef FLEXT_DEBUG + PyErr_Print(); +#else + PyErr_Clear(); +#endif + } + PyUnlock(state); +} + +PyObject *pydsp::GetSig(int ix,bool in) +{ + PyObject *r = buffers[in?ix:CntInSig()+ix]; + Py_XINCREF(r); + return r; +} + +#endif diff --git a/externals/grill/py/source/pyext.cpp b/externals/grill/py/source/pyext.cpp index 5c573bd2..31945277 100644 --- a/externals/grill/py/source/pyext.cpp +++ b/externals/grill/py/source/pyext.cpp @@ -117,8 +117,10 @@ pyext::pyext(int argc,const t_atom *argv,bool sig): methname(NULL), pyobj(NULL), inlets(-1),outlets(-1), - siginlets(0),sigoutlets(0), - pythr(NULL) + siginlets(0),sigoutlets(0) +#ifndef PY_USE_GIL + ,pythr(NULL) +#endif { #ifdef FLEXT_THREADS FLEXT_ADDTIMER(stoptmr,tick); @@ -141,7 +143,7 @@ pyext::pyext(int argc,const t_atom *argv,bool sig): // check if the object name is pyext. , pyx. or similar bool dotted = strrchr(thisName(),'.') != NULL; - PyThreadState *state = PyLockSys(); + ThrState state = PyLockSys(); // init script module if(argc) { @@ -194,7 +196,7 @@ pyext::pyext(int argc,const t_atom *argv,bool sig): bool pyext::Init() { - PyThreadState *state = PyLockSys(); + ThrState state = PyLockSys(); if(methname) { MakeInstance(); @@ -221,7 +223,7 @@ void pyext::Exit() { pybase::Exit(); // exit threads - PyThreadState *state = PyLockSys(); + ThrState state = PyLockSys(); DoExit(); Unregister(GetRegistry(REGNAME)); @@ -414,7 +416,7 @@ void pyext::Unload() void pyext::m_get(const t_symbol *s) { - PyThreadState *state = PyLockSys(); + ThrState state = PyLockSys(); PyObject *pvar = PyObject_GetAttrString(pyobj,const_cast(GetString(s))); /* fetch bound method */ if(pvar) { @@ -437,7 +439,7 @@ void pyext::m_get(const t_symbol *s) void pyext::m_set(int argc,const t_atom *argv) { - PyThreadState *state = PyLockSys(); + ThrState state = PyLockSys(); if(argc < 2 || !IsString(argv[0])) post("%s - Syntax: set varname arguments...",thisName()); @@ -542,7 +544,7 @@ bool pyext::work(int n,const t_symbol *s,int argc,const t_atom *argv) { bool ret = false; - PyThreadState *state = PyLock(); + ThrState state = PyLock(); // should be enough... char str[256]; diff --git a/externals/grill/py/source/pyext.h b/externals/grill/py/source/pyext.h index 4696c72c..da08aa53 100644 --- a/externals/grill/py/source/pyext.h +++ b/externals/grill/py/source/pyext.h @@ -114,7 +114,9 @@ private: virtual void callpy(PyObject *fun,PyObject *args); static bool stcallpy(PyObject *fun,PyObject *args); - PyThreadState *pythr; +#ifndef PY_USE_GIL + ThrState pythr; +#endif private: static bool boundmeth(flext_base *,t_symbol *sym,int argc,t_atom *argv,void *data); diff --git a/externals/grill/py/source/pymeth.cpp b/externals/grill/py/source/pymeth.cpp index 3bc90ee2..ff0d6b83 100644 --- a/externals/grill/py/source/pymeth.cpp +++ b/externals/grill/py/source/pymeth.cpp @@ -1,428 +1,428 @@ -/* - -py/pyext - python script object for PD and Max/MSP - -Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) -For information on usage and redistribution, and for a DISCLAIMER OF ALL -WARRANTIES, see the file, "license.txt," in this distribution. - -*/ - -#include "pybase.h" -#include - -struct xlt { const t_symbol *from,*to; }; - -static const xlt xtdefs[] = { - { flext::MakeSymbol("+"),flext::MakeSymbol("__add__") }, - { flext::MakeSymbol("+="),flext::MakeSymbol("__iadd__") }, - { flext::MakeSymbol("!+"),flext::MakeSymbol("__radd__") }, - { flext::MakeSymbol("-"),flext::MakeSymbol("__sub__") }, - { flext::MakeSymbol("-="),flext::MakeSymbol("__isub__") }, - { flext::MakeSymbol("!-"),flext::MakeSymbol("__rsub__") }, - { flext::MakeSymbol("*"),flext::MakeSymbol("__mul__") }, - { flext::MakeSymbol("*="),flext::MakeSymbol("__imul__") }, - { flext::MakeSymbol("!*"),flext::MakeSymbol("__rmul__") }, - { flext::MakeSymbol("/"),flext::MakeSymbol("__div__") }, - { flext::MakeSymbol("/="),flext::MakeSymbol("__idiv__") }, - { flext::MakeSymbol("!/"),flext::MakeSymbol("__rdiv__") }, - { flext::MakeSymbol("//"),flext::MakeSymbol("__floordiv__") }, - { flext::MakeSymbol("//="),flext::MakeSymbol("__ifloordiv__") }, - { flext::MakeSymbol("!//"),flext::MakeSymbol("__rfloordiv__") }, - { flext::MakeSymbol("%"),flext::MakeSymbol("__mod__") }, - { flext::MakeSymbol("%="),flext::MakeSymbol("__imod__") }, - { flext::MakeSymbol("!%"),flext::MakeSymbol("__rmod__") }, - { flext::MakeSymbol("**"),flext::MakeSymbol("__pow__") }, - { flext::MakeSymbol("**="),flext::MakeSymbol("__ipow__") }, - { flext::MakeSymbol("!**"),flext::MakeSymbol("__rpow__") }, - { flext::MakeSymbol("&"),flext::MakeSymbol("__and__") }, - { flext::MakeSymbol("&="),flext::MakeSymbol("__iand__") }, - { flext::MakeSymbol("!&"),flext::MakeSymbol("__rand__") }, - { flext::MakeSymbol("|"),flext::MakeSymbol("__or__") }, - { flext::MakeSymbol("|="),flext::MakeSymbol("__ior__") }, - { flext::MakeSymbol("!|"),flext::MakeSymbol("__ror__") }, - { flext::MakeSymbol("^"),flext::MakeSymbol("__xor__") }, - { flext::MakeSymbol("^="),flext::MakeSymbol("__ixor__") }, - { flext::MakeSymbol("!^"),flext::MakeSymbol("__rxor__") }, - { flext::MakeSymbol("<<"),flext::MakeSymbol("__lshift__") }, - { flext::MakeSymbol("<<="),flext::MakeSymbol("__ilshift__") }, - { flext::MakeSymbol("!<<"),flext::MakeSymbol("__rlshift__") }, - { flext::MakeSymbol(">>"),flext::MakeSymbol("__rshift__") }, - { flext::MakeSymbol(">>="),flext::MakeSymbol("__irshift__") }, - { flext::MakeSymbol("!>>"),flext::MakeSymbol("__rrshift__") }, - { flext::MakeSymbol("=="),flext::MakeSymbol("__eq__") }, - { flext::MakeSymbol("!="),flext::MakeSymbol("__ne__") }, - { flext::MakeSymbol("<"),flext::MakeSymbol("__lt__") }, - { flext::MakeSymbol(">"),flext::MakeSymbol("__gt__") }, - { flext::MakeSymbol("<="),flext::MakeSymbol("__le__") }, - { flext::MakeSymbol(">="),flext::MakeSymbol("__ge__") }, - { flext::MakeSymbol("!"),flext::MakeSymbol("__nonzero__") }, - { flext::MakeSymbol("~"),flext::MakeSymbol("__invert__") }, - { flext::MakeSymbol("[]"),flext::MakeSymbol("__getitem__") }, - { flext::MakeSymbol("[]="),flext::MakeSymbol("__setitem__") }, - { flext::MakeSymbol("[:]"),flext::MakeSymbol("__getslice__") }, - { flext::MakeSymbol("[:]="),flext::MakeSymbol("__setslice__") }, - - { flext::MakeSymbol(".abs"),flext::MakeSymbol("__abs__") }, - { flext::MakeSymbol(".neg"),flext::MakeSymbol("__neg__") }, - { flext::MakeSymbol(".pos"),flext::MakeSymbol("__pos__") }, - { flext::MakeSymbol(".divmod"),flext::MakeSymbol("__divmod__") }, - - { flext::MakeSymbol(".int"),flext::MakeSymbol("__int__") }, - { flext::MakeSymbol(".long"),flext::MakeSymbol("__long__") }, - { flext::MakeSymbol(".float"),flext::MakeSymbol("__float__") }, - { flext::MakeSymbol(".complex"),flext::MakeSymbol("__complex__") }, - { flext::MakeSymbol(".str"),flext::MakeSymbol("__str__") }, - { flext::MakeSymbol(".coerce"),flext::MakeSymbol("__coerce__") }, - - { flext::MakeSymbol(".doc"),flext::MakeSymbol("__doc__") }, - { flext::MakeSymbol(".repr"),flext::MakeSymbol("__repr__") }, - - { flext::MakeSymbol(".len"),flext::MakeSymbol("__len__") }, - { flext::MakeSymbol(".in"),flext::MakeSymbol("__contains") }, - - { NULL,NULL } // sentinel -}; - -typedef std::map XTable; -static XTable xtable; - - -class pymeth - : public pybase - , public flext_base -{ - FLEXT_HEADER_S(pymeth,flext_base,Setup) - -public: - pymeth(int argc,const t_atom *argv); - ~pymeth(); - -protected: - virtual void Exit(); - - virtual bool CbMethodResort(int n,const t_symbol *s,int argc,const t_atom *argv); - - void m_help(); - - void m_reload() { Reload(); } - void m_reload_(int argc,const t_atom *argv) { args(argc,argv); Reload(); } - void m_set(int argc,const t_atom *argv); - void m_dir_() { m__dir(function); } - void m_doc_() { m__doc(function); } - - const t_symbol *funname; - PyObject *function; - - virtual void LoadModule(); - virtual void UnloadModule(); - - virtual void Load(); - virtual void Unload(); - - void SetFunction(const t_symbol *func); - void ResetFunction(); - - virtual void DumpOut(const t_symbol *sym,int argc,const t_atom *argv); - - PyObject **objects; - -private: - - virtual void callpy(PyObject *fun,PyObject *args); - - static void Setup(t_classid c); - - FLEXT_CALLBACK(m_help) - FLEXT_CALLBACK(m_reload) - FLEXT_CALLBACK_V(m_reload_) - FLEXT_CALLBACK_V(m_set) - FLEXT_CALLBACK(m_dir_) - FLEXT_CALLBACK(m_doc_) - - // callbacks - FLEXT_ATTRVAR_I(detach) - FLEXT_ATTRVAR_B(pymsg) - FLEXT_ATTRVAR_B(respond) - - FLEXT_CALLBACK_V(m_stop) - FLEXT_CALLBACK(m_dir) - FLEXT_CALLGET_V(mg_dir) - FLEXT_CALLBACK(m_doc) - -#ifdef FLEXT_THREADS - FLEXT_CALLBACK_T(tick) -#endif -}; - -FLEXT_LIB_V("pym",pymeth) - - -void pymeth::Setup(t_classid c) -{ - FLEXT_CADDMETHOD_(c,0,"doc",m_doc); - FLEXT_CADDMETHOD_(c,0,"dir",m_dir); -#ifdef FLEXT_THREADS - FLEXT_CADDATTR_VAR1(c,"detach",detach); - FLEXT_CADDMETHOD_(c,0,"stop",m_stop); -#endif - - FLEXT_CADDMETHOD_(c,0,"help",m_help); - FLEXT_CADDMETHOD_(c,0,"reload",m_reload_); - FLEXT_CADDMETHOD_(c,0,"reload.",m_reload); - FLEXT_CADDMETHOD_(c,0,"doc+",m_doc_); - FLEXT_CADDMETHOD_(c,0,"dir+",m_dir_); - - FLEXT_CADDMETHOD_(c,0,"set",m_set); - - FLEXT_CADDATTR_VAR1(c,"py",pymsg); - FLEXT_CADDATTR_VAR1(c,"respond",respond); - - // init translation map - for(const xlt *xi = xtdefs; xi->from; ++xi) xtable[xi->from] = xi->to; -} - -pymeth::pymeth(int argc,const t_atom *argv) - : funname(NULL) - , function(NULL) - , objects(NULL) -{ -#ifdef FLEXT_THREADS - FLEXT_ADDTIMER(stoptmr,tick); -#endif - - PyThreadState *state = PyLockSys(); - - int inlets; - if(argc && CanbeInt(*argv)) { - inlets = GetAInt(*argv); - if(inlets < 1) inlets = 1; - argv++,argc--; - } - else inlets = 1; - - objects = new PyObject *[inlets]; - for(int i = 0; i < inlets; ++i) { objects[i] = Py_None; Py_INCREF(Py_None); } - - if(inlets <= 0) InitProblem(); - - AddInAnything(1+(inlets < 0?1:inlets)); - AddOutAnything(); - - Register(GetRegistry(REGNAME)); - - if(argc) { - const t_symbol *funnm = GetASymbol(*argv); - argv++,argc--; - - if(funnm) - SetFunction(funnm); - else - PyErr_SetString(PyExc_ValueError,"Invalid function name"); - } - - if(argc) args(argc,argv); - - Report(); - - PyUnlock(state); -} - -pymeth::~pymeth() -{ - if(objects) { - for(int i = 0; i < CntIn()-1; ++i) Py_DECREF(objects[i]); - delete[] objects; - } - - PyThreadState *state = PyLockSys(); - Unregister(GetRegistry(REGNAME)); - Report(); - PyUnlock(state); -} - -void pymeth::Exit() -{ - pybase::Exit(); - flext_base::Exit(); -} - -void pymeth::m_set(int argc,const t_atom *argv) -{ - PyThreadState *state = PyLockSys(); - - // function name has precedence - if(argc >= 2) { - const char *sn = GetAString(*argv); - ++argv,--argc; - - if(sn) { - if(!module || !strcmp(sn,PyModule_GetName(module))) { - ImportModule(sn); - Register(GetRegistry(REGNAME)); - } - } - else - PyErr_SetString(PyExc_ValueError,"Invalid module name"); - } - - if(argc) { - const t_symbol *fn = GetASymbol(*argv); - if(fn) - SetFunction(fn); - else - PyErr_SetString(PyExc_ValueError,"Invalid function name"); - } - - Report(); - - PyUnlock(state); -} - -void pymeth::m_help() -{ - post(""); - post("%s %s - python method object, (C)2002-2005 Thomas Grill",thisName(),PY__VERSION); -#ifdef FLEXT_DEBUG - post("DEBUG VERSION, compiled on " __DATE__ " " __TIME__); -#endif - - post("Arguments: %s [method name] {args...}",thisName()); - - post("Inlet 1:messages to control the py object"); - post(" 2:call python function with message as argument(s)"); - post("Outlet: 1:return values from python function"); - post("Methods:"); - post("\thelp: shows this help"); - post("\tbang: call script without arguments"); - post("\tset [script name] [function name]: set (script and) function name"); - post("\treload {args...}: reload python script"); - post("\treload. : reload with former arguments"); - post("\tdoc: display module doc string"); - post("\tdoc+: display function doc string"); - post("\tdir: dump module dictionary"); - post("\tdir+: dump function dictionary"); -#ifdef FLEXT_THREADS - post("\tdetach 0/1/2: detach threads"); - post("\tstop {wait time (ms)}: stop threads"); -#endif - post(""); -} - -void pymeth::ResetFunction() -{ - Py_XDECREF(function); - function = NULL; - - if(funname && objects[0] != Py_None) { - function = PyObject_GetAttrString(objects[0],(char *)GetString(funname)); // new reference - if(!function) - PyErr_SetString(PyExc_AttributeError,"Method not found"); - } - - // exception could be set here -} - -void pymeth::SetFunction(const t_symbol *func) -{ - // look for method name in translation table - XTable::iterator it = xtable.find(func); - funname = it == xtable.end()?func:it->second; - - ResetFunction(); -} - - -void pymeth::LoadModule() -{ - SetFunction(funname); -} - -void pymeth::UnloadModule() -{ -} - -void pymeth::Load() -{ - ResetFunction(); -} - -void pymeth::Unload() -{ - SetFunction(NULL); -} - -void pymeth::callpy(PyObject *fun,PyObject *args) -{ - PyObject *ret = PyObject_CallObject(fun,args); - if(ret) { - OutObject(this,0,ret); // exception might be raised here - Py_DECREF(ret); - } -} - -bool pymeth::CbMethodResort(int n,const t_symbol *s,int argc,const t_atom *argv) -{ - if(n == 0 && s != sym_bang) - return flext_base::CbMethodResort(n,s,argc,argv); - - PyThreadState *state = PyLockSys(); - - bool ret = false; - - if(n >= 1) { - // store args - PyObject *&obj = objects[n-1]; - Py_DECREF(obj); - obj = MakePyArg(s,argc,argv); // steal reference - - if(n > 1) ret = true; // just store, don't trigger - } - - if(!ret) { - if(function) { - PyObject *self = PyMethod_Self(function); - PyErr_Clear(); - if(!self || self->ob_type != objects[0]->ob_type) - // type has changed, search for new method - ResetFunction(); - else if(self != objects[0]) { - // type hasn't changed, but object has - PyObject *f = function; - function = PyMethod_New(PyMethod_GET_FUNCTION(f),objects[0],PyMethod_GET_CLASS(f)); - Py_DECREF(f); - } - } - else - ResetFunction(); - - if(function) { - Py_INCREF(function); - - int inlets = CntIn()-1; - PyObject *pargs = PyTuple_New(inlets-1); - for(int i = 1; i < inlets; ++i) { - Py_INCREF(objects[i]); - PyTuple_SET_ITEM(pargs,i-1,objects[i]); - } - - gencall(function,pargs); // references are stolen - ret = true; - } - else - PyErr_SetString(PyExc_RuntimeError,"No function set"); - - Report(); - } - - PyUnlock(state); - - Respond(ret); - - return ret; -} - -void pymeth::DumpOut(const t_symbol *sym,int argc,const t_atom *argv) -{ - ToOutAnything(GetOutAttr(),sym?sym:thisTag(),argc,argv); -} +/* + +py/pyext - python script object for PD and Max/MSP + +Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) +For information on usage and redistribution, and for a DISCLAIMER OF ALL +WARRANTIES, see the file, "license.txt," in this distribution. + +*/ + +#include "pybase.h" +#include + +struct xlt { const t_symbol *from,*to; }; + +static const xlt xtdefs[] = { + { flext::MakeSymbol("+"),flext::MakeSymbol("__add__") }, + { flext::MakeSymbol("+="),flext::MakeSymbol("__iadd__") }, + { flext::MakeSymbol("!+"),flext::MakeSymbol("__radd__") }, + { flext::MakeSymbol("-"),flext::MakeSymbol("__sub__") }, + { flext::MakeSymbol("-="),flext::MakeSymbol("__isub__") }, + { flext::MakeSymbol("!-"),flext::MakeSymbol("__rsub__") }, + { flext::MakeSymbol("*"),flext::MakeSymbol("__mul__") }, + { flext::MakeSymbol("*="),flext::MakeSymbol("__imul__") }, + { flext::MakeSymbol("!*"),flext::MakeSymbol("__rmul__") }, + { flext::MakeSymbol("/"),flext::MakeSymbol("__div__") }, + { flext::MakeSymbol("/="),flext::MakeSymbol("__idiv__") }, + { flext::MakeSymbol("!/"),flext::MakeSymbol("__rdiv__") }, + { flext::MakeSymbol("//"),flext::MakeSymbol("__floordiv__") }, + { flext::MakeSymbol("//="),flext::MakeSymbol("__ifloordiv__") }, + { flext::MakeSymbol("!//"),flext::MakeSymbol("__rfloordiv__") }, + { flext::MakeSymbol("%"),flext::MakeSymbol("__mod__") }, + { flext::MakeSymbol("%="),flext::MakeSymbol("__imod__") }, + { flext::MakeSymbol("!%"),flext::MakeSymbol("__rmod__") }, + { flext::MakeSymbol("**"),flext::MakeSymbol("__pow__") }, + { flext::MakeSymbol("**="),flext::MakeSymbol("__ipow__") }, + { flext::MakeSymbol("!**"),flext::MakeSymbol("__rpow__") }, + { flext::MakeSymbol("&"),flext::MakeSymbol("__and__") }, + { flext::MakeSymbol("&="),flext::MakeSymbol("__iand__") }, + { flext::MakeSymbol("!&"),flext::MakeSymbol("__rand__") }, + { flext::MakeSymbol("|"),flext::MakeSymbol("__or__") }, + { flext::MakeSymbol("|="),flext::MakeSymbol("__ior__") }, + { flext::MakeSymbol("!|"),flext::MakeSymbol("__ror__") }, + { flext::MakeSymbol("^"),flext::MakeSymbol("__xor__") }, + { flext::MakeSymbol("^="),flext::MakeSymbol("__ixor__") }, + { flext::MakeSymbol("!^"),flext::MakeSymbol("__rxor__") }, + { flext::MakeSymbol("<<"),flext::MakeSymbol("__lshift__") }, + { flext::MakeSymbol("<<="),flext::MakeSymbol("__ilshift__") }, + { flext::MakeSymbol("!<<"),flext::MakeSymbol("__rlshift__") }, + { flext::MakeSymbol(">>"),flext::MakeSymbol("__rshift__") }, + { flext::MakeSymbol(">>="),flext::MakeSymbol("__irshift__") }, + { flext::MakeSymbol("!>>"),flext::MakeSymbol("__rrshift__") }, + { flext::MakeSymbol("=="),flext::MakeSymbol("__eq__") }, + { flext::MakeSymbol("!="),flext::MakeSymbol("__ne__") }, + { flext::MakeSymbol("<"),flext::MakeSymbol("__lt__") }, + { flext::MakeSymbol(">"),flext::MakeSymbol("__gt__") }, + { flext::MakeSymbol("<="),flext::MakeSymbol("__le__") }, + { flext::MakeSymbol(">="),flext::MakeSymbol("__ge__") }, + { flext::MakeSymbol("!"),flext::MakeSymbol("__nonzero__") }, + { flext::MakeSymbol("~"),flext::MakeSymbol("__invert__") }, + { flext::MakeSymbol("[]"),flext::MakeSymbol("__getitem__") }, + { flext::MakeSymbol("[]="),flext::MakeSymbol("__setitem__") }, + { flext::MakeSymbol("[:]"),flext::MakeSymbol("__getslice__") }, + { flext::MakeSymbol("[:]="),flext::MakeSymbol("__setslice__") }, + + { flext::MakeSymbol(".abs"),flext::MakeSymbol("__abs__") }, + { flext::MakeSymbol(".neg"),flext::MakeSymbol("__neg__") }, + { flext::MakeSymbol(".pos"),flext::MakeSymbol("__pos__") }, + { flext::MakeSymbol(".divmod"),flext::MakeSymbol("__divmod__") }, + + { flext::MakeSymbol(".int"),flext::MakeSymbol("__int__") }, + { flext::MakeSymbol(".long"),flext::MakeSymbol("__long__") }, + { flext::MakeSymbol(".float"),flext::MakeSymbol("__float__") }, + { flext::MakeSymbol(".complex"),flext::MakeSymbol("__complex__") }, + { flext::MakeSymbol(".str"),flext::MakeSymbol("__str__") }, + { flext::MakeSymbol(".coerce"),flext::MakeSymbol("__coerce__") }, + + { flext::MakeSymbol(".doc"),flext::MakeSymbol("__doc__") }, + { flext::MakeSymbol(".repr"),flext::MakeSymbol("__repr__") }, + + { flext::MakeSymbol(".len"),flext::MakeSymbol("__len__") }, + { flext::MakeSymbol(".in"),flext::MakeSymbol("__contains") }, + + { NULL,NULL } // sentinel +}; + +typedef std::map XTable; +static XTable xtable; + + +class pymeth + : public pybase + , public flext_base +{ + FLEXT_HEADER_S(pymeth,flext_base,Setup) + +public: + pymeth(int argc,const t_atom *argv); + ~pymeth(); + +protected: + virtual void Exit(); + + virtual bool CbMethodResort(int n,const t_symbol *s,int argc,const t_atom *argv); + + void m_help(); + + void m_reload() { Reload(); } + void m_reload_(int argc,const t_atom *argv) { args(argc,argv); Reload(); } + void m_set(int argc,const t_atom *argv); + void m_dir_() { m__dir(function); } + void m_doc_() { m__doc(function); } + + const t_symbol *funname; + PyObject *function; + + virtual void LoadModule(); + virtual void UnloadModule(); + + virtual void Load(); + virtual void Unload(); + + void SetFunction(const t_symbol *func); + void ResetFunction(); + + virtual void DumpOut(const t_symbol *sym,int argc,const t_atom *argv); + + PyObject **objects; + +private: + + virtual void callpy(PyObject *fun,PyObject *args); + + static void Setup(t_classid c); + + FLEXT_CALLBACK(m_help) + FLEXT_CALLBACK(m_reload) + FLEXT_CALLBACK_V(m_reload_) + FLEXT_CALLBACK_V(m_set) + FLEXT_CALLBACK(m_dir_) + FLEXT_CALLBACK(m_doc_) + + // callbacks + FLEXT_ATTRVAR_I(detach) + FLEXT_ATTRVAR_B(pymsg) + FLEXT_ATTRVAR_B(respond) + + FLEXT_CALLBACK_V(m_stop) + FLEXT_CALLBACK(m_dir) + FLEXT_CALLGET_V(mg_dir) + FLEXT_CALLBACK(m_doc) + +#ifdef FLEXT_THREADS + FLEXT_CALLBACK_T(tick) +#endif +}; + +FLEXT_LIB_V("pym",pymeth) + + +void pymeth::Setup(t_classid c) +{ + FLEXT_CADDMETHOD_(c,0,"doc",m_doc); + FLEXT_CADDMETHOD_(c,0,"dir",m_dir); +#ifdef FLEXT_THREADS + FLEXT_CADDATTR_VAR1(c,"detach",detach); + FLEXT_CADDMETHOD_(c,0,"stop",m_stop); +#endif + + FLEXT_CADDMETHOD_(c,0,"help",m_help); + FLEXT_CADDMETHOD_(c,0,"reload",m_reload_); + FLEXT_CADDMETHOD_(c,0,"reload.",m_reload); + FLEXT_CADDMETHOD_(c,0,"doc+",m_doc_); + FLEXT_CADDMETHOD_(c,0,"dir+",m_dir_); + + FLEXT_CADDMETHOD_(c,0,"set",m_set); + + FLEXT_CADDATTR_VAR1(c,"py",pymsg); + FLEXT_CADDATTR_VAR1(c,"respond",respond); + + // init translation map + for(const xlt *xi = xtdefs; xi->from; ++xi) xtable[xi->from] = xi->to; +} + +pymeth::pymeth(int argc,const t_atom *argv) + : funname(NULL) + , function(NULL) + , objects(NULL) +{ +#ifdef FLEXT_THREADS + FLEXT_ADDTIMER(stoptmr,tick); +#endif + + ThrState state = PyLockSys(); + + int inlets; + if(argc && CanbeInt(*argv)) { + inlets = GetAInt(*argv); + if(inlets < 1) inlets = 1; + argv++,argc--; + } + else inlets = 1; + + objects = new PyObject *[inlets]; + for(int i = 0; i < inlets; ++i) { objects[i] = Py_None; Py_INCREF(Py_None); } + + if(inlets <= 0) InitProblem(); + + AddInAnything(1+(inlets < 0?1:inlets)); + AddOutAnything(); + + Register(GetRegistry(REGNAME)); + + if(argc) { + const t_symbol *funnm = GetASymbol(*argv); + argv++,argc--; + + if(funnm) + SetFunction(funnm); + else + PyErr_SetString(PyExc_ValueError,"Invalid function name"); + } + + if(argc) args(argc,argv); + + Report(); + + PyUnlock(state); +} + +pymeth::~pymeth() +{ + if(objects) { + for(int i = 0; i < CntIn()-1; ++i) Py_DECREF(objects[i]); + delete[] objects; + } + + ThrState state = PyLockSys(); + Unregister(GetRegistry(REGNAME)); + Report(); + PyUnlock(state); +} + +void pymeth::Exit() +{ + pybase::Exit(); + flext_base::Exit(); +} + +void pymeth::m_set(int argc,const t_atom *argv) +{ + ThrState state = PyLockSys(); + + // function name has precedence + if(argc >= 2) { + const char *sn = GetAString(*argv); + ++argv,--argc; + + if(sn) { + if(!module || !strcmp(sn,PyModule_GetName(module))) { + ImportModule(sn); + Register(GetRegistry(REGNAME)); + } + } + else + PyErr_SetString(PyExc_ValueError,"Invalid module name"); + } + + if(argc) { + const t_symbol *fn = GetASymbol(*argv); + if(fn) + SetFunction(fn); + else + PyErr_SetString(PyExc_ValueError,"Invalid function name"); + } + + Report(); + + PyUnlock(state); +} + +void pymeth::m_help() +{ + post(""); + post("%s %s - python method object, (C)2002-2005 Thomas Grill",thisName(),PY__VERSION); +#ifdef FLEXT_DEBUG + post("DEBUG VERSION, compiled on " __DATE__ " " __TIME__); +#endif + + post("Arguments: %s [method name] {args...}",thisName()); + + post("Inlet 1:messages to control the py object"); + post(" 2:call python function with message as argument(s)"); + post("Outlet: 1:return values from python function"); + post("Methods:"); + post("\thelp: shows this help"); + post("\tbang: call script without arguments"); + post("\tset [script name] [function name]: set (script and) function name"); + post("\treload {args...}: reload python script"); + post("\treload. : reload with former arguments"); + post("\tdoc: display module doc string"); + post("\tdoc+: display function doc string"); + post("\tdir: dump module dictionary"); + post("\tdir+: dump function dictionary"); +#ifdef FLEXT_THREADS + post("\tdetach 0/1/2: detach threads"); + post("\tstop {wait time (ms)}: stop threads"); +#endif + post(""); +} + +void pymeth::ResetFunction() +{ + Py_XDECREF(function); + function = NULL; + + if(funname && objects[0] != Py_None) { + function = PyObject_GetAttrString(objects[0],(char *)GetString(funname)); // new reference + if(!function) + PyErr_SetString(PyExc_AttributeError,"Method not found"); + } + + // exception could be set here +} + +void pymeth::SetFunction(const t_symbol *func) +{ + // look for method name in translation table + XTable::iterator it = xtable.find(func); + funname = it == xtable.end()?func:it->second; + + ResetFunction(); +} + + +void pymeth::LoadModule() +{ + SetFunction(funname); +} + +void pymeth::UnloadModule() +{ +} + +void pymeth::Load() +{ + ResetFunction(); +} + +void pymeth::Unload() +{ + SetFunction(NULL); +} + +void pymeth::callpy(PyObject *fun,PyObject *args) +{ + PyObject *ret = PyObject_CallObject(fun,args); + if(ret) { + OutObject(this,0,ret); // exception might be raised here + Py_DECREF(ret); + } +} + +bool pymeth::CbMethodResort(int n,const t_symbol *s,int argc,const t_atom *argv) +{ + if(n == 0 && s != sym_bang) + return flext_base::CbMethodResort(n,s,argc,argv); + + ThrState state = PyLockSys(); + + bool ret = false; + + if(n >= 1) { + // store args + PyObject *&obj = objects[n-1]; + Py_DECREF(obj); + obj = MakePyArg(s,argc,argv); // steal reference + + if(n > 1) ret = true; // just store, don't trigger + } + + if(!ret) { + if(function) { + PyObject *self = PyMethod_Self(function); + PyErr_Clear(); + if(!self || self->ob_type != objects[0]->ob_type) + // type has changed, search for new method + ResetFunction(); + else if(self != objects[0]) { + // type hasn't changed, but object has + PyObject *f = function; + function = PyMethod_New(PyMethod_GET_FUNCTION(f),objects[0],PyMethod_GET_CLASS(f)); + Py_DECREF(f); + } + } + else + ResetFunction(); + + if(function) { + Py_INCREF(function); + + int inlets = CntIn()-1; + PyObject *pargs = PyTuple_New(inlets-1); + for(int i = 1; i < inlets; ++i) { + Py_INCREF(objects[i]); + PyTuple_SET_ITEM(pargs,i-1,objects[i]); + } + + gencall(function,pargs); // references are stolen + ret = true; + } + else + PyErr_SetString(PyExc_RuntimeError,"No function set"); + + Report(); + } + + PyUnlock(state); + + Respond(ret); + + return ret; +} + +void pymeth::DumpOut(const t_symbol *sym,int argc,const t_atom *argv) +{ + ToOutAnything(GetOutAttr(),sym?sym:thisTag(),argc,argv); +} diff --git a/externals/grill/py/source/pyprefix.h b/externals/grill/py/source/pyprefix.h index de74ca42..9be12e85 100644 --- a/externals/grill/py/source/pyprefix.h +++ b/externals/grill/py/source/pyprefix.h @@ -1,43 +1,43 @@ -/* - -py/pyext - python script object for PD and MaxMSP - -Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) -For information on usage and redistribution, and for a DISCLAIMER OF ALL -WARRANTIES, see the file, "license.txt," in this distribution. - -*/ - -#ifndef __PYPREFIX_H -#define __PYPREFIX_H - -#define FLEXT_ATTRIBUTES 1 -#include - -// hack: must include math.h before Python.h (at least on OSX) -// otherwise some functions don't get defined -#include - -#if FLEXT_OS == FLEXT_OS_MAC -#include -#else -#include -#endif - -#if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 501) -#error You need at least flext version 0.5.1 -#endif - -#if FLEXT_OS == FLEXT_LINUX || FLEXT_OS == FLEXT_IRIX -#include -#endif - -#if FLEXT_SYS == FLEXT_SYS_PD && (!defined (PD_MINOR_VERSION) || PD_MINOR_VERSION < 37) -#error PD version >= 0.37 required, please upgrade! -#endif - -#include -#include - - -#endif +/* + +py/pyext - python script object for PD and MaxMSP + +Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) +For information on usage and redistribution, and for a DISCLAIMER OF ALL +WARRANTIES, see the file, "license.txt," in this distribution. + +*/ + +#ifndef __PYPREFIX_H +#define __PYPREFIX_H + +#define FLEXT_ATTRIBUTES 1 +#include + +// hack: must include math.h before Python.h (at least on OSX) +// otherwise some functions don't get defined +#include + +#if FLEXT_OS == FLEXT_OS_MAC +#include +#else +#include +#endif + +#if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 501) +#error You need at least flext version 0.5.1 +#endif + +#if FLEXT_OS == FLEXT_LINUX || FLEXT_OS == FLEXT_IRIX +#include +#endif + +#if FLEXT_SYS == FLEXT_SYS_PD && (!defined (PD_MINOR_VERSION) || PD_MINOR_VERSION < 37) +#error PD version >= 0.37 required, please upgrade! +#endif + +#include +#include + + +#endif diff --git a/externals/grill/py/source/pysymbol.cpp b/externals/grill/py/source/pysymbol.cpp index 2f023469..f907cc45 100644 --- a/externals/grill/py/source/pysymbol.cpp +++ b/externals/grill/py/source/pysymbol.cpp @@ -1,254 +1,254 @@ -/* - -py/pyext - python script object for PD and Max/MSP - -Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) -For information on usage and redistribution, and for a DISCLAIMER OF ALL -WARRANTIES, see the file, "license.txt," in this distribution. - -*/ - -#include "pyprefix.h" -#include "pysymbol.h" - -inline pySymbol *symbol_newsym(const t_symbol *sym) -{ - pySymbol *self = (pySymbol *)pySymbol_Type.tp_alloc(&pySymbol_Type, 0); - if(self) self->sym = sym; - return self; -} - -static PyObject *symbol_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - return (PyObject *)symbol_newsym(flext::sym__); -} - -static int symbol_init(PyObject *self, PyObject *args, PyObject *kwds) -{ - FLEXT_ASSERT(pySymbol_Check(self)); - - PyObject *arg = PySequence_GetItem(args,0); // new reference - if(!arg) return -1; - - int ret = 0; - - if(pySymbol_Check(arg)) - ((pySymbol *)self)->sym = pySymbol_AS_SYMBOL(arg); - else if(PyString_Check(arg)) - ((pySymbol *)self)->sym = flext::MakeSymbol(PyString_AS_STRING(arg)); - else { - PyErr_SetString(PyExc_TypeError,"string or symbol argument expected"); - ret = -1; - } - Py_DECREF(arg); - - return ret; -} - -static PyObject *symbol_str(PyObject *self) -{ - FLEXT_ASSERT(pySymbol_Check(self)); - return (PyObject *)PyString_FromString(pySymbol_AS_STRING(self)); -} - -static PyObject *symbol_repr(PyObject *self) -{ - FLEXT_ASSERT(pySymbol_Check(self)); - return (PyObject *)PyString_FromFormat("",pySymbol_AS_STRING(self)); -} - -static PyObject *symbol_richcompare(PyObject *a,PyObject *b,int cmp) -{ - if(pySymbol_Check(a) && pySymbol_Check(b)) { - const t_symbol *asym = pySymbol_AS_SYMBOL(a); - const t_symbol *bsym = pySymbol_AS_SYMBOL(b); - bool ret; - switch(cmp) { - case Py_LT: ret = asym < bsym; break; - case Py_LE: ret = asym <= bsym; break; - case Py_EQ: ret = asym == bsym; break; - case Py_NE: ret = asym != bsym; break; - case Py_GT: ret = asym > bsym; break; - case Py_GE: ret = asym >= bsym; break; - } - return PyBool_FromLong(ret); - } - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; -} - -static long symbol_hash(PyObject *self) -{ - FLEXT_ASSERT(pySymbol_Check(self)); - return (long)pySymbol_AS_SYMBOL(self); -} - - -static int symbol_length(pySymbol *self) -{ - return strlen(flext::GetString(self->sym)); -} - -static PyObject *symbol_item(pySymbol *self, int i) -{ - const char *str = flext::GetString(self->sym); - int len = strlen(str); - if(i < 0) i += len; - - if(i >= 0 && i < len) - return PyString_FromStringAndSize(str+i,1); - else { - Py_INCREF(Py_None); - return Py_None; - } -} - -static PyObject *symbol_slice(pySymbol *self,int ilow = 0,int ihigh = 1<<(sizeof(int)*8-2)) -{ - const char *str = flext::GetString(self->sym); - int len = strlen(str); - if(ilow < 0) { - ilow += len; - if(ilow < 0) ilow = 0; - } - if(ihigh < 0) ihigh += len; - if(ihigh >= len) ihigh = len-1; - - return PyString_FromStringAndSize(str+ilow,ilow <= ihigh?ihigh-ilow+1:0); -} - -static PyObject *symbol_concat(pySymbol *self,PyObject *op) -{ - PyObject *nobj = symbol_slice(self); // take all - if(nobj) { - PyObject *ret = PySequence_Concat(nobj,op); - Py_DECREF(nobj); - return ret; - } - else - return NULL; -} - -static PyObject *symbol_repeat(pySymbol *self,int rep) -{ - PyObject *nobj = symbol_slice(self); // take all - if(nobj) { - PyObject *ret = PySequence_Repeat(nobj,rep); - Py_DECREF(nobj); - return ret; - } - else - return NULL; -} - -static PySequenceMethods symbol_as_seq = { - (inquiry)symbol_length, /* inquiry sq_length; __len__ */ - (binaryfunc)symbol_concat, /* __add__ */ - (intargfunc)symbol_repeat, /* __mul__ */ - (intargfunc)symbol_item, /* intargfunc sq_item; __getitem__ */ - (intintargfunc)symbol_slice, /* intintargfunc sq_slice; __getslice__ */ - NULL, /* intobjargproc sq_ass_item; __setitem__ */ - NULL, /* intintobjargproc sq_ass_slice; __setslice__ */ -}; - -static PyObject *symbol_iter(PyObject *obj) -{ - pySymbol *self = (pySymbol *)obj; - PyObject *nobj = symbol_slice(self); - if(nobj) { - PyObject *it = PyObject_GetIter(nobj); - Py_DECREF(nobj); - return it; - } - else - return NULL; -} - - - -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 */ - symbol_richcompare, /* tp_richcompare */ - 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 */ - 0, /* tp_alloc */ - symbol_new, /* tp_new */ -}; - -pySymbol *pySymbol__; -pySymbol *pySymbol_bang; -pySymbol *pySymbol_list; -pySymbol *pySymbol_symbol; -pySymbol *pySymbol_float; -pySymbol *pySymbol_int; - - -void initsymbol() -{ - if(PyType_Ready(&pySymbol_Type) < 0) - return; - - Py_INCREF(&pySymbol_Type); - - // initialize predefined objects - pySymbol__ = symbol_newsym(flext::sym__); - pySymbol_bang = symbol_newsym(flext::sym_bang); - pySymbol_list = symbol_newsym(flext::sym_list); - pySymbol_symbol = symbol_newsym(flext::sym_symbol); - pySymbol_float = symbol_newsym(flext::sym_float); - pySymbol_int = symbol_newsym(flext::sym_int); -} - - -PyObject *pySymbol_FromSymbol(const t_symbol *sym) -{ - pySymbol *op; - if(sym == flext::sym__) - Py_INCREF(op = pySymbol__); - else if(sym == flext::sym_bang) - Py_INCREF(op = pySymbol_bang); - else if(sym == flext::sym_list) - Py_INCREF(op = pySymbol_list); - else if(sym == flext::sym_symbol) - Py_INCREF(op = pySymbol_symbol); - else if(sym == flext::sym_float) - Py_INCREF(op = pySymbol_float); - else if(sym == flext::sym_int) - Py_INCREF(op = pySymbol_int); - else - op = symbol_newsym(sym); - return (PyObject *)op; -} +/* + +py/pyext - python script object for PD and Max/MSP + +Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) +For information on usage and redistribution, and for a DISCLAIMER OF ALL +WARRANTIES, see the file, "license.txt," in this distribution. + +*/ + +#include "pyprefix.h" +#include "pysymbol.h" + +inline pySymbol *symbol_newsym(const t_symbol *sym) +{ + pySymbol *self = (pySymbol *)pySymbol_Type.tp_alloc(&pySymbol_Type, 0); + if(self) self->sym = sym; + return self; +} + +static PyObject *symbol_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + return (PyObject *)symbol_newsym(flext::sym__); +} + +static int symbol_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + FLEXT_ASSERT(pySymbol_Check(self)); + + PyObject *arg = PySequence_GetItem(args,0); // new reference + if(!arg) return -1; + + int ret = 0; + + if(pySymbol_Check(arg)) + ((pySymbol *)self)->sym = pySymbol_AS_SYMBOL(arg); + else if(PyString_Check(arg)) + ((pySymbol *)self)->sym = flext::MakeSymbol(PyString_AS_STRING(arg)); + else { + PyErr_SetString(PyExc_TypeError,"string or symbol argument expected"); + ret = -1; + } + Py_DECREF(arg); + + return ret; +} + +static PyObject *symbol_str(PyObject *self) +{ + FLEXT_ASSERT(pySymbol_Check(self)); + return (PyObject *)PyString_FromString(pySymbol_AS_STRING(self)); +} + +static PyObject *symbol_repr(PyObject *self) +{ + FLEXT_ASSERT(pySymbol_Check(self)); + return (PyObject *)PyString_FromFormat("",pySymbol_AS_STRING(self)); +} + +static PyObject *symbol_richcompare(PyObject *a,PyObject *b,int cmp) +{ + if(pySymbol_Check(a) && pySymbol_Check(b)) { + const t_symbol *asym = pySymbol_AS_SYMBOL(a); + const t_symbol *bsym = pySymbol_AS_SYMBOL(b); + bool ret; + switch(cmp) { + case Py_LT: ret = asym < bsym; break; + case Py_LE: ret = asym <= bsym; break; + case Py_EQ: ret = asym == bsym; break; + case Py_NE: ret = asym != bsym; break; + case Py_GT: ret = asym > bsym; break; + case Py_GE: ret = asym >= bsym; break; + } + return PyBool_FromLong(ret); + } + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; +} + +static long symbol_hash(PyObject *self) +{ + FLEXT_ASSERT(pySymbol_Check(self)); + return (long)pySymbol_AS_SYMBOL(self); +} + + +static int symbol_length(pySymbol *self) +{ + return strlen(flext::GetString(self->sym)); +} + +static PyObject *symbol_item(pySymbol *self, int i) +{ + const char *str = flext::GetString(self->sym); + int len = strlen(str); + if(i < 0) i += len; + + if(i >= 0 && i < len) + return PyString_FromStringAndSize(str+i,1); + else { + Py_INCREF(Py_None); + return Py_None; + } +} + +static PyObject *symbol_slice(pySymbol *self,int ilow = 0,int ihigh = 1<<(sizeof(int)*8-2)) +{ + const char *str = flext::GetString(self->sym); + int len = strlen(str); + if(ilow < 0) { + ilow += len; + if(ilow < 0) ilow = 0; + } + if(ihigh < 0) ihigh += len; + if(ihigh >= len) ihigh = len-1; + + return PyString_FromStringAndSize(str+ilow,ilow <= ihigh?ihigh-ilow+1:0); +} + +static PyObject *symbol_concat(pySymbol *self,PyObject *op) +{ + PyObject *nobj = symbol_slice(self); // take all + if(nobj) { + PyObject *ret = PySequence_Concat(nobj,op); + Py_DECREF(nobj); + return ret; + } + else + return NULL; +} + +static PyObject *symbol_repeat(pySymbol *self,int rep) +{ + PyObject *nobj = symbol_slice(self); // take all + if(nobj) { + PyObject *ret = PySequence_Repeat(nobj,rep); + Py_DECREF(nobj); + return ret; + } + else + return NULL; +} + +static PySequenceMethods symbol_as_seq = { + (inquiry)symbol_length, /* inquiry sq_length; __len__ */ + (binaryfunc)symbol_concat, /* __add__ */ + (intargfunc)symbol_repeat, /* __mul__ */ + (intargfunc)symbol_item, /* intargfunc sq_item; __getitem__ */ + (intintargfunc)symbol_slice, /* intintargfunc sq_slice; __getslice__ */ + NULL, /* intobjargproc sq_ass_item; __setitem__ */ + NULL, /* intintobjargproc sq_ass_slice; __setslice__ */ +}; + +static PyObject *symbol_iter(PyObject *obj) +{ + pySymbol *self = (pySymbol *)obj; + PyObject *nobj = symbol_slice(self); + if(nobj) { + PyObject *it = PyObject_GetIter(nobj); + Py_DECREF(nobj); + return it; + } + else + return NULL; +} + + + +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 */ + symbol_richcompare, /* tp_richcompare */ + 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 */ + 0, /* tp_alloc */ + symbol_new, /* tp_new */ +}; + +pySymbol *pySymbol__; +pySymbol *pySymbol_bang; +pySymbol *pySymbol_list; +pySymbol *pySymbol_symbol; +pySymbol *pySymbol_float; +pySymbol *pySymbol_int; + + +void initsymbol() +{ + if(PyType_Ready(&pySymbol_Type) < 0) + return; + + Py_INCREF(&pySymbol_Type); + + // initialize predefined objects + pySymbol__ = symbol_newsym(flext::sym__); + pySymbol_bang = symbol_newsym(flext::sym_bang); + pySymbol_list = symbol_newsym(flext::sym_list); + pySymbol_symbol = symbol_newsym(flext::sym_symbol); + pySymbol_float = symbol_newsym(flext::sym_float); + pySymbol_int = symbol_newsym(flext::sym_int); +} + + +PyObject *pySymbol_FromSymbol(const t_symbol *sym) +{ + pySymbol *op; + if(sym == flext::sym__) + Py_INCREF(op = pySymbol__); + else if(sym == flext::sym_bang) + Py_INCREF(op = pySymbol_bang); + else if(sym == flext::sym_list) + Py_INCREF(op = pySymbol_list); + else if(sym == flext::sym_symbol) + Py_INCREF(op = pySymbol_symbol); + else if(sym == flext::sym_float) + Py_INCREF(op = pySymbol_float); + else if(sym == flext::sym_int) + Py_INCREF(op = pySymbol_int); + else + op = symbol_newsym(sym); + return (PyObject *)op; +} diff --git a/externals/grill/py/source/pysymbol.h b/externals/grill/py/source/pysymbol.h index 42f6765c..3de4cb00 100644 --- a/externals/grill/py/source/pysymbol.h +++ b/externals/grill/py/source/pysymbol.h @@ -1,55 +1,55 @@ -/* - -py/pyext - python script object for PD and Max/MSP - -Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) -For information on usage and redistribution, and for a DISCLAIMER OF ALL -WARRANTIES, see the file, "license.txt," in this distribution. - -*/ - -#ifndef __PYSYMBOL_H -#define __PYSYMBOL_H - -#include - -#if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 500) -#error You need at least flext version 0.5.0 -#endif - -#if FLEXT_OS == FLEXT_OS_MAC -#include -#else -#include -#endif - - -#ifdef _MSC_VER - #ifdef PY_EXPORTS - #define PY_EXPORT __declspec(dllexport) - #else - #define PY_EXPORT __declspec(dllimport) - #endif -#else - #define PY_EXPORT -#endif - -typedef struct { - PyObject_HEAD - /* Type-specific fields go here. */ - const t_symbol *sym; -} pySymbol; - -PY_EXPORT extern PyTypeObject pySymbol_Type; - -PY_EXPORT extern pySymbol *pySymbol__; -PY_EXPORT extern pySymbol *pySymbol_bang; -PY_EXPORT extern pySymbol *pySymbol_list; -PY_EXPORT extern pySymbol *pySymbol_symbol; -PY_EXPORT extern pySymbol *pySymbol_float; -PY_EXPORT extern pySymbol *pySymbol_int; - - +/* + +py/pyext - python script object for PD and Max/MSP + +Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org) +For information on usage and redistribution, and for a DISCLAIMER OF ALL +WARRANTIES, see the file, "license.txt," in this distribution. + +*/ + +#ifndef __PYSYMBOL_H +#define __PYSYMBOL_H + +#include + +#if !defined(FLEXT_VERSION) || (FLEXT_VERSION < 500) +#error You need at least flext version 0.5.0 +#endif + +#if FLEXT_OS == FLEXT_OS_MAC +#include +#else +#include +#endif + + +#ifdef _MSC_VER + #ifdef PY_EXPORTS + #define PY_EXPORT __declspec(dllexport) + #else + #define PY_EXPORT __declspec(dllimport) + #endif +#else + #define PY_EXPORT +#endif + +typedef struct { + PyObject_HEAD + /* Type-specific fields go here. */ + const t_symbol *sym; +} pySymbol; + +PY_EXPORT extern PyTypeObject pySymbol_Type; + +PY_EXPORT extern pySymbol *pySymbol__; +PY_EXPORT extern pySymbol *pySymbol_bang; +PY_EXPORT extern pySymbol *pySymbol_list; +PY_EXPORT extern pySymbol *pySymbol_symbol; +PY_EXPORT extern pySymbol *pySymbol_float; +PY_EXPORT extern pySymbol *pySymbol_int; + + #define pySymbol_Check(op) PyObject_TypeCheck(op, &pySymbol_Type) #define pySymbol_CheckExact(op) ((op)->ob_type == &pySymbol_Type) @@ -66,27 +66,27 @@ inline PyObject *pySymbol_FromString(PyObject *str) return pySymbol_FromString(PyString_AsString(str)); } -inline const t_symbol *pySymbol_AS_SYMBOL(PyObject *op) -{ - return ((pySymbol *)op)->sym; -} - -inline const t_symbol *pySymbol_AsSymbol(PyObject *op) -{ - return pySymbol_Check(op)?pySymbol_AS_SYMBOL(op):NULL; -} - -inline const char *pySymbol_AS_STRING(PyObject *op) -{ - return flext::GetString(pySymbol_AS_SYMBOL(op)); -} - -inline const t_symbol *pyObject_AsSymbol(PyObject *op) -{ - if(PyString_Check(op)) - return flext::MakeSymbol(PyString_AS_STRING(op)); - else - return pySymbol_AsSymbol(op); -} - -#endif +inline const t_symbol *pySymbol_AS_SYMBOL(PyObject *op) +{ + return ((pySymbol *)op)->sym; +} + +inline const t_symbol *pySymbol_AsSymbol(PyObject *op) +{ + return pySymbol_Check(op)?pySymbol_AS_SYMBOL(op):NULL; +} + +inline const char *pySymbol_AS_STRING(PyObject *op) +{ + return flext::GetString(pySymbol_AS_SYMBOL(op)); +} + +inline const t_symbol *pyObject_AsSymbol(PyObject *op) +{ + if(PyString_Check(op)) + return flext::MakeSymbol(PyString_AS_STRING(op)); + else + return pySymbol_AsSymbol(op); +} + +#endif -- cgit v1.2.1