/*- * Copyright (c) 2012 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 uk.ac.diamond.scisoft.python; import java.io.File; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; import java.util.Arrays; import java.util.HashSet; import java.util.Properties; import java.util.Set; import org.dawb.common.util.eclipse.BundleUtils; import org.python.core.PyList; import org.python.core.PyString; import org.python.core.PyStringMap; import org.python.core.PySystemState; import org.python.util.PythonInterpreter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.ac.diamond.scisoft.jython.JythonPath; /** * SCISOFT - added static method which returns a PythonInterpreter which can run scisoft scripts * This is for executing a script directly from the workflow tool when you do not want to * start a separate debug/run process to start the script. */ public class JythonInterpreterUtils { // Boolean to set to true if running jython scripts that utilise ScisoftPy in IDE public static final String RUN_IN_ECLIPSE = "run.in.eclipse"; private static final String SCISOFTPY = "uk.ac.diamond.scisoft.python"; private static Logger logger = LoggerFactory.getLogger(JythonInterpreterUtils.class); /** * Create Jython interpreter * * @return a new PythonInterpreter. * @throws IOException * @throws ClassNotFoundException */ public static PythonInterpreter getBasicInterpreter() throws Exception { return getBasicInterpreter(null); } public static PythonInterpreter getBasicInterpreter(Set<String> extraPaths) throws Exception { return getBasicInterpreter(extraPaths, JythonInterpreterUtils.class.getClassLoader(), Boolean.getBoolean(RUN_IN_ECLIPSE)); } /** * Create Jython interpreter with extra java libraries * * @param extraPaths set of paths to extra libraries to load * @return a new Jython Interpreter * @throws Exception from getJythonInterpreterDirectory in case of missing JYTHON_BUNDLE_LOC when no Jython bundle found */ public static PythonInterpreter getBasicInterpreter(Set<String> extraPaths, ClassLoader classLoader, boolean isRunningInEclipse) throws Exception { final long start = System.currentTimeMillis(); Properties preProperties = System.getProperties(); Properties postProperties = new Properties(); //Set some useful parameters for the jython environment File jyRoot = JythonPath.getInterpreterDirectory(isRunningInEclipse); postProperties.setProperty("python.home", jyRoot.getAbsolutePath()); postProperties.setProperty("python.executable", new File(jyRoot, JythonPath.getJythonExecutableName()).getAbsolutePath()); //Set the cache for java classes loaded by Jython String cacheDir = System.getProperty("python.cachedir"); if (cacheDir == null) { cacheDir = "/scratch/.jython_consumer_cachedir"; postProperties.setProperty("python.cachedir", cacheDir); } File cacheDirPath = new File(cacheDir); if (!cacheDirPath.exists()) { try { logger.debug("Creating jython cachedir", cacheDir); cacheDirPath.mkdirs(); } catch (Exception e){ logger.warn("Could not create python.cachedir. Resetting to cachedir"); postProperties.setProperty("python.cachedir", "cachedir"); } } //Set up the path environmental variable & send it to properties StringBuilder allPaths = new StringBuilder(); allPaths.append(new File(jyRoot, "jython.jar").getAbsolutePath()+File.pathSeparatorChar); File jyLib = new File(jyRoot, "Lib"); allPaths.append(jyLib.getAbsolutePath()+File.pathSeparatorChar); allPaths.append(new File(jyLib, "distutils").getAbsolutePath()+File.pathSeparatorChar); allPaths.append(new File(jyLib, "site-packages").getAbsolutePath()+File.pathSeparatorChar); //If there's anything else to add to the path, add it. if (extraPaths != null) { for (String path : extraPaths) { allPaths.append(path); allPaths.append(File.pathSeparatorChar); } } String pythonPath = allPaths.toString(); postProperties.setProperty("python.path", pythonPath); logger.debug("Starting new Jython Interpreter."); PythonInterpreter.initialize(preProperties, postProperties, null); //Create object to give access to python system PySystemState state = new PySystemState(); // prevent problem in warnings.py where it expects len(sys.argv) > 0 state.argv = new PyList(); state.argv.add(new PyString(postProperties.getProperty("python.executable"))); //This adds an external classloader & reports classpath if (classLoader!=null) state.setClassLoader(classLoader); logger.info("Class loader is {}", classLoader); if (classLoader instanceof URLClassLoader) { logger.debug("URL classpath:"); for (URL u : ((URLClassLoader) classLoader).getURLs()) { logger.debug("\t{}", u.getPath()); } } logger.debug("Classpath:"); for (String p : System.getProperty("java.class.path").split(File.pathSeparator)) { logger.debug("\t{}", p); } //All set? Create the interpreter! PythonInterpreter interpreter = new PythonInterpreter(new PyStringMap(), state); final long end = System.currentTimeMillis(); logger.debug("Created new Jython Interpreter in {}ms.", end-start); return interpreter; } /** * scisoftpy is imported as dnp * @return a new Jython Interpreter, with only scisoftpy in sys.path * @throws Exception from getJythonInterpreterDirectory in case of missing JYTHON_BUNDLE_LOC when no Jython bundle found */ public static PythonInterpreter getScisoftpyInterpreter() throws Exception{ final Set<String> extraPaths = new HashSet<String>(); try { //This seems to work in git repo case (and presumably in binary) //Old code in 0f667dd and before (now deleted) was more verbose File pythonPlugin = BundleUtils.getBundleLocation(SCISOFTPY); logger.debug("Found Scisoft Python (Jython) plugin: {}", pythonPlugin); File binDir = new File(pythonPlugin, "bin"); if (binDir.exists()) { logger.debug("Found bin directory at {}", binDir); extraPaths.add(binDir.getAbsolutePath()); } else { extraPaths.add(pythonPlugin.getAbsolutePath()); } } catch (Exception e) { logger.error("Errors encountered getting paths to Scisoft Python (Jython) plugin", e); } PythonInterpreter interpreter = getBasicInterpreter(extraPaths); interpreter.exec("import sys"); interpreter.exec("for p in sys.path: print '\t%s' % p"); interpreter.exec("import scisoftpy as dnp"); return interpreter; } /** * Provide an interpreter with same plugin/lib paths as the one in DAWN, * with possibility to add extra paths * * @param classLoader * @param extras * @return PythonInterpreter * @throws Exception */ public static PythonInterpreter getFullInterpreter(ClassLoader classLoader, String... extras) throws Exception { //Where we are searching for additional jars/plugins (affected by whether running in eclipse) boolean isRunningInEclipse = "true".equalsIgnoreCase(System.getProperty(RUN_IN_ECLIPSE)); File pluginsDir = JythonPath.getPluginsDirectory(isRunningInEclipse); if (pluginsDir == null) { logger.error("Failed to find the plugins directory! Cannot start jython interpreter."); return null; } logger.debug("Plugins directory set to: {}", pluginsDir); //Instantiate the jyPaths HashSet and get its contents Set<String> jyPaths = JythonPath.assembleJyPaths(pluginsDir, Arrays.asList(extras), isRunningInEclipse); //If we've got everything in the extraPaths list, send it to the interpreter maker PythonInterpreter interpreter = getBasicInterpreter(jyPaths, classLoader, isRunningInEclipse); return interpreter; } /** * scisoftpy is imported as dnp * * @return a new PythonInterpreter with scisoft scripts loaded. * @throws IOException * @throws ClassNotFoundException */ public static PythonInterpreter getInterpreter() throws Exception { final long start = System.currentTimeMillis(); PythonInterpreter interpreter = getScisoftpyInterpreter(); final long end = System.currentTimeMillis(); logger.debug("Created new Jython Interpreter in {}ms.", end-start); return interpreter; } }