/* * Copyright (c) 2012 European Synchrotron Radiation Facility, * Diamond Light Source Ltd. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.dawb.fabio; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.util.HashMap; import java.util.Map; import jep.Jep; import jep.JepException; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.swt.widgets.Display; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Wrapper for Java embedded Python (Jep) to create and return a Jep * interpreter. * * @author andy * */ public class FableJep { /** * The PYTHONPATH which is prepended to sys.path when starting FableJep. */ private static String pythonPath = ""; private Jep jep = null; private String jepError; private long jepLastError = 0; private Logger logger; private String filename = ""; private boolean requireErrorMessage; /** * This value is set to true to record Python calls into a file. */ private static boolean bRecord = false; public Jep getJep() { return jep; } private static Map<Long, FableJep> threadCache; /** * This method will return a FableJep instance unique for the calling Thread. * */ public static synchronized FableJep getFableJep() throws Throwable { if (threadCache==null) threadCache = new HashMap<Long, FableJep>(7); final long id = Thread.currentThread().getId(); if (threadCache.containsKey(id)) return threadCache.get(id); final FableJep fj = new FableJep(false); threadCache.put(id, fj); return fj; } public static synchronized void closeFableJep() throws Throwable { if (threadCache==null) return; final long id = Thread.currentThread().getId(); if (threadCache.containsKey(id)) { FableJep jj = threadCache.get(id); jj.close(); } } /** * Close Jep. This releases the Python subinterpreter and Python will * garbage collect any object created during the script's execution. This * must be called when needed or you may see large memory usage. * * You must call this method to avoid memory leaks * @throws Throwable */ public void close() throws Throwable { if (jep != null) { jep.isValidThread(); jep.close(); jep = null; } threadCache.remove(Thread.currentThread().getId()); } /** * * @return a new jep object * @throws JepException */ private FableJep(final boolean requireErrorMessage) throws Throwable { try { this.requireErrorMessage = requireErrorMessage; this.jep = new Jep(true, null, Thread.currentThread().getContextClassLoader()); jepImportModules("sys", requireErrorMessage); jep.eval("if not hasattr(sys,'argv'):\n\tsys.argv = ['fable']"); jepSetPythonPath(jep); logger = LoggerFactory.getLogger(FableJep.class); // Only switch to Level.INFO when really necessary (i.e. debugging) //logger.setLevel(Level.ERROR); } catch (JepException ex) { throw ex; } catch (UnsatisfiedLinkError e) { if (System.currentTimeMillis() - jepLastError > 5000) { jepLastError = System.currentTimeMillis(); String os = System.getProperty("os.name"); if (os.toLowerCase().contains("windows")) { jepError = "Failed to create the Java embedded Python " + "interpreter (jep)."; jepError += "The error was :\n\n"; jepError += e.getMessage() + "\n\n"; jepError += "Make sure Python is installed. "; jepError += "You can (partially) test your environment" + " by typing python"; } else if (os.toLowerCase().contains("mac")) { jepError = "Failed to create the Java embedded Python" + " interpreter (jep)."; jepError += "The error was :\n\n"; jepError += e.getMessage() + "\n\n"; jepError += "Make sure Python is installed and and" + " that you have a "; jepError += "" + "symbolic link " + "from $IMAGEVIEWER_HOME/plugins/jep_2.0.1/lib.macosx "; jepError += "to /Library/Java/Extensions e.g.\n\n"; jepError += "sudo ln -s " + "$IMAGEVIEWER_HOME/plugins/jep_2.0.1/lib/macosx/libjep.dylib " + "/Library/Java/Extensions/libjep.jnilib"; } else { jepError = "Failed to create the Java embedded Python " + "interpreter (jep). "; jepError += "The error was :\n\n"; jepError += e.getMessage() + "\n\n"; jepError += "Make sure Python is installed and " + "that the environment "; jepError += "variable LD_PRELOAD is pointing to your " + "Python shared object interpreter "; jepError += "e.g. LD_PRELOAD=/usr/lib/libpython2.6.so.1.0. "; jepError += "Your current LD_PRELOAD is :\n\n" + System.getenv("LD_PRELOAD") + "\n\n"; jepError += "You can (partially) test your environment " + "by typing python "; } if (requireErrorMessage) { Display.getDefault().asyncExec(new Runnable() { @Override public void run() { MessageDialog.openConfirm(Display.getDefault() .getActiveShell(), "Confirm", jepError); } }); } else { throw new Exception(jepError, e); } jepLastError = System.currentTimeMillis(); } } } /** * Prepend the pythonPath to sys.path. * * @throws JepException */ public void jepSetPythonPath(Jep jep) throws JepException { String ps = System.getProperty("path.separator", "|"); String path = pythonPath.replace("\\", "\\\\"); String[] paths = path.split(ps); String cmd; for (int i = paths.length - 1; i >= 0; i--) { cmd = "sys.path.insert(0, '" + paths[i] + "')"; jep.eval(cmd); } } public void jepImportModules(String _modules) throws JepException { jepImportModules(_modules, true); } /** * Import a module for the jep instance. * * @param _jep * @param _modules * @throws JepException * @throws JepException */ public void jepImportModules(String _modules, boolean requireErrorMessage) throws JepException { try { final Jep _jep = getJep(); _jep.eval("import " + _modules); writeScript("import " + _modules); } catch (JepException e) { if (System.currentTimeMillis() - jepLastError > 5000) { jepLastError = System.currentTimeMillis(); String os = System.getProperty("os.name"); jepError = "Failed to import the modules " + _modules + " into " + "the Java embedded Python interpreter (jep). "; jepError += "The error was :\n\n"; jepError += e.getMessage() + "\n\n"; if (os.toLowerCase().contains("windows")) { jepError += "Make sure Python is installed and jep.dll" + " is in your PATH "; } else { jepError += "Make sure Python is installed and " + "that the environment "; jepError += "variable LD_PRELOAD is pointing to your " + "Python shared object interpreter "; jepError += "e.g. LD_PRELOAD=/usr/lib/libpython2.6.so.1.0. "; jepError += "Your current LD_PRELOAD is :\n\n" + System.getenv("LD_PRELOAD") + "\n\n"; } jepError += "and that the Python modules " + _modules + " are installed and in your PYTHONPATH. "; jepError += "Your current PYTHONPATH is :\n\n" + PythonInfo.getPythonPathFromEnvironment(" ") + "\n"; jepError += "You can test your environment by starting " + "python and then typing :\n\nimport " + _modules; if (requireErrorMessage) { Display.getDefault().asyncExec(new Runnable() { @Override public void run() { MessageDialog.openConfirm(Display.getDefault() .getActiveShell(), "Confirm", jepError); jepLastError = System.currentTimeMillis(); } }); } else { throw new JepException(jepError); } } throw e; } } public void jepImportSpecificDefinition(String _from, String _import) throws JepException { jepImportSpecificDefinition(_from, _import, true); } /** * Import a specific definition into the current namespace, ie "from * ImageD11 import peaksearcher" and add the module path to sys.path. * * @param _jep * The jep instance. * @param _from * The module to import. * @param _import * The specific definition to import. * @throws JepException * @author SUCHET */ public void jepImportSpecificDefinition(String _from, String _import, final boolean requireMessage) throws JepException { try { Jep _jep=getJep(); _jep.eval("from " + _from + " import " + _import); writeScript("from " + _from + " import " + _import); } catch (JepException e) { if (System.currentTimeMillis() - jepLastError > 5000) { jepLastError = System.currentTimeMillis(); jepError = "Failed to import the modules " + _from + " into the" + " Java embedded Python interpreter (jep)."; jepError += "The error was :\n\n"; jepError += e.getMessage() + "\n\n"; jepError += "Make sure Python is installed and jep.dll" + " is in your PATH and that the Python modules " + _from + " are installed and in your PYTHONPATH. "; jepError += "Your current PYTHONPATH is :\n\n" + PythonInfo.getPythonPathFromEnvironment(" ") + "\n"; jepError += "You can test your environment by starting " + "python and then typing :\n\nimport " + _from; if (requireMessage) { Display.getDefault().asyncExec(new Runnable() { @Override public void run() { MessageDialog.openConfirm(Display.getDefault() .getActiveShell(), "Confirm", jepError); jepLastError = System.currentTimeMillis(); } }); } else { throw new JepException(jepError); } jepLastError = System.currentTimeMillis(); } throw e; } } public boolean eval(String str) throws JepException { logger.info(str); writeScript(str); return jep.eval(str); } public void set(String str1, String str2) throws JepException { logger.info(str1 + "= " + str2); writeScript(str1 + "= \"" + str2 + "\""); jep.set(str1, str2); } public void set(String str1, double dbl) throws JepException { logger.info(str1 + "= " + dbl); writeScript(str1 + "= " + dbl); jep.set(str1, dbl); } public void set(String str1, int str2) throws JepException { logger.info(str1 + "= \"" + str2 + "\""); writeScript(str1 + "= " + str2); jep.set(str1, str2); } public void set(String str, float f1) throws JepException { logger.info(str + "= " + f1); writeScript(str + "= " + f1); jep.set(str, f1); } /** * Return a python variable as a Java Object type using Jep. * <P> * Be careful this methods returns any of the following Java types e.g. * Float, Integer or String, depending on the Python variable. This can * cause problems at run time. * * @param str * The name of python variable e.g. "a". * @return The Java object (could be Float or Int or String). * @throws JepException */ public Object getValue(String str) throws JepException { logger.info(str); Object value = null; try { value = jep.getValue(str); } catch (JepException e) { } catch (Exception e) { /* * ignore all exceptions, this handles the case when the python * variable isn't a string */ } return value; } /** * Return the value as a Boolean using Jep. Returns false if the value is * not defined in Python. * * @param str * The name of the Python variable e.g. "a". * @return The value cast to Boolean. * @throws JepException */ public Boolean getBooleanValue(String str) { Boolean value = false; try { value = (Boolean) jep.getValue(str); } catch (JepException e) { } catch (Exception e) { /* * ignore all exceptions, this handles the case when the python * variable isn't a string */ } return value; } /** * Cast a variable in python to int and return the value as an Integer using * Jep. Returns null if the value is not defined in Python. * * @param str * The name of the Python variable e.g. "a". * @return The value cast to Integer. */ public Integer getIntegerValue(String str) { Integer value = null; try { value = (Integer) jep.getValue("int(" + str + ")"); } catch (JepException e) { } catch (Exception e) { /* * ignore all exceptions, this handles the case when the python * variable isn't a string */ } return value; } /** * Cast a variable in Python to float and return the value as a Float using * Jep. Returns null if the value is not defined in Python. * * @param str * The name of the Python variable e.g. "a". * @return The value cast to Float. * @throws JepException */ public Float getFloatValue(String str) { Float value = null; try { value = (Float) jep.getValue("float(" + str + ")"); } catch (JepException e) { } catch (Exception e) { /* * ignore all exceptions, this handles the case when the python * variable isn't a string */ } return value; } /** * Return the value as a String using Jep. Returns null if the value is not * defined in Python. * * @param str * The name of the Python variable e.g. "a". * @return The value cast to Float. * @throws JepException */ public String getStringValue(String str) { String value = null; try { value = (String) jep.getValue(str); } catch (JepException e) { } catch (Exception e) { /* * ignore all exceptions, this handles the case when the python * variable isn't a string */ } return value; } /** * Return a float array from Python using Jep. * * @param str * The name of the Python float array. * @return The float[]. * @throws JepException */ public float[] getValue_floatarray(String str) throws JepException { logger.info("getValue_floatarray"); return jep.getValue_floatarray(str); } /** * Redirect stdout in a python program to a file. * * @usage new FableJep().redirectStdout() * @throws JepException */ public void redirectStdout(String filename) throws JepException { set("filename", filename); jep.eval("logstdout=open(filename, 'w')"); // redirect sys.stdout to mystdout so we can pick up the output in // interactive mode jep.eval("sys.stdout=logstdout"); flushStdout(); } /** * Redirect stdout in a python program to a file. * * @usage new FableJep().redirectStdout() * @throws JepException */ public void redirectStderr(String filename) throws JepException { set("filename", filename); jep.eval("logstderr=open(filename, 'w')"); // redirect sys.stderr to mystdout so we can pick up the output in // interactive mode jep.eval("sys.stderr=logstderr"); flushStdout(); } /** * Flush sys.stdout * * @throws JepException */ public void flushStdout() throws JepException { jep.eval("sys.stdout.flush()"); } /** * Insert a line in script file. * * @param addline */ private void writeScript(String addline) { if (bRecord) { try { BufferedWriter out = new BufferedWriter(new FileWriter( filename, true)); out.write(addline); out.newLine(); out.close(); } catch (IOException ex) { logger.error("Error writing script", ex); } } } public static void record(boolean b) { // TODO Auto-generated method stub bRecord = b; } public void setScriptFileName(String name) { filename = name; } /** * @return the pythonpath */ public static String getPythonPath() { return pythonPath; } /** * @param pythonpath * the pythonpath to set */ public static void setPythonPath(String pythonPath) { FableJep.pythonPath = pythonPath; } /** * Replaces escape sequences in the given string with a \ plus the escape * sequence character. For example: "\t" -> "\\t" (printed as TAB -> \t). * This is needed when returning filenames from Python using Jep. * * @param input * @return */ public static String replaceEscapeSequences(String input) { if (input == null) { return null; } String output = input; output = output.replaceAll("\t", "\\\\t"); output = output.replaceAll("\b", "\\\\b"); output = output.replaceAll("\n", "\\\\n"); output = output.replaceAll("\r", "\\\\r"); output = output.replaceAll("\f", "\\\\f"); output = output.replaceAll("\'", "\\\\'"); output = output.replaceAll("\"", "\\\\\""); return output; } public boolean isRequireErrorMessage() { return requireErrorMessage; } public void setRequireErrorMessage(boolean requireErrorMessage) { this.requireErrorMessage = requireErrorMessage; } }