1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.monkeyrunner; 17 18 import com.google.common.base.Predicate; 19 import com.google.common.collect.ImmutableMap; 20 import com.google.common.collect.ImmutableMap.Builder; 21 import com.google.common.collect.Lists; 22 23 import org.python.core.Py; 24 import org.python.core.PyException; 25 import org.python.core.PyJavaPackage; 26 import org.python.core.PyList; 27 import org.python.core.PyObject; 28 import org.python.core.PyString; 29 import org.python.core.PySystemState; 30 import org.python.util.InteractiveConsole; 31 import org.python.util.JLineConsole; 32 import org.python.util.PythonInterpreter; 33 34 import java.io.File; 35 import java.util.Arrays; 36 import java.util.Collection; 37 import java.util.Collections; 38 import java.util.List; 39 import java.util.Map; 40 import java.util.Properties; 41 import java.util.logging.Level; 42 import java.util.logging.Logger; 43 44 45 /** 46 * Runs Jython based scripts. 47 */ 48 public class ScriptRunner { 49 private static final Logger LOG = Logger.getLogger(MonkeyRunnerOptions.class.getName()); 50 51 /** The "this" scope object for scripts. */ 52 private final Object scope; 53 private final String variable; 54 55 /** Private constructor. */ 56 private ScriptRunner(Object scope, String variable) { 57 this.scope = scope; 58 this.variable = variable; 59 } 60 61 /** Creates a new instance for the given scope object. */ 62 public static ScriptRunner newInstance(Object scope, String variable) { 63 return new ScriptRunner(scope, variable); 64 } 65 66 /** 67 * Runs the specified Jython script. First runs the initialization script to 68 * preload the appropriate client library version. 69 * 70 * @param scriptfilename the name of the file to run. 71 * @param args the arguments passed in (excluding the filename). 72 * @param plugins a list of plugins to load. 73 * @return the error code from running the script. 74 */ 75 public static int run(String executablePath, String scriptfilename, 76 Collection<String> args, Map<String, 77 Predicate<PythonInterpreter>> plugins) { 78 // Add the current directory of the script to the python.path search path. 79 File f = new File(scriptfilename); 80 81 // Adjust the classpath so jython can access the classes in the specified classpath. 82 Collection<String> classpath = Lists.newArrayList(f.getParent()); 83 classpath.addAll(plugins.keySet()); 84 85 String[] argv = new String[args.size() + 1]; 86 argv[0] = f.getAbsolutePath(); 87 int x = 1; 88 for (String arg : args) { 89 argv[x++] = arg; 90 } 91 92 initPython(executablePath, classpath, argv); 93 94 PythonInterpreter python = new PythonInterpreter(); 95 96 // Now let the mains run. 97 for (Map.Entry<String, Predicate<PythonInterpreter>> entry : plugins.entrySet()) { 98 boolean success; 99 try { 100 success = entry.getValue().apply(python); 101 } catch (Exception e) { 102 LOG.log(Level.SEVERE, "Plugin Main through an exception.", e); 103 continue; 104 } 105 if (!success) { 106 LOG.severe("Plugin Main returned error for: " + entry.getKey()); 107 } 108 } 109 110 // Bind __name__ to __main__ so mains will run 111 python.set("__name__", "__main__"); 112 // Also find __file__ 113 python.set("__file__", scriptfilename); 114 115 try { 116 python.execfile(scriptfilename); 117 } catch (PyException e) { 118 if (Py.SystemExit.equals(e.type)) { 119 // Then recover the error code so we can pass it on 120 return (Integer) e.value.__tojava__(Integer.class); 121 } 122 // Then some other kind of exception was thrown. Log it and return error; 123 LOG.log(Level.SEVERE, "Script terminated due to an exception", e); 124 return 1; 125 } 126 return 0; 127 } 128 129 public static void runString(String executablePath, String script) { 130 initPython(executablePath); 131 PythonInterpreter python = new PythonInterpreter(); 132 python.exec(script); 133 } 134 135 public static Map<String, PyObject> runStringAndGet(String executablePath, 136 String script, String... names) { 137 return runStringAndGet(executablePath, script, Arrays.asList(names)); 138 } 139 140 public static Map<String, PyObject> runStringAndGet(String executablePath, 141 String script, Collection<String> names) { 142 initPython(executablePath); 143 final PythonInterpreter python = new PythonInterpreter(); 144 python.exec(script); 145 146 Builder<String, PyObject> builder = ImmutableMap.builder(); 147 for (String name : names) { 148 builder.put(name, python.get(name)); 149 } 150 return builder.build(); 151 } 152 153 private static void initPython(String executablePath) { 154 List<String> arg = Collections.emptyList(); 155 initPython(executablePath, arg, new String[] {""}); 156 } 157 158 private static void initPython(String executablePath, 159 Collection<String> pythonPath, String[] argv) { 160 Properties props = new Properties(); 161 162 // Build up the python.path 163 StringBuilder sb = new StringBuilder(); 164 sb.append(System.getProperty("java.class.path")); 165 for (String p : pythonPath) { 166 sb.append(":").append(p); 167 } 168 props.setProperty("python.path", sb.toString()); 169 170 /** Initialize the python interpreter. */ 171 // Default is 'message' which displays sys-package-mgr bloat 172 // Choose one of error,warning,message,comment,debug 173 props.setProperty("python.verbose", "error"); 174 175 // This needs to be set for sys.executable to function properly 176 props.setProperty("python.executable", executablePath); 177 178 PythonInterpreter.initialize(System.getProperties(), props, argv); 179 180 String frameworkDir = System.getProperty("java.ext.dirs"); 181 File monkeyRunnerJar = new File(frameworkDir, "monkeyrunner.jar"); 182 if (monkeyRunnerJar.canRead()) { 183 PySystemState.packageManager.addJar(monkeyRunnerJar.getAbsolutePath(), false); 184 } 185 } 186 187 /** 188 * Start an interactive python interpreter. 189 */ 190 public static void console(String executablePath) { 191 initPython(executablePath); 192 InteractiveConsole python = new JLineConsole(); 193 python.interact(); 194 } 195 } 196