From 22eaba4518f85902489a35dbac0fd97654b59d43 Mon Sep 17 00:00:00 2001 From: Thomas Grill Date: Thu, 23 Mar 2006 01:42:05 +0000 Subject: added message bundle functionality (pyext.Bundle class) enable compiled-only scripts (without .py) small optimizations and fixes some optimizations and py reload fix better error message for reload with invalid args enable module packages (module/__init__.py[co]), now also for Max svn path=/trunk/; revision=4750 --- externals/grill/py/license.txt | 4 +- externals/grill/py/py.vcproj | 6 +- externals/grill/py/readme.txt | 22 ++-- externals/grill/py/scripts/sendrecv.py | 6 +- externals/grill/py/source/py.cpp | 11 +- externals/grill/py/source/pybase.cpp | 207 ++++++++++++++++++++++----------- externals/grill/py/source/pybase.h | 1 - externals/grill/py/source/pyext.cpp | 4 - 8 files changed, 167 insertions(+), 94 deletions(-) diff --git a/externals/grill/py/license.txt b/externals/grill/py/license.txt index f3813962..66c01ee4 100644 --- a/externals/grill/py/license.txt +++ b/externals/grill/py/license.txt @@ -1,5 +1,5 @@ py/pyext - python script objects for PD and MaxMSP -Copyright (C) 2002-2005 Thomas Grill +Copyright (C) 2002-2006 Thomas Grill This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -28,7 +28,7 @@ This package uses the flext C++ layer - See its license text below: --- flext ---------------------------------------------- flext - C++ layer for Max/MSP and pd (pure data) externals -Copyright (C) 2001-2005 Thomas Grill +Copyright (C) 2001-2006 Thomas Grill This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/externals/grill/py/py.vcproj b/externals/grill/py/py.vcproj index 25b2ec33..021edc52 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" - PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;PY_EXPORTS;FLEXT_SYS=2;FLEXT_THREADS;PY_NUMARRAY" + PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;PY_EXPORTS;FLEXT_SYS=2;FLEXT_THREADS;PY_NUMARRAY;xFLEXT_USECMEM" BasicRuntimeChecks="3" RuntimeLibrary="1" RuntimeTypeInfo="TRUE" @@ -488,7 +488,7 @@ Name="VCCustomBuildTool"/> = 2.1. It has been thoroughly tested with versions 2.2 to 2.4 ----------------------------------------------------------------------------- - -Goals/features of the package: - -Access the flexibility of the python language in PD and MaxMSP +Check out the sample patches and scripts -PD - Load it as i library with e.g. "pd -lib py -path scripts" +---------------------------------------------------------------------------- +Installation: +PD version >= 0.38 - Add "py" to the Startup items ("binaries to load") +PD version < 0.38 - Load it as i library with e.g. "pd -lib py -path scripts" -Check out the sample patches and scripts +Max/MSP - Copy py-objectmappings.txt into the init folder and py.mxe (Windows) or py.mxo (OSX) into the externals folder. +---------------------------------------------------------------------------- Description: @@ -97,7 +97,7 @@ Version history: - ADD: Python objects can be sent/received through outlets/inlets - ADD: py can have multiple inlets for multiple function arguments (right inlets are non-triggering) - ADD: allow module.function syntax for py and pyext -- FIX: pyext: cleanup up float vs. int ... first decision is made by tag, afterwards a conversion is tried +- FIX: pyext: cleanup float vs. int ... first decision is made by tag, afterwards a conversion is tried - ADD: pym: object-oriented object... Python methods for any object type - ADD: py: allow all callables (also object constructors and builtins) - ADD: py: enable Python built-in functions (like range, str etc.) @@ -115,6 +115,8 @@ Version history: - ADD: enable symbol binding for all callables (not only functions and methods) - ADD: Buffer.resize(frames,keep=1,zero=1) method - ADD: py.Bundle class to support flext message bundles +- ADD: enable usage of compiled-only modules (.py[co]) +- ADD: enable usage of module packages (with module/__init__.py[co]) 0.2.0: - ADD: handling of Python threads @@ -196,10 +198,12 @@ TODO list: bugs: - crashes with long Python printouts +- pybase::GetModulePath should also look for .pyc and .pyo general: - Documentation and better example patches - better error reporting for runtime errors +- we should pre-scan and cache class methods features: - enable multiple interpreters? ( -> not possible within one thread) diff --git a/externals/grill/py/scripts/sendrecv.py b/externals/grill/py/scripts/sendrecv.py index a8b57469..9d873bae 100644 --- a/externals/grill/py/scripts/sendrecv.py +++ b/externals/grill/py/scripts/sendrecv.py @@ -135,7 +135,7 @@ class ex3(pyext._class): """Class constructor""" # called scripting method should run on its own thread - if self._isthreaded(): + if self._isthreaded: print "Threading is on" self._detach(1) @@ -143,7 +143,7 @@ class ex3(pyext._class): """Do some scripting - PD only!""" num = 12 # number of objects - ori = complex(150,150) # origin + ori = complex(150,180) # origin rad = 100 # radius l = range(num) # initialize list @@ -152,7 +152,7 @@ class ex3(pyext._class): for i in xrange(num): l[i] = ori+rad*exp(complex(0,i*2*pi/num)) self._tocanvas("obj",l[i].real,l[i].imag,"bng",15,250,50,0,"empty","yeah"+str(i),"empty",0,-6,64,8,0,-1,-1) - self._tocanvas("connect",2,0,3+i,0) + self._tocanvas("connect",6,0,7+i,0) # blink for i in range(10): diff --git a/externals/grill/py/source/py.cpp b/externals/grill/py/source/py.cpp index 3ef823a0..c773ffcb 100644 --- a/externals/grill/py/source/py.cpp +++ b/externals/grill/py/source/py.cpp @@ -156,12 +156,8 @@ pyobj::pyobj(int argc,const t_atom *argv) *pt = 0; } - if(*modnm) { - char dir[1024]; - GetModulePath(modnm,dir,sizeof(dir)); - AddToPath(dir); + if(*modnm) ImportModule(modnm); - } else ImportModule(NULL); } @@ -219,7 +215,8 @@ void pyobj::m_set(int argc,const t_atom *argv) ++argv,--argc; if(sn) { - if(!module || !strcmp(sn,PyModule_GetName(module))) { +// if(!module || !strcmp(sn,PyModule_GetName(module))) + { ImportModule(sn); Register(GetRegistry(REGNAME)); } @@ -244,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-2005 Thomas Grill",thisName(),PY__VERSION); + post("%s %s - python script object, (C)2002-2006 Thomas Grill",thisName(),PY__VERSION); #ifdef FLEXT_DEBUG post("DEBUG VERSION, compiled on " __DATE__ " " __TIME__); #endif diff --git a/externals/grill/py/source/pybase.cpp b/externals/grill/py/source/pybase.cpp index f20328d8..f4762fbf 100644 --- a/externals/grill/py/source/pybase.cpp +++ b/externals/grill/py/source/pybase.cpp @@ -101,7 +101,7 @@ void pybase::lib_setup() post(""); post("------------------------------------------------"); post("py/pyext %s - python script objects",PY__VERSION); - post("(C)2002-2005 Thomas Grill - http://grrrr.org/ext"); + post("(C)2002-2006 Thomas Grill - http://grrrr.org/ext"); post(""); post("using Python %s",Py_GetVersion()); @@ -220,6 +220,7 @@ FLEXT_LIB_SETUP(py,pybase::lib_setup) pybase::pybase() : module(NULL) + , dict(NULL) #ifdef FLEXT_THREADS , thrcount(0) , shouldexit(false),stoptick(0) @@ -387,7 +388,7 @@ void pybase::OpenEditor() char *editor = getenv("EDITOR"); - if(!editor || !strcmp(editor, "/usr/bin/nano") || !strcmp(editor, "/usr/bin/pico") || !strcmp(editor, "/usr/bin/vi")) { + 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"; } @@ -424,44 +425,163 @@ void pybase::SetArgs() 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,"."); +#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 1 + 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) return true; - modname = 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) return; + if(module) { + FLEXT_ASSERT(dict && module_obj && module_dict); - FLEXT_ASSERT(dict && module_obj && module_dict); + Py_DECREF(module); - 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 - // 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; + module = NULL; + dict = NULL; + } } bool pybase::ReloadModule() { - bool ok = false; - SetArgs(); PyObject *newmod; - if(modname.length()) - newmod = module - ?PyImport_ReloadModule(module) - :PyImport_ImportModule((char *)modname.c_str()); + if(modname.length()) { + if(module) + newmod = PyImport_ReloadModule(module); + else { + // search in module path + char dir[1024]; + if(getmodulepath(modname.c_str(),dir,sizeof(dir))) { + AddToPath(dir); + newmod = PyImport_ImportModule((char *)modname.c_str()); + } + else { + PyErr_SetString(PyExc_ImportError,"Module not found in path"); + newmod = NULL; + } + } + } else { // if no module name given, take py module newmod = module_obj; @@ -470,58 +590,15 @@ bool pybase::ReloadModule() if(!newmod) { // unload faulty module - if(module) UnimportModule(); + UnimportModule(); + return false; } else { Py_XDECREF(module); module = newmod; dict = PyModule_GetDict(module); // borrowed - ok = true; + return true; } - return ok; -} - -void pybase::GetModulePath(const char *mod,char *dir,int len) -{ -#if FLEXT_SYS == FLEXT_SYS_PD - // uarghh... pd doesn't show its path for extra modules - - char *name; - int fd = open_via_path("",mod,".py",dir,&name,len,0); - if(fd > 0) close(fd); - else name = NULL; - - // if dir is current working directory... name points to dir - if(dir == name) strcpy(dir,"."); -#elif FLEXT_SYS == FLEXT_SYS_MAX - // how do i get the path in Max/MSP? - short path; - long type; - char smod[1024]; - strcat(strcpy(smod,mod),".py"); - if(!locatefile_extended(smod,&path,&type,&type,-1)) { -#if FLEXT_OS == FLEXT_OS_WIN - path_topathname(path,NULL,dir); -#else - // convert pathname to unix style - path_topathname(path,NULL,smod); - char *colon = strchr(smod,':'); - if(colon) { - *colon = 0; - strcpy(dir,"/Volumes/"); - strcat(dir,smod); - strcat(dir,colon+1); - } - else - strcpy(dir,smod); -#endif - } - else - // not found - *dir = 0; -#else - *dir = 0; -#endif } void pybase::AddToPath(const char *dir) diff --git a/externals/grill/py/source/pybase.h b/externals/grill/py/source/pybase.h index 3645a7af..6b661268 100644 --- a/externals/grill/py/source/pybase.h +++ b/externals/grill/py/source/pybase.h @@ -53,7 +53,6 @@ protected: AtomList args; void AddCurrentPath(flext_base *o); - void GetModulePath(const char *mod,char *dir,int len); void SetArgs(); bool OutObject(flext_base *ext,int o,PyObject *obj); diff --git a/externals/grill/py/source/pyext.cpp b/externals/grill/py/source/pyext.cpp index d7449200..5c573bd2 100644 --- a/externals/grill/py/source/pyext.cpp +++ b/externals/grill/py/source/pyext.cpp @@ -162,10 +162,6 @@ pyext::pyext(int argc,const t_atom *argv,bool sig): } } - char dir[1024]; - GetModulePath(modnm,dir,sizeof(dir)); - AddToPath(dir); - ImportModule(modnm); } else -- cgit v1.2.1