QDataSets are wrapped so that operators are overloaded.
*
a standard set of names are imported.
*
* This also adds things to the python search path
* (see getLocalJythonAutoplotLib) so imports will find them.
*
* @param sandbox limit symbols to safe symbols for server.
* @return PythonInterpreter ready for commands.
* @throws java.io.IOException
*/
public static InteractiveInterpreter createInterpreter(boolean sandbox) throws IOException {
if ( PySystemState.cachedir==null ) {
System.setProperty( "python.cachedir", System.getProperty("user.home")+"/autoplot_data/pycache" );
}
/// http://www.gossamer-threads.com/lists/python/python/697524
org.python.core.PySystemState pySys = new org.python.core.PySystemState();
//pySys.setdefaultencoding("utf8"); //doesn't work with Jython2.2, try with 2.5
String[] loadClasses= new String[] { "glob.py", "autoplot2017.py", "autoplotapp2017.py" }; // these must be in the root of the interpretter search path.
for ( String pysrc: loadClasses ) {
if ( pysrc.equals("glob.py") ) {
URL jarUrl= InteractiveInterpreter.class.getResource("/"+pysrc);
if ( jarUrl!=null ) {
String f= getLocalJythonLib();
pySys.path.insert(0, new PyString( f ));
} else {
logger.log(Level.WARNING, "Couldn''t find jar containing {0}. See https://sourceforge.net/p/autoplot/bugs/576/", pysrc);
}
} else if ( pysrc.equals("autoplotapp2017.py" ) ) {
String f= getLocalJythonAutoplotAppLib();
if ( !pySys.path.contains( new PyString(f) ) ) { // TODO possible bug here: PyString/String means local path is in there 4 times.
pySys.path.insert(0,new PyString(f) );
}
} else {
String f= getLocalJythonAutoplotLib();
if ( !pySys.path.contains( new PyString(f) ) ) {
pySys.path.insert(0,new PyString(f) );
}
}
}
InteractiveInterpreter interp = new InteractiveInterpreter( null, pySys );
// try {
// System.err.println("java1-> "+interp.eval("java") );
// } catch ( Exception ex ) {
// System.err.println("java1-> ok!" );
// }
boolean loadAutoplotStuff= true;
if ( loadAutoplotStuff ) {
maybeLoadAdapters();
if ( Util.isLegacyImports() ) {
URL imports= JythonOps.class.getResource("/imports2017.py");
if ( imports==null ) {
throw new RuntimeException("unable to locate imports2017.py on classpath");
} else {
logger.log(Level.FINE, "loading imports2017.py from {0}", imports);
}
InputStream in= imports.openStream(); // note this stream will load in another stream.
byte[] bimports= FileUtil.readBytes(in);
String simports= new String( bimports );
logger.log( Level.FINE, simports );
//InputStream in = imports.openStream();
try {
interp.execfile( new ByteArrayInputStream(bimports), "/imports2017.py");
} finally {
in.close();
}
}
}
interp.set( "dataset", new DatasetCommand() );
interp.set( "monitor", new NullProgressMonitor() );
// try {
// System.err.println("java2-> "+interp.eval("java") );
// } catch ( Exception ex ) {
// System.err.println("java2-> ok!" );
// }
return interp;
}
/**
* set up the interp variables scripts will often use, such as PWD and monitor.
* @param interp
* @param pwd
* @param resourceUri
* @param paramsl
* @param mon
*/
public static void setupInterp( PythonInterpreter interp, String pwd, String resourceUri, Map paramsl, ProgressMonitor mon ) {
interp.set("PWD", pwd);
interp.exec("import autoplot2017 as autoplot");
interp.exec("autoplot.params=dict()");
for ( Entry e : paramsl.entrySet()) {
String s= e.getKey();
if (!s.equals("arg_0") && !s.equals("script") ) {
String sval= e.getValue();
sval= JythonUtil.maybeQuoteString( sval );
logger.log(Level.FINE, "autoplot.params[''{0}'']={1}", new Object[]{s, sval});
interp.exec("autoplot.params['" + s + "']=" + sval);
}
}
if ( resourceUri!=null ) {
interp.set( "resourceURI", resourceUri ); // legacy
interp.exec("autoplot.params['"+"resourceURI"+"']="+ JythonUtil.maybeQuoteString( resourceUri ) );
}
interp.set("monitor", mon);
try ( InputStream in= JythonOps.class.getResource("/autoplot2017.py").openStream() ) {
interp.execfile( in, "/autoplot2017.py"); // import everything into default namespace.
} catch ( IOException ex ) {
logger.log( Level.SEVERE, ex.getMessage(), ex );
}
}
/**
* transfer the contents of in to out. in and out are closed after the operation.
* //TODO: other implementations of this exist...
* @param in
* @param out
* @throws IOException
*/
private static void transferStream( InputStream in, OutputStream out ) throws IOException {
byte[] buf= new byte[2048];
int n;
try {
n= in.read(buf);
while ( n>-1 ) {
out.write(buf,0,n);
n= in.read(buf);
}
} finally {
out.close();
in.close();
}
}
/**
* ensure that the file has a parent writable directory.
* @param file
* @return true if the folder could be made.
*/
private static boolean makeHomeFor( File file ) {
File f= file.getParentFile();
if ( !f.exists() ) {
return f.mkdirs();
} else {
return true;
}
}
/**
* copy everything out to autoplot_data/jython without going to web again. The old
* code showed issues where autoplot.org could not be resolved.
* @return the item to add to the python search path.
* @throws IOException
*/
private static String getLocalJythonLib() throws IOException {
File ff2= new File( AutoplotSettings.settings().resolveProperty(AutoplotSettings.PROP_AUTOPLOTDATA ) );
File ff3= new File( ff2.toString() + "/jython" );
File ff4= new File( ff2.toString() + "/jython/zlib.py" );
if ( ff4.exists() ) {
return ff3.toString();
}
synchronized ( JythonUtil.class ) {
if ( !ff3.exists() ) {
if ( !ff3.mkdirs() ) {
throw new IOException("Unable to mkdirs "+ff3);
}
}
}
if ( JythonUtil.class.getResource("/pylisting.txt")==null ) {
throw new IllegalArgumentException("unable to find pylisting.txt in application, which is needed to install Jython codes.");
} else {
logger.log(Level.FINE, "unpacking jython codes in {0}", JythonUtil.class.getResource("/pylisting.txt"));
try ( BufferedReader r= new BufferedReader( new InputStreamReader( JythonUtil.class.getResourceAsStream("/pylisting.txt") ) ) ) {
String s= r.readLine();
while ( s!=null ) {
File ff5= new File( ff3, s );
logger.log(Level.FINER, "copy to local folder python code: {0}", s);
InputStream in= JythonUtil.class.getResourceAsStream("/"+s);
if ( in==null ) {
throw new IllegalArgumentException("unable to find jython code which should be embedded in application: "+s);
}
if ( s.contains("/") ) {
if ( !makeHomeFor( ff5 ) ) {
throw new IOException("Unable to makeHomeFor "+ff5);
}
}
try (FileOutputStream out = new FileOutputStream( ff5 ) ) {
transferStream(in,out);
} finally {
in.close();
if ( new File( ff3, s ).setReadOnly()==false ) {
logger.log( Level.FINER, "set read-only on file {0} failed", s );
}
if ( new File( ff3, s ).setWritable( true, true )==false ) {
logger.log( Level.FINER, "set write for user only on file {0} failed", s );
}
}
s= r.readLine();
}
}
}
logger.fine(" ...done");
return ff3.toString();
}
/**
* copy all the app stuff to autoplot_data/jython without going to web again.
* @return the item to add to the python search path.
* @throws IOException
*/
private static String getLocalJythonAutoplotAppLib() throws IOException {
File ff2= new File( AutoplotSettings.settings().resolveProperty(AutoplotSettings.PROP_AUTOPLOTDATA ) );
File ff3= new File( ff2.toString() + "/jython" );
File ff4= new File( ff3.toString(), "pylistingapp2017.txt" );
if ( ff4.exists() ) {
return ff3.toString();
}
synchronized ( JythonUtil.class ) {
if ( !ff3.exists() ) {
if ( !ff3.mkdirs() ) {
throw new IOException("Unable to mkdirs "+ff3);
}
}
}
if ( JythonUtil.class.getResource("/pylistingapp2017.txt")==null ) {
logger.log( Level.FINE, "unable to find pylistingapp2017.txt in application, assuming this is not the Autoplot client application.");
} else {
logger.log(Level.FINE, "unpacking jython codes in {0}", JythonUtil.class.getResource("/pylistingapp2017.txt"));
try ( BufferedReader r= new BufferedReader( new InputStreamReader( JythonUtil.class.getResourceAsStream("/pylistingapp2017.txt") ) ) ) {
String s= r.readLine();
while ( s!=null ) {
int i= s.indexOf("#");
if ( i>-1 ) s= s.substring(0,i);
s= s.trim();
if ( s.length()>0 ) {
File ff5= new File( ff3, s );
logger.log(Level.FINER, "copy to local folder python code: {0}", s);
if ( s.contains("/") ) {
if ( !makeHomeFor( ff5 ) ) {
throw new IOException("Unable to makeHomeFor "+ff5);
}
}
if ( ff5.exists() ) {
logger.fine("already have file, skip...");
s= r.readLine();
continue;
}
InputStream in= JythonUtil.class.getResourceAsStream("/"+s);
if ( in==null ) {
throw new IllegalArgumentException("unable to find jython code which should be embedded in application: "+s);
}
//Re https://sourceforge.net/p/autoplot/bugs/1724/:
//Really each file should be copied and then renamed.
try (FileOutputStream out = new FileOutputStream( ff5 ) ) {
transferStream(in,out);
} finally {
in.close();
if ( new File( ff3, s ).setReadOnly()==false ) {
logger.log( Level.FINER, "set read-only on file {0} failed", s );
}
if ( new File( ff3, s ).setWritable( true, true )==false ) {
logger.log( Level.FINER, "set write for user only on file {0} failed", s );
}
}
}
s= r.readLine();
}
}
}
return ff3.toString();
}
/**
* copy the two python files specific to Autoplot into the user's autoplot_data/jython folder.
* This reads the version from the first line of the autoplot2017.py.
* @return the item to add to the python search path.
* @throws IOException
*/
private static String getLocalJythonAutoplotLib() throws IOException {
File ff2= new File( AutoplotSettings.settings().resolveProperty(AutoplotSettings.PROP_AUTOPLOTDATA ) );
File ff3= new File( ff2.toString() + "/jython" );
File ff4= new File( ff3, "autoplot2017.py" );
String vers= "";
// This is the version that Autoplot would like to find, and should be found within the Java class path.
double currentVersion= 1.60; //rfe320 improved getParam support.
if ( ff4.exists() ) {
try ( BufferedReader r= new BufferedReader( new FileReader( ff4 ) ) ) {
String line= r.readLine();
if ( line!=null ) {
Pattern versPattern= Pattern.compile("# autoplot2017.py v([\\d\\.]+) .*"); // must be parsable as a double.
Matcher m= versPattern.matcher(line);
if ( m.matches() ) {
vers= m.group(1);
}
}
}
}
if ( logger.isLoggable(Level.FINE) ) {
logger.fine("== JythonUtil getLocalJythonAutoplotLib ==");
logger.log(Level.FINE, "ff4.exists()={0}", ff4.exists());
logger.log(Level.FINE, "vers={0}", vers);
logger.log(Level.FINE, "currentVersion={0}", currentVersion);
}
if ( ! ff4.exists() || vers.equals("") || Double.parseDouble(vers) errs ) throws IOException {
LineNumberReader reader;
File src = DataSetURI.getFile( uri, new NullProgressMonitor());
reader = new LineNumberReader( new BufferedReader( new FileReader(src)) );
try {
return pythonLint( reader, errs );
} finally {
reader.close();
}
}
/**
* check the script that it doesn't redefine symbol names like "str"
* @param reader, which will be not be closed here.
* @param errs an empty list where the errors can be logged.
* @return true if an err is suspected.
* @throws java.io.IOException
*/
public static boolean pythonLint( LineNumberReader reader, List errs ) throws IOException {
String vnarg= "\\s*([a-zA-Z_][a-zA-Z0-9_]*)\\s*"; // any variable name VERIFIED
Pattern assign= Pattern.compile( vnarg+"=.*" );
InteractiveInterpreter interp= createInterpreter(true);
String line= reader.readLine();
while ( line!=null ) {
Matcher m= assign.matcher(line);
if ( m.matches() ) {
String vname= m.group(1);
try {
PyObject po= interp.eval(vname);
errs.add( "" + reader.getLineNumber() + ": "+ vname + "=" + po.__repr__() );
} catch ( PyException ex ) {
// this is what we want
}
}
line= reader.readLine();
}
return errs.size()>0;
}
private static boolean haveloadedAdapters= false;
/**
* load the adapters, once.
*/
private synchronized static void maybeLoadAdapters() {
if ( !haveloadedAdapters ) {
Py.getAdapter().addPostClass(new PyQDataSetAdapter());
Py.getAdapter().addPostClass(new PyDatumAdapter());
haveloadedAdapters= true;
}
}
/**
* TODO: this ought to remove the need for ParametersFormPanel.
*/
public static class Param {
public String name;
public String label; // the label for the variable used in the script
public Object deft;
public Object value; // the value if available, null means not present.
public String doc;
public List