PageRenderTime 41ms CodeModel.GetById 7ms RepoModel.GetById 0ms app.codeStats 0ms

/src/org/python/util/jython.java

http://github.com/nriley/jython
Java | 525 lines | 435 code | 39 blank | 51 comment | 138 complexity | eced7940c78073445bab28d6ad3e25bb MD5 | raw file
  1. // Copyright (c) Corporation for National Research Initiatives
  2. package org.python.util;
  3. import java.io.File;
  4. import java.io.FileInputStream;
  5. import java.io.FileNotFoundException;
  6. import java.io.IOException;
  7. import java.io.InputStream;
  8. import java.nio.charset.Charset;
  9. import java.util.List;
  10. import java.util.Properties;
  11. import java.util.zip.ZipEntry;
  12. import java.util.zip.ZipFile;
  13. import org.python.Version;
  14. import org.python.core.CodeFlag;
  15. import org.python.core.CompileMode;
  16. import org.python.core.Options;
  17. import org.python.core.Py;
  18. import org.python.core.PyCode;
  19. import org.python.core.PyException;
  20. import org.python.core.PyFile;
  21. import org.python.core.PyList;
  22. import org.python.core.PyString;
  23. import org.python.core.PyStringMap;
  24. import org.python.core.PySystemState;
  25. import org.python.core.imp;
  26. import org.python.core.util.FileUtil;
  27. import org.python.core.util.RelativeFile;
  28. import org.python.modules._systemrestart;
  29. import org.python.modules.thread.thread;
  30. public class jython {
  31. private static final String COPYRIGHT =
  32. "Type \"help\", \"copyright\", \"credits\" or \"license\" for more information.";
  33. static final String usageHeader =
  34. "usage: jython [option] ... [-c cmd | -m mod | file | -] [arg] ...\n";
  35. private static final String usage = usageHeader +
  36. "Options and arguments:\n" + //(and corresponding environment variables):\n" +
  37. "-c cmd : program passed in as string (terminates option list)\n" +
  38. //"-d : debug output from parser (also PYTHONDEBUG=x)\n" +
  39. "-Dprop=v : Set the property `prop' to value `v'\n"+
  40. //"-E : ignore environment variables (such as PYTHONPATH)\n" +
  41. "-C codec : Use a different codec when reading from the console.\n"+
  42. "-h : print this help message and exit (also --help)\n" +
  43. "-i : inspect interactively after running script\n" + //, (also PYTHONINSPECT=x)\n" +
  44. " and force prompts, even if stdin does not appear to be a terminal\n" +
  45. "-jar jar : program read from __run__.py in jar file\n"+
  46. "-m mod : run library module as a script (terminates option list)\n" +
  47. //"-O : optimize generated bytecode (a tad; also PYTHONOPTIMIZE=x)\n" +
  48. //"-OO : remove doc-strings in addition to the -O optimizations\n" +
  49. "-Q arg : division options: -Qold (default), -Qwarn, -Qwarnall, -Qnew\n" +
  50. "-S : don't imply 'import site' on initialization\n" +
  51. //"-t : issue warnings about inconsistent tab usage (-tt: issue errors)\n" +
  52. "-u : unbuffered binary stdout and stderr\n" + // (also PYTHONUNBUFFERED=x)\n" +
  53. //" see man page for details on internal buffering relating to '-u'\n" +
  54. "-v : verbose (trace import statements)\n" + // (also PYTHONVERBOSE=x)\n" +
  55. "-V : print the Python version number and exit (also --version)\n" +
  56. "-W arg : warning control (arg is action:message:category:module:lineno)\n" +
  57. //"-x : skip first line of source, allowing use of non-Unix forms of #!cmd\n" +
  58. "file : program read from script file\n" +
  59. "- : program read from stdin (default; interactive mode if a tty)\n" +
  60. "arg ... : arguments passed to program in sys.argv[1:]\n" +
  61. "Other environment variables:\n" +
  62. "JYTHONPATH: '" + File.pathSeparator +
  63. "'-separated list of directories prefixed to the default module\n" +
  64. " search path. The result is sys.path.";
  65. public static boolean shouldRestart;
  66. public static void runJar(String filename) {
  67. // TBD: this is kind of gross because a local called `zipfile' just magically
  68. // shows up in the module's globals. Either `zipfile' should be called
  69. // `__zipfile__' or (preferrably, IMO), __run__.py should be imported and a main()
  70. // function extracted. This function should be called passing zipfile in as an
  71. // argument.
  72. //
  73. // Probably have to keep this code around for backwards compatibility (?)
  74. try {
  75. ZipFile zip = new ZipFile(filename);
  76. ZipEntry runit = zip.getEntry("__run__.py");
  77. if (runit == null) {
  78. throw Py.ValueError("jar file missing '__run__.py'");
  79. }
  80. PyStringMap locals = new PyStringMap();
  81. // Stripping the stuff before the last File.separator fixes Bug #931129 by
  82. // keeping illegal characters out of the generated proxy class name
  83. int beginIndex;
  84. if ((beginIndex = filename.lastIndexOf(File.separator)) != -1) {
  85. filename = filename.substring(beginIndex + 1);
  86. }
  87. locals.__setitem__("__name__", new PyString(filename));
  88. locals.__setitem__("zipfile", Py.java2py(zip));
  89. InputStream file = zip.getInputStream(runit);
  90. PyCode code;
  91. try {
  92. code = Py.compile(file, "__run__", CompileMode.exec);
  93. } finally {
  94. file.close();
  95. }
  96. Py.runCode(code, locals, locals);
  97. } catch (IOException e) {
  98. throw Py.IOError(e);
  99. }
  100. }
  101. public static void main(String[] args) {
  102. do {
  103. shouldRestart = false;
  104. run(args);
  105. } while (shouldRestart);
  106. }
  107. public static void run(String[] args) {
  108. // Parse the command line options
  109. CommandLineOptions opts = new CommandLineOptions();
  110. if (!opts.parse(args)) {
  111. if (opts.version) {
  112. System.err.println("Jython " + Version.PY_VERSION);
  113. System.exit(0);
  114. }
  115. if (!opts.runCommand && !opts.runModule) {
  116. System.err.println(usage);
  117. }
  118. int exitcode = opts.help ? 0 : -1;
  119. System.exit(exitcode);
  120. }
  121. // Setup the basic python system state from these options
  122. PySystemState.initialize(PySystemState.getBaseProperties(), opts.properties, opts.argv);
  123. PyList warnoptions = new PyList();
  124. for (String wopt : opts.warnoptions) {
  125. warnoptions.append(new PyString(wopt));
  126. }
  127. Py.getSystemState().setWarnoptions(warnoptions);
  128. // Decide if stdin is interactive
  129. if (!opts.fixInteractive && opts.interactive) {
  130. opts.interactive = ((PyFile)Py.defaultSystemState.stdin).isatty();
  131. if (!opts.interactive) {
  132. PySystemState systemState = Py.getSystemState();
  133. systemState.ps1 = systemState.ps2 = Py.EmptyString;
  134. }
  135. }
  136. // Now create an interpreter
  137. InteractiveConsole interp = newInterpreter(opts.interactive);
  138. // Print banner and copyright information (or not)
  139. if (opts.interactive && opts.notice && !opts.runModule) {
  140. System.err.println(InteractiveConsole.getDefaultBanner());
  141. }
  142. if (Options.importSite) {
  143. try {
  144. imp.load("site");
  145. if (opts.interactive && opts.notice && !opts.runModule) {
  146. System.err.println(COPYRIGHT);
  147. }
  148. } catch (PyException pye) {
  149. if (!pye.match(Py.ImportError)) {
  150. System.err.println("error importing site");
  151. Py.printException(pye);
  152. System.exit(-1);
  153. }
  154. }
  155. }
  156. if (opts.division != null) {
  157. if ("old".equals(opts.division)) {
  158. Options.divisionWarning = 0;
  159. } else if ("warn".equals(opts.division)) {
  160. Options.divisionWarning = 1;
  161. } else if ("warnall".equals(opts.division)) {
  162. Options.divisionWarning = 2;
  163. } else if ("new".equals(opts.division)) {
  164. Options.Qnew = true;
  165. interp.cflags.setFlag(CodeFlag.CO_FUTURE_DIVISION);
  166. }
  167. }
  168. // was there a filename on the command line?
  169. if (opts.filename != null) {
  170. String path;
  171. try {
  172. path = new File(opts.filename).getCanonicalFile().getParent();
  173. } catch (IOException ioe) {
  174. path = new File(opts.filename).getAbsoluteFile().getParent();
  175. }
  176. if (path == null) {
  177. path = "";
  178. }
  179. Py.getSystemState().path.insert(0, new PyString(path));
  180. if (opts.jar) {
  181. runJar(opts.filename);
  182. } else if (opts.filename.equals("-")) {
  183. try {
  184. interp.locals.__setitem__(new PyString("__file__"), new PyString("<stdin>"));
  185. interp.execfile(System.in, "<stdin>");
  186. } catch (Throwable t) {
  187. Py.printException(t);
  188. }
  189. } else {
  190. try {
  191. interp.locals.__setitem__(new PyString("__file__"),
  192. new PyString(opts.filename));
  193. FileInputStream file;
  194. try {
  195. file = new FileInputStream(new RelativeFile(opts.filename));
  196. } catch (FileNotFoundException e) {
  197. throw Py.IOError(e);
  198. }
  199. try {
  200. if (FileUtil.isatty(file.getFD())) {
  201. opts.interactive = true;
  202. interp.interact(null, new PyFile(file));
  203. System.exit(0);
  204. } else {
  205. interp.execfile(file, opts.filename);
  206. }
  207. } finally {
  208. file.close();
  209. }
  210. } catch (Throwable t) {
  211. if (t instanceof PyException
  212. && ((PyException)t).match(_systemrestart.SystemRestart)) {
  213. // Shutdown this instance...
  214. shouldRestart = true;
  215. shutdownInterpreter();
  216. // ..reset the state...
  217. Py.setSystemState(new PySystemState());
  218. // ...and start again
  219. } else {
  220. Py.printException(t);
  221. if (!opts.interactive) {
  222. interp.cleanup();
  223. System.exit(-1);
  224. }
  225. }
  226. }
  227. }
  228. }
  229. else {
  230. // if there was no file name on the command line, then "" is the first element
  231. // on sys.path. This is here because if there /was/ a filename on the c.l.,
  232. // and say the -i option was given, sys.path[0] will have gotten filled in
  233. // with the dir of the argument filename.
  234. Py.getSystemState().path.insert(0, Py.EmptyString);
  235. if (opts.command != null) {
  236. try {
  237. interp.exec(opts.command);
  238. } catch (Throwable t) {
  239. Py.printException(t);
  240. System.exit(1);
  241. }
  242. }
  243. if (opts.moduleName != null) {
  244. // PEP 338 - Execute module as a script
  245. try {
  246. interp.exec("import runpy");
  247. interp.set("name", Py.newString(opts.moduleName));
  248. interp.exec("runpy.run_module(name, run_name='__main__', alter_sys=True)");
  249. interp.cleanup();
  250. System.exit(0);
  251. } catch (Throwable t) {
  252. Py.printException(t);
  253. interp.cleanup();
  254. System.exit(-1);
  255. }
  256. }
  257. }
  258. if (opts.fixInteractive || (opts.filename == null && opts.command == null)) {
  259. if (opts.encoding == null) {
  260. opts.encoding = PySystemState.registry.getProperty("python.console.encoding");
  261. }
  262. if (opts.encoding != null) {
  263. if (!Charset.isSupported(opts.encoding)) {
  264. System.err.println(opts.encoding
  265. + " is not a supported encoding on this JVM, so it can't "
  266. + "be used in python.console.encoding.");
  267. System.exit(1);
  268. }
  269. interp.cflags.encoding = opts.encoding;
  270. }
  271. try {
  272. interp.interact(null, null);
  273. } catch (Throwable t) {
  274. Py.printException(t);
  275. }
  276. }
  277. interp.cleanup();
  278. if (opts.fixInteractive || opts.interactive) {
  279. System.exit(0);
  280. }
  281. }
  282. /**
  283. * Returns a new python interpreter using the InteractiveConsole subclass from the
  284. * <tt>python.console</tt> registry key.
  285. * <p>
  286. * When stdin is interactive the default is {@link JLineConsole}. Otherwise the
  287. * featureless {@link InteractiveConsole} is always used as alternative consoles cause
  288. * unexpected behavior with the std file streams.
  289. */
  290. private static InteractiveConsole newInterpreter(boolean interactiveStdin) {
  291. if (!interactiveStdin) {
  292. return new InteractiveConsole();
  293. }
  294. String interpClass = PySystemState.registry.getProperty("python.console", "");
  295. if (interpClass.length() > 0) {
  296. try {
  297. return (InteractiveConsole)Class.forName(interpClass).newInstance();
  298. } catch (Throwable t) {
  299. // fall through
  300. }
  301. }
  302. return new JLineConsole();
  303. }
  304. /**
  305. * Run any finalizations on the current interpreter in preparation for a SytemRestart.
  306. */
  307. public static void shutdownInterpreter() {
  308. // Stop all the active threads
  309. thread.interruptAllThreads();
  310. // Close all sockets -- not all of their operations are stopped by
  311. // Thread.interrupt (in particular pre-nio sockets)
  312. try {
  313. imp.load("socket").__findattr__("_closeActiveSockets").__call__();
  314. } catch (PyException pye) {
  315. // continue
  316. }
  317. }
  318. }
  319. class CommandLineOptions {
  320. public String filename;
  321. public boolean jar, interactive, notice;
  322. public boolean runCommand, runModule;
  323. public boolean fixInteractive;
  324. public boolean help, version;
  325. public String[] argv;
  326. public Properties properties;
  327. public String command;
  328. public List<String> warnoptions = Generic.list();
  329. public String encoding;
  330. public String division;
  331. public String moduleName;
  332. public CommandLineOptions() {
  333. filename = null;
  334. jar = fixInteractive = false;
  335. interactive = notice = true;
  336. runModule = false;
  337. properties = new Properties();
  338. help = version = false;
  339. }
  340. public void setProperty(String key, String value) {
  341. properties.put(key, value);
  342. try {
  343. System.setProperty(key, value);
  344. } catch (SecurityException e) {
  345. // continue
  346. }
  347. }
  348. public boolean parse(String[] args) {
  349. int index = 0;
  350. while (index < args.length && args[index].startsWith("-")) {
  351. String arg = args[index];
  352. if (arg.equals("-h") || arg.equals("-?") || arg.equals("--help")) {
  353. help = true;
  354. return false;
  355. } else if (arg.equals("-V") || arg.equals("--version")) {
  356. version = true;
  357. return false;
  358. } else if (arg.equals("-")) {
  359. if (!fixInteractive) {
  360. interactive = false;
  361. }
  362. filename = "-";
  363. } else if (arg.equals("-i")) {
  364. fixInteractive = true;
  365. interactive = true;
  366. } else if (arg.equals("-jar")) {
  367. jar = true;
  368. if (!fixInteractive) {
  369. interactive = false;
  370. }
  371. } else if (arg.equals("-u")) {
  372. Options.unbuffered = true;
  373. } else if (arg.equals("-v")) {
  374. Options.verbose++;
  375. } else if (arg.equals("-vv")) {
  376. Options.verbose += 2;
  377. } else if (arg.equals("-vvv")) {
  378. Options.verbose +=3 ;
  379. } else if (arg.equals("-S")) {
  380. Options.importSite = false;
  381. } else if (arg.equals("-c")) {
  382. runCommand = true;
  383. if (arg.length() > 2) {
  384. command = arg.substring(2);
  385. } else if ((index + 1) < args.length) {
  386. command = args[++index];
  387. } else {
  388. System.err.println("Argument expected for the -c option");
  389. System.err.print(jython.usageHeader);
  390. System.err.println("Try `jython -h' for more information.");
  391. return false;
  392. }
  393. if (!fixInteractive) {
  394. interactive = false;
  395. }
  396. index++;
  397. break;
  398. } else if (arg.equals("-W")) {
  399. warnoptions.add(args[++index]);
  400. } else if (arg.equals("-C")) {
  401. encoding = args[++index];
  402. setProperty("python.console.encoding", encoding);
  403. } else if (arg.equals("-E")) {
  404. // XXX: accept -E (ignore environment variables) to be compatiable with
  405. // CPython. do nothing for now (we could ignore the registry)
  406. } else if (arg.startsWith("-D")) {
  407. String key = null;
  408. String value = null;
  409. int equals = arg.indexOf("=");
  410. if (equals == -1) {
  411. String arg2 = args[++index];
  412. key = arg.substring(2, arg.length());
  413. value = arg2;
  414. } else {
  415. key = arg.substring(2, equals);
  416. value = arg.substring(equals + 1, arg.length());
  417. }
  418. setProperty(key, value);
  419. } else if (arg.startsWith("-Q")) {
  420. if (arg.length() > 2) {
  421. division = arg.substring(2);
  422. } else {
  423. division = args[++index];
  424. }
  425. } else if (arg.startsWith("-m")) {
  426. runModule = true;
  427. if (arg.length() > 2) {
  428. moduleName = arg.substring(2);
  429. } else if ((index + 1) < args.length) {
  430. moduleName = args[++index];
  431. } else {
  432. System.err.println("Argument expected for the -m option");
  433. System.err.print(jython.usageHeader);
  434. System.err.println("Try `jython -h' for more information.");
  435. return false;
  436. }
  437. if (!fixInteractive) {
  438. interactive = false;
  439. }
  440. index++;
  441. int n = args.length - index + 1;
  442. argv = new String[n];
  443. argv[0] = moduleName;
  444. for (int i = 1; index < args.length; i++, index++) {
  445. argv[i] = args[index];
  446. }
  447. return true;
  448. } else {
  449. String opt = args[index];
  450. if (opt.startsWith("--")) {
  451. opt = opt.substring(2);
  452. } else if (opt.startsWith("-")) {
  453. opt = opt.substring(1);
  454. }
  455. System.err.println("Unknown option: " + opt);
  456. return false;
  457. }
  458. index += 1;
  459. }
  460. notice = interactive;
  461. if (filename == null && index < args.length && command == null) {
  462. filename = args[index++];
  463. if (!fixInteractive) {
  464. interactive = false;
  465. }
  466. notice = false;
  467. }
  468. if (command != null) {
  469. notice = false;
  470. }
  471. int n = args.length - index + 1;
  472. argv = new String[n];
  473. if (filename != null) {
  474. argv[0] = filename;
  475. } else if (command != null) {
  476. argv[0] = "-c";
  477. } else {
  478. argv[0] = "";
  479. }
  480. for (int i = 1; i < n; i++, index++) {
  481. argv[i] = args[index];
  482. }
  483. return true;
  484. }
  485. }