From 29416a643b9c3d19a60b91b37a263d300c11486b Mon Sep 17 00:00:00 2001 From: Thomas Grill Date: Sat, 9 Jul 2005 13:03:34 +0000 Subject: python-like dotted module.function syntax reworked outbound message generation (now with symbols instead of one-element anythings) multiply inlets for py (hot and cold inlets) cleaned up float vs. int pyext tags pymeth object for object methods enable built-in functions sequence protocol for symbol type enabled built-in functions py: allow all callables svn path=/trunk/; revision=3310 --- externals/grill/py/package.txt | 2 +- externals/grill/py/pd/builtins-1.pd | 47 ++++ externals/grill/py/pd/methods-1.pd | 24 ++ externals/grill/py/pd/methods-2.pd | 28 ++ externals/grill/py/pd/script-1.pd | 26 +- externals/grill/py/pd/simple-2.pd | 8 +- externals/grill/py/py.vcproj | 7 + externals/grill/py/readme.txt | 5 + externals/grill/py/source/clmeth.cpp | 7 +- externals/grill/py/source/modmeth.cpp | 10 +- externals/grill/py/source/py.cpp | 137 +++++----- externals/grill/py/source/pyargs.cpp | 40 +-- externals/grill/py/source/pybase.cpp | 52 +++- externals/grill/py/source/pybase.h | 7 +- externals/grill/py/source/pyext.cpp | 9 +- externals/grill/py/source/pymeth.cpp | 449 +++++++++++++++++++++++++++++++++ externals/grill/py/source/pysymbol.cpp | 87 ++++++- 17 files changed, 819 insertions(+), 126 deletions(-) create mode 100644 externals/grill/py/pd/builtins-1.pd create mode 100644 externals/grill/py/pd/methods-1.pd create mode 100644 externals/grill/py/pd/methods-2.pd create mode 100644 externals/grill/py/source/pymeth.cpp (limited to 'externals/grill/py') diff --git a/externals/grill/py/package.txt b/externals/grill/py/package.txt index 31637831..5204b0a8 100644 --- a/externals/grill/py/package.txt +++ b/externals/grill/py/package.txt @@ -11,6 +11,6 @@ SRCS= \ py.cpp pyext.cpp modmeth.cpp clmeth.cpp \ register.cpp bound.cpp pyargs.cpp \ pysymbol.cpp pybuffer.cpp pydsp.cpp \ - pyatom.cpp pybase.cpp + pyatom.cpp pybase.cpp pymeth.cpp HDRS= pyprefix.h main.h pyext.h pysymbol.h pybuffer.h pyatom.h pybase.h diff --git a/externals/grill/py/pd/builtins-1.pd b/externals/grill/py/pd/builtins-1.pd new file mode 100644 index 00000000..095e7690 --- /dev/null +++ b/externals/grill/py/pd/builtins-1.pd @@ -0,0 +1,47 @@ +#N canvas 327 349 706 421 12; +#X obj 36 241 py .range @xlate 0; +#X floatatom 35 356 5 0 0 0 - - -; +#X obj 35 323 py .sum; +#X obj 16 13 cnv 15 650 40 empty empty py/pyext 10 22 0 24 -260818 +-1 0; +#X text 235 16 Python script objects \, (C)2003-2005 Thomas Grill; +#X text 235 32 http://grrrr.org/ext; +#X obj 36 159 nbx 5 14 -1e+037 1e+037 0 1 empty empty min 0 -6 0 10 +-262131 -1 -1 38 256; +#X obj 95 159 nbx 5 14 -1e+037 1e+037 0 1 empty empty max 0 -6 0 10 +-262131 -1 -1 100 256; +#X obj 154 159 nbx 5 14 1 100000 1 1 empty empty step 0 -6 0 10 -262131 +-1 -1 1 256; +#N canvas 0 0 462 312 pak3 0; +#X obj 34 26 inlet; +#X obj 81 28 inlet; +#X obj 129 28 inlet; +#X obj 36 158 outlet; +#X obj 36 123 pack 0 0 0; +#X obj 61 78 t b f; +#X obj 108 80 t b f; +#X obj 208 46 loadbang; +#X obj 208 73 1; +#X connect 0 0 4 0; +#X connect 1 0 5 0; +#X connect 2 0 6 0; +#X connect 4 0 3 0; +#X connect 5 0 4 0; +#X connect 5 1 4 1; +#X connect 6 0 4 0; +#X connect 6 1 4 2; +#X connect 7 0 8 0; +#X connect 8 0 4 2; +#X restore 36 195 pd pak3; +#X text 115 240 construct a Python list; +#X text 78 282 Python object pointer is propagated to next object; +#X text 106 320 calculate sum over list elements; +#X text 21 73 Py can use built-in Python functions; +#X text 21 97 A . preceding the function name searches for the function +in either the pyext module or in __builtins__; +#X connect 0 0 2 1; +#X connect 2 0 1 0; +#X connect 6 0 9 0; +#X connect 7 0 9 1; +#X connect 8 0 9 2; +#X connect 9 0 0 1; diff --git a/externals/grill/py/pd/methods-1.pd b/externals/grill/py/pd/methods-1.pd new file mode 100644 index 00000000..8d49f319 --- /dev/null +++ b/externals/grill/py/pd/methods-1.pd @@ -0,0 +1,24 @@ +#N canvas 540 469 714 349 12; +#X obj 16 13 cnv 15 650 40 empty empty py/pyext 10 22 0 24 -260818 +-1 0; +#X text 235 16 Python script objects \, (C)2003-2005 Thomas Grill; +#X text 235 32 http://grrrr.org/ext; +#X symbolatom 21 139 10 0 0 0 - - -; +#X symbolatom 25 298 10 0 0 0 - - -; +#X obj 25 252 pym upper; +#X obj 22 179 py .str @xlate 0; +#X text 93 167 convert the symbol to a Python string; +#X text 35 216 pass it as a true Python object; +#X text 105 251 use string.upper method; +#X obj 363 252 py string.upper; +#X symbolatom 364 288 10 0 0 0 - - -; +#X text 495 254 use module function; +#X text 23 119 enter some text; +#X text 21 73 Py can act on Python objects in a object-oriented manner +; +#X text 93 184 using the built-in str function; +#X connect 3 0 6 1; +#X connect 5 0 4 0; +#X connect 6 0 5 1; +#X connect 6 0 10 1; +#X connect 10 0 11 0; diff --git a/externals/grill/py/pd/methods-2.pd b/externals/grill/py/pd/methods-2.pd new file mode 100644 index 00000000..b10e183c --- /dev/null +++ b/externals/grill/py/pd/methods-2.pd @@ -0,0 +1,28 @@ +#N canvas 540 469 734 339 12; +#X obj 16 13 cnv 15 650 40 empty empty py/pyext 10 22 0 24 -260818 +-1 0; +#X text 235 16 Python script objects \, (C)2003-2005 Thomas Grill; +#X text 235 32 http://grrrr.org/ext; +#X symbolatom 21 139 10 0 0 0 - #0-t -; +#X text 23 119 enter some text; +#X text 21 73 Py can act on Python objects in a object-oriented manner +; +#X obj 25 252 pym 2 *; +#X obj 213 169 t b f; +#X text 105 251 repeat text; +#X text 215 117 multiply it!; +#X symbolatom 25 283 80 0 0 0 - - -; +#X obj 214 139 nbx 5 14 1 100 0 1 empty empty empty 0 -6 0 10 -262131 +-1 -1 34 256; +#N canvas 0 0 458 308 init 0; +#X obj 61 116 s \$0-t; +#X obj 64 44 loadbang; +#X obj 64 81 symbol a; +#X connect 1 0 2 0; +#X connect 2 0 0 0; +#X restore 606 127 pd init; +#X connect 3 0 6 1; +#X connect 6 0 10 0; +#X connect 7 0 6 0; +#X connect 7 1 6 2; +#X connect 11 0 7 0; diff --git a/externals/grill/py/pd/script-1.pd b/externals/grill/py/pd/script-1.pd index 328b096d..98fee527 100644 --- a/externals/grill/py/pd/script-1.pd +++ b/externals/grill/py/pd/script-1.pd @@ -1,4 +1,4 @@ -#N canvas 297 17 696 538 12; +#N canvas 297 17 700 542 12; #X obj 39 278 print; #X obj 345 251 print; #X msg 499 149 freakhole; @@ -26,7 +26,6 @@ file.; #X obj 39 241 py script strcat; #X obj 43 424 py script addall; #X obj 350 420 py script; -#X obj 516 419 py script ret3; #X obj 346 204 py script strlen; #X msg 21 159 dir; #X obj 146 279 print A; @@ -39,26 +38,27 @@ file.; #X text 556 181 too many args; #X text 505 372 just trigger without arguments; #X msg 386 371 set ret4; -#X connect 2 0 25 1; +#X obj 516 419 py script ret3; +#X connect 2 0 24 1; #X connect 3 0 21 1; #X connect 4 0 21 1; #X connect 6 0 22 1; #X connect 7 0 22 1; #X connect 9 0 23 0; -#X connect 11 0 24 0; +#X connect 11 0 35 0; #X connect 12 0 21 1; -#X connect 13 0 25 0; +#X connect 13 0 24 0; #X connect 14 0 23 0; #X connect 15 0 23 0; -#X connect 17 0 25 1; +#X connect 17 0 24 1; #X connect 19 0 21 0; #X connect 21 0 0 0; -#X connect 21 1 27 0; +#X connect 21 1 26 0; #X connect 22 0 5 0; #X connect 23 0 8 0; -#X connect 24 0 10 0; -#X connect 25 0 1 0; -#X connect 26 0 21 0; -#X connect 28 0 21 0; -#X connect 32 0 25 1; -#X connect 35 0 23 0; +#X connect 24 0 1 0; +#X connect 25 0 21 0; +#X connect 27 0 21 0; +#X connect 31 0 24 1; +#X connect 34 0 23 0; +#X connect 35 0 10 0; diff --git a/externals/grill/py/pd/simple-2.pd b/externals/grill/py/pd/simple-2.pd index 1845b032..4c857d49 100644 --- a/externals/grill/py/pd/simple-2.pd +++ b/externals/grill/py/pd/simple-2.pd @@ -1,4 +1,4 @@ -#N canvas 95 223 654 394 12; +#N canvas 570 275 788 398 12; #X floatatom 202 113 5 0 0 0 - - -; #X text 338 286 watch the console output!; #X msg 20 115 help; @@ -16,13 +16,11 @@ file.; #X msg 120 174 hello; #X msg 193 180 hello; #X msg 266 179 msg; -#X msg 325 154 hello 3; -#X text 338 189 special case: 'hello' handler doesn't like args \, -so _anything_ is called!; #X obj 16 13 cnv 15 600 40 empty empty py/pyext 10 22 0 24 -260818 -1 0; #X text 213 16 Python script objects \, (C)2003-2005 Thomas Grill; #X text 213 32 http://grrrr.org/ext; +#X msg 333 186 whoopie a b c; #X connect 0 0 8 1; #X connect 2 0 8 0; #X connect 4 0 8 0; @@ -36,4 +34,4 @@ so _anything_ is called!; #X connect 13 0 8 1; #X connect 14 0 8 3; #X connect 15 0 8 2; -#X connect 16 0 8 3; +#X connect 19 0 8 3; diff --git a/externals/grill/py/py.vcproj b/externals/grill/py/py.vcproj index a103c872..31e24d3c 100644 --- a/externals/grill/py/py.vcproj +++ b/externals/grill/py/py.vcproj @@ -1244,6 +1244,13 @@ + + + + sigoutlets; - if(ext->OutObject(ext,o,val)) + if(ext->OutObject(ext,o-1,val)) ok = true; else PyErr_SetString(PyExc_ValueError,"pyext - _outlet: invalid arguments"); @@ -295,11 +295,12 @@ PyObject *pyext::pyext_tocanvas(PyObject *,PyObject *args) val = PyTuple_GetSlice(args,1,sz); // new ref flext::AtomListStatic<16> lst; - if(GetPyArgs(lst,val)) { + const t_symbol *sym = GetPyArgs(lst,val); + if(sym) { t_glist *gl = ext->thisCanvas(); //canvas_getcurrent(); t_class **cl = (t_pd *)gl; if(cl) - pd_forwardmess(cl,lst.Count(),lst.Atoms()); + pd_forwardmess(cl,lst.Count(),lst.Atoms()); #ifdef FLEXT_DEBUG else post("pyext - no parent canvas?!"); diff --git a/externals/grill/py/source/modmeth.cpp b/externals/grill/py/source/modmeth.cpp index d56441ac..44299b0c 100644 --- a/externals/grill/py/source/modmeth.cpp +++ b/externals/grill/py/source/modmeth.cpp @@ -128,13 +128,9 @@ PyObject *pybase::py_send(PyObject *,PyObject *args) val = PySequence_GetSlice(args,1,sz); // new ref AtomListStatic<16> lst; - if(GetPyArgs(lst,val)) { - bool ok; - if(lst.Count() && IsSymbol(lst[0])) - ok = Forward(recv,GetSymbol(lst[0]),lst.Count()-1,lst.Atoms()+1); - else - ok = Forward(recv,lst); - + const t_symbol *sym = GetPyArgs(lst,val); + if(sym) { + bool ok = Forward(recv,sym,lst.Count(),lst.Atoms()); #ifdef FLEXT_DEBUG if(!ok) post("py/pyext - Receiver doesn't exist"); diff --git a/externals/grill/py/source/py.cpp b/externals/grill/py/source/py.cpp index d007f0c3..7cb7fa97 100644 --- a/externals/grill/py/source/py.cpp +++ b/externals/grill/py/source/py.cpp @@ -44,7 +44,7 @@ protected: virtual void Load(); virtual void Unload(); - bool SetFunction(const char *func); + bool SetFunction(const t_symbol *func); bool ResetFunction(); virtual bool thrcall(void *data); @@ -122,11 +122,15 @@ pyobj::pyobj(int argc,const t_atom *argv) PyThreadState *state = PyLockSys(); - int inlets = -1; // -1 signals non-explicit definition + int inlets; if(argc && CanbeInt(*argv)) { inlets = GetAInt(*argv); + if(inlets < 1) inlets = 1; argv++,argc--; } + else + // -1 signals non-explicit definition + inlets = -1; if(inlets >= 1) { objects = new PyObject *[inlets]; @@ -136,7 +140,7 @@ pyobj::pyobj(int argc,const t_atom *argv) AddInAnything(1+(inlets < 0?1:inlets)); AddOutAnything(); - const char *funnm = NULL; + const t_symbol *funnm = NULL; // init script module if(argc) { @@ -151,15 +155,24 @@ pyobj::pyobj(int argc,const t_atom *argv) char *pt = strrchr(modnm,'.'); // search for last dot if(pt && *pt) { - funnm = pt+1; + funnm = MakeSymbol(pt+1); *pt = 0; - } - char dir[1024]; - GetModulePath(modnm,dir,sizeof(dir)); - AddToPath(dir); +#if 0 + if(!*modnm) + // if module name is empty set it to __builtin__ + strcpy(modnm,"__builtin__"); +#endif + } - ImportModule(modnm); + if(*modnm) { + char dir[1024]; + GetModulePath(modnm,dir,sizeof(dir)); + AddToPath(dir); + ImportModule(modnm); + } + else + ImportModule(NULL); } else PyErr_SetString(PyExc_ValueError,"Invalid module name"); @@ -169,7 +182,7 @@ pyobj::pyobj(int argc,const t_atom *argv) if(funnm || argc) { if(!funnm) { - funnm = GetAString(*argv); + funnm = GetASymbol(*argv); argv++,argc--; } @@ -225,7 +238,7 @@ void pyobj::m_set(int argc,const t_atom *argv) } if(argc) { - const char *fn = GetAString(*argv); + const t_symbol *fn = GetASymbol(*argv); if(fn) SetFunction(fn); else @@ -269,18 +282,24 @@ void pyobj::m_help() bool pyobj::ResetFunction() { + // function was borrowed from dict! function = NULL; - if(!module || !dict) - post("%s - No module loaded",thisName()); + if(!dict) + post("%s - No namespace available",thisName()); else { if(funname) { function = PyDict_GetItemString(dict,(char *)GetString(funname)); // borrowed!!! + + if(!function && dict == module_dict) + // search also in __builtins__ + function = PyDict_GetItemString(builtins_dict,(char *)GetString(funname)); // borrowed!!! + if(!function) PyErr_SetString(PyExc_AttributeError,"Function not found"); - else if(!PyFunction_Check(function)) { + else if(!PyCallable_Check(function)) { function = NULL; - PyErr_SetString(PyExc_TypeError,"Attribute is not a function"); + PyErr_SetString(PyExc_TypeError,"Attribute is not callable"); } } } @@ -289,10 +308,10 @@ bool pyobj::ResetFunction() return function != NULL; } -bool pyobj::SetFunction(const char *func) +bool pyobj::SetFunction(const t_symbol *func) { if(func) { - funname = MakeSymbol(func); + funname = func; withfunction = ResetFunction(); } else { @@ -307,7 +326,7 @@ bool pyobj::SetFunction(const char *func) void pyobj::LoadModule() { - SetFunction(funname?GetString(funname):NULL); + SetFunction(funname); } void pyobj::UnloadModule() @@ -342,68 +361,68 @@ bool pyobj::callpy(PyObject *fun,PyObject *args) 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 = PyLock(); + bool ret = false; - if(n == 0 && s != sym_bang) goto end; - if(objects && n >= 1) { // store args PyObject *&obj = objects[n-1]; Py_DECREF(obj); obj = MakePyArg(s,argc,argv); // steal reference - if(n > 1) { - ret = true; // just store, don't trigger - goto end; - } + if(n > 1) ret = true; // just store, don't trigger } - PyThreadState *state = PyLock(); - - if(withfunction) { - if(function) { - Py_INCREF(function); - - PyObject *pargs; - - if(objects) { - int inlets = CntIn()-1; - pargs = PyTuple_New(inlets); - for(int i = 0; i < inlets; ++i) { - Py_INCREF(objects[i]); - PyTuple_SET_ITEM(pargs,i,objects[i]); + if(!ret) { + if(withfunction) { + if(function) { + Py_INCREF(function); + + PyObject *pargs; + + if(objects) { + int inlets = CntIn()-1; + pargs = PyTuple_New(inlets); + for(int i = 0; i < inlets; ++i) { + Py_INCREF(objects[i]); + PyTuple_SET_ITEM(pargs,i,objects[i]); + } } - } - else - // construct tuple from args - pargs = MakePyArgs(s,argc,argv); + else + // construct tuple from args + // if n == 0, it's a pure bang + pargs = MakePyArgs(n?s:NULL,argc,argv); - ret = gencall(function,pargs); // references are stolen + ret = gencall(function,pargs); // references are stolen + } + else + PyErr_SetString(PyExc_RuntimeError,"No function set"); } - else - PyErr_SetString(PyExc_RuntimeError,"No function set"); - } - else if(module) { - // no function defined as creation argument -> use message tag - if(s) { - PyObject *func = PyObject_GetAttrString(module,const_cast(GetString(s))); - if(func) { - PyObject *pargs = MakePyArgs(sym_list,argc,argv); - ret = gencall(func,pargs); + else if(module) { + // no function defined as creation argument -> use message tag + if(s) { + PyObject *func = PyObject_GetAttrString(module,const_cast(GetString(s))); + if(func) { + PyObject *pargs = MakePyArgs(sym_list,argc,argv); + ret = gencall(func,pargs); + } } + else + PyErr_SetString(PyExc_RuntimeError,"No function set"); } - else - PyErr_SetString(PyExc_RuntimeError,"No function set"); - } - Report(); + Report(); + } PyUnlock(state); Respond(ret); -end: - return ret || flext_base::CbMethodResort(n,s,argc,argv); + return ret; } void pyobj::CbClick() { pybase::OpenEditor(); } diff --git a/externals/grill/py/source/pyargs.cpp b/externals/grill/py/source/pyargs.cpp index f08369a7..5c903f17 100644 --- a/externals/grill/py/source/pyargs.cpp +++ b/externals/grill/py/source/pyargs.cpp @@ -17,19 +17,19 @@ static PyObject *MakePyAtom(const t_atom &at) { if(flext::IsSymbol(at)) return pySymbol_FromSymbol(flext::GetSymbol(at)); -/* +#if 1 else if(flext::CanbeInt(at) || flext::CanbeFloat(at)) { // if a number can be an integer... let it be an integer! int ival = flext::GetAInt(at); double fval = flext::GetAFloat(at); return (double)ival == fval?PyInt_FromLong(ival):PyFloat_FromDouble(fval); } -*/ +#else else if(flext::IsFloat(at)) return PyFloat_FromDouble(flext::GetFloat(at)); else if(flext::IsInt(at)) return PyInt_FromLong(flext::GetInt(at)); - +#endif return NULL; } @@ -99,7 +99,8 @@ PyObject *pybase::MakePyArg(const t_symbol *s,int argc,const t_atom *argv) if(s == symatom && (ret = MakePyAtom(argc,argv)) != NULL) { // ok! } - else if(argc == 1 && IsAtom(s)) + else if(argc == 1 && !IsAnything(s)) + // convert atoms and one-element lists ret = MakePyAtom(*argv); else { bool any = s != sym_list; @@ -125,14 +126,13 @@ PyObject *pybase::MakePyArg(const t_symbol *s,int argc,const t_atom *argv) return ret; } -bool pybase::GetPyArgs(AtomList &lst,PyObject *pValue,int offs) +const t_symbol *pybase::GetPyArgs(AtomList &lst,PyObject *pValue,int offs) { if(pValue == NULL) return false; // analyze return value or tuple int rargc = 0; - bool ok = true; retval tp = nothing; if(PyString_Check(pValue)) { @@ -152,6 +152,8 @@ bool pybase::GetPyArgs(AtomList &lst,PyObject *pValue,int offs) lst(offs+rargc); + const t_symbol *sym = NULL; + for(int ix = 0; ix < rargc; ++ix) { PyObject *arg; if(tp == sequ) @@ -160,11 +162,11 @@ bool pybase::GetPyArgs(AtomList &lst,PyObject *pValue,int offs) arg = pValue; t_atom &at = lst[offs+ix]; - if(PyInt_Check(arg)) SetInt(at,PyInt_AsLong(arg)); - else if(PyLong_Check(arg)) SetInt(at,PyLong_AsLong(arg)); - else if(PyFloat_Check(arg)) SetFloat(at,(float)PyFloat_AsDouble(arg)); - else if(pySymbol_Check(arg)) SetSymbol(at,pySymbol_AS_SYMBOL(arg)); - else if(PyString_Check(arg)) SetString(at,PyString_AS_STRING(arg)); + if(PyInt_Check(arg)) { SetInt(at,PyInt_AsLong(arg)); sym = sym_fint; } + else if(PyLong_Check(arg)) { SetInt(at,PyLong_AsLong(arg)); sym = sym_fint; } + else if(PyFloat_Check(arg)) { SetFloat(at,(float)PyFloat_AsDouble(arg)); sym = sym_float; } + else if(pySymbol_Check(arg)) { SetSymbol(at,pySymbol_AS_SYMBOL(arg)); sym = sym_symbol; } + else if(PyString_Check(arg)) { SetString(at,PyString_AS_STRING(arg)); sym = sym_symbol; } /* else if(ix == 0 && self && PyInstance_Check(arg)) { // assumed to be self ... that should be checked _somehow_ !!! @@ -180,25 +182,27 @@ bool pybase::GetPyArgs(AtomList &lst,PyObject *pValue,int offs) post("py/pyext: Could not convert argument %s",tmp); Py_XDECREF(stp); Py_XDECREF(tp); - ok = false; + + SetSymbol(at,sym__); sym = sym_symbol; } if(tp == sequ) Py_DECREF(arg); } - return ok; + if(sym && tp == sequ) sym = sym_list; + + return sym; } -bool pybase::GetPyAtom(AtomList &lst,PyObject *obj) +const t_symbol *pybase::GetPyAtom(AtomList &lst,PyObject *obj) { size_t atom = PyAtom::Register(obj); size_t szat = sizeof(atom)/2; - lst(1+szat); - SetSymbol(lst[0],symatom); + lst(szat); for(size_t i = 0; i < szat; ++i,atom >>= 16) - flext::SetInt(lst[i+1],(int)(atom&((1<<16)-1))); - return true; + flext::SetInt(lst[i],(int)(atom&((1<<16)-1))); + return symatom; } diff --git a/externals/grill/py/source/pybase.cpp b/externals/grill/py/source/pybase.cpp index cc51a59e..3bc589de 100644 --- a/externals/grill/py/source/pybase.cpp +++ b/externals/grill/py/source/pybase.cpp @@ -62,6 +62,10 @@ void pybase::FreeThreadState() 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; // ----------------------------------------------------------------------------------------------------------- @@ -128,6 +132,9 @@ void pybase::lib_setup() 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); @@ -144,9 +151,16 @@ void pybase::lib_setup() initsamplebuffer(); PyModule_AddObject(module_obj,"Buffer",(PyObject *)&pySamplebuffer_Type); +#if FLEXT_SYS == FLEXT_SYS_PD + sym_fint = sym_float; +#else + sym_fint = sym_int; +#endif + // ------------------------------------------------------------- FLEXT_SETUP(pyobj); + FLEXT_SETUP(pymeth); FLEXT_SETUP(pyext); FLEXT_DSP_SETUP(pydsp); @@ -213,8 +227,11 @@ void pybase::GetDir(PyObject *obj,AtomList &lst) if(!pvar) PyErr_Print(); // no method found else { - if(!GetPyArgs(lst,pvar)) + 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); } @@ -294,9 +311,12 @@ void pybase::SetArgs() bool pybase::ImportModule(const char *name) { - if(!name) return false; - if(modname == name) return true; - modname = name; + if(name) { + if(modname == name) return true; + modname = name; + } + else + modname.clear(); return ReloadModule(); } @@ -321,9 +341,17 @@ bool pybase::ReloadModule() bool ok = false; SetArgs(); - PyObject *newmod = module - ?PyImport_ReloadModule(module) - :PyImport_ImportModule((char *)modname.c_str()); + PyObject *newmod; + + if(modname.length()) + newmod = module + ?PyImport_ReloadModule(module) + :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 @@ -415,15 +443,11 @@ void pybase::AddCurrentPath(t_canvas *cnv) bool pybase::OutObject(flext_base *ext,int o,PyObject *obj) { flext::AtomListStatic<16> lst; - if(xlate?GetPyArgs(lst,obj):GetPyAtom(lst,obj)) { + const t_symbol *sym = xlate?GetPyArgs(lst,obj):GetPyAtom(lst,obj); + if(sym) { // call to outlet _outside_ the Mutex lock! // otherwise (if not detached) deadlock will occur - if(lst.Count() && IsSymbol(lst[0])) - ext->ToOutAnything(o,GetSymbol(lst[0]),lst.Count()-1,lst.Atoms()+1); - else if(lst.Count() > 1) - ext->ToOutList(o,lst); - else - ext->ToOutAtom(o,lst[0]); + ext->ToOutAnything(o,sym,lst.Count(),lst.Atoms()); return true; } else diff --git a/externals/grill/py/source/pybase.h b/externals/grill/py/source/pybase.h index ea5f6b49..068aeb86 100644 --- a/externals/grill/py/source/pybase.h +++ b/externals/grill/py/source/pybase.h @@ -26,8 +26,8 @@ public: static PyObject *MakePyArgs(const t_symbol *s,int argc,const t_atom *argv,int inlet = -1); static PyObject *MakePyArg(const t_symbol *s,int argc,const t_atom *argv); - static bool GetPyArgs(AtomList &lst,PyObject *pValue,int offs = 0); - static bool GetPyAtom(AtomList &lst,PyObject *pValue); + static const t_symbol *GetPyArgs(AtomList &lst,PyObject *pValue,int offs = 0); + static const t_symbol *GetPyAtom(AtomList &lst,PyObject *pValue); static void lib_setup(); @@ -94,6 +94,7 @@ protected: // --- 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); @@ -161,6 +162,8 @@ protected: static PyThreadState *FindThreadState() { return NULL; } #endif + static const t_symbol *sym_fint; // float or int symbol, depending on native number message type + public: #ifdef FLEXT_THREADS diff --git a/externals/grill/py/source/pyext.cpp b/externals/grill/py/source/pyext.cpp index 88b5de0c..593c8bd1 100644 --- a/externals/grill/py/source/pyext.cpp +++ b/externals/grill/py/source/pyext.cpp @@ -420,7 +420,9 @@ void pyext::m_get(const t_symbol *s) PyObject *pvar = PyObject_GetAttrString(pyobj,const_cast(GetString(s))); /* fetch bound method */ if(pvar) { flext::AtomListStatic<16> lst; - if(GetPyArgs(lst,pvar,1)) { + const t_symbol *sym = GetPyArgs(lst,pvar,1); + if(sym) { + FLEXT_ASSERT(!IsAnything(sym)); // dump value to attribute outlet SetSymbol(lst[0],s); ToOutAnything(GetOutAttr(),sym_get,lst.Count(),lst.Atoms()); @@ -469,7 +471,10 @@ void pyext::m_set(int argc,const t_atom *argv) bool pyext::CbMethodResort(int n,const t_symbol *s,int argc,const t_atom *argv) { - return (pyobj && n >= 1 && work(n,s,argc,argv)) || flext_dsp::CbMethodResort(n,s,argc,argv); + if(!n) + return flext_dsp::CbMethodResort(n,s,argc,argv); + + return pyobj && work(n,s,argc,argv); } diff --git a/externals/grill/py/source/pymeth.cpp b/externals/grill/py/source/pymeth.cpp new file mode 100644 index 00000000..8c80bd43 --- /dev/null +++ b/externals/grill/py/source/pymeth.cpp @@ -0,0 +1,449 @@ +/* + +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); + virtual void CbClick(); + + 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 bool thrcall(void *data); + virtual void DumpOut(const t_symbol *sym,int argc,const t_atom *argv); + + PyObject **objects; + +private: + + virtual bool callpy(PyObject *fun,PyObject *args); + + 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(xlate) + 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) + FLEXT_THREAD(threadworker) + FLEXT_THREAD_X(work_wrapper) +#else + FLEXT_CALLBACK_X(work_wrapper) +#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,"xlate",xlate); + 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); + // launch thread worker + FLEXT_CALLMETHOD(threadworker); +#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); +} + +bool pymeth::callpy(PyObject *fun,PyObject *args) +{ + PyObject *ret = PyObject_CallObject(fun,args); + if(ret == NULL) { + // function not found resp. arguments not matching + PyErr_Print(); + return false; + } + else { + if(ret != Py_None && !OutObject(this,0,ret) && PyErr_Occurred()) + PyErr_Print(); + Py_DECREF(ret); + return true; + } +} + +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 = PyLock(); + + 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]); + } + + ret = gencall(function,pargs); // references are stolen + } + else + PyErr_SetString(PyExc_RuntimeError,"No function set"); + + Report(); + } + + PyUnlock(state); + + Respond(ret); + + return ret; +} + +void pymeth::CbClick() { pybase::OpenEditor(); } + +void pymeth::DumpOut(const t_symbol *sym,int argc,const t_atom *argv) +{ + ToOutAnything(GetOutAttr(),sym?sym:thisTag(),argc,argv); +} + +bool pymeth::thrcall(void *data) +{ + return FLEXT_CALLMETHOD_X(work_wrapper,data); +} diff --git a/externals/grill/py/source/pysymbol.cpp b/externals/grill/py/source/pysymbol.cpp index 189d6a6f..be7d3139 100644 --- a/externals/grill/py/source/pysymbol.cpp +++ b/externals/grill/py/source/pysymbol.cpp @@ -81,6 +81,89 @@ static long symbol_hash(PyObject *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*/ @@ -94,7 +177,7 @@ PyTypeObject pySymbol_Type = { 0, /*tp_compare*/ symbol_repr, /*tp_repr*/ 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ + &symbol_as_seq, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ symbol_hash, /*tp_hash */ 0, /*tp_call*/ @@ -108,7 +191,7 @@ PyTypeObject pySymbol_Type = { 0, /* tp_clear */ symbol_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ + symbol_iter, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ -- cgit v1.2.1