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) {
String autoplotData= AutoplotSettings.settings().resolveProperty(AutoplotSettings.PROP_AUTOPLOTDATA);
System.setProperty("python.cachedir", autoplotData + "/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 = 2.00; //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) < currentVersion) {
logger.log(Level.FINE, "looking for version={0} of {1}, but didn''t find it.", new Object[]{currentVersion, ff4});
logger.log(Level.FINE, "doesn't seem like we have the right file, downloading...");
synchronized (JythonUtil.class) {
if (!ff3.exists()) {
if (!ff3.mkdir()) {
throw new IOException("Unable to mkdir " + ff3);
}
}
}
String[] ss = new String[]{"autoplot2017.py", "autoplotapp2017.py"};
for (String s : ss) {
try (InputStream in = JythonUtil.class.getResourceAsStream("/" + s);
FileOutputStream out = new FileOutputStream(new File(ff3, s))) {
transferStream(in, out);
}
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);
}
}
}
logger.fine(" ...done");
return ff3.toString();
}
/**
* check the script that it doesn't redefine symbol names like "str"
*
* @param uri, such as sftp://user@host/script.jy
* @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(URI uri, List 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(2);
String indent= m.group(1);
try {
if ( vname.equals("xrange") && indent.length()>0 ) {
logger.fine("this is just xrange keyword in plot");
} else {
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