From 22eaba4518f85902489a35dbac0fd97654b59d43 Mon Sep 17 00:00:00 2001
From: Thomas Grill <xovo@users.sourceforge.net>
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;&quot;c:\data\pd\pd-cvs\src&quot;;..\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"/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="maxapi.lib maxaudio.lib pthreadVC.lib"
+				AdditionalDependencies="maxapi.lib maxaudio.lib"
 				OutputFile="&quot;C:\Programme\Gemeinsame Dateien\Cycling &apos;74\externals\flext\py.mxe&quot;"
 				LinkIncremental="1"
 				SuppressStartupBanner="TRUE"
@@ -556,7 +556,7 @@
 				Name="VCCustomBuildTool"/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="maxapi.lib maxaudio.lib pthreadVC.lib"
+				AdditionalDependencies="maxapi.lib maxaudio.lib"
 				OutputFile="./max-msvc/py.mxe"
 				LinkIncremental="1"
 				SuppressStartupBanner="TRUE"
diff --git a/externals/grill/py/readme.txt b/externals/grill/py/readme.txt
index 31f4d2a3..ecf4d27d 100644
--- a/externals/grill/py/readme.txt
+++ b/externals/grill/py/readme.txt
@@ -1,6 +1,6 @@
 py/pyext - python script objects for PD and Max/MSP
 
-Copyright (c)2002-2005 Thomas Grill (gr@grrrr.org)
+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.  
 
@@ -18,19 +18,19 @@ For OS X keep things as the are - it has Python installed by default.
 The py/pyext package should run with Python version >= 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