-
Notifications
You must be signed in to change notification settings - Fork 226
Expand file tree
/
Copy pathPythonInterpreter.java
More file actions
420 lines (371 loc) · 13.5 KB
/
PythonInterpreter.java
File metadata and controls
420 lines (371 loc) · 13.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
package org.python.util;
import java.io.Closeable;
import java.io.Reader;
import java.io.StringReader;
import java.util.Properties;
import org.python.antlr.base.mod;
import org.python.core.CodeFlag;
import org.python.core.CompileMode;
import org.python.core.CompilerFlags;
import org.python.core.Options;
import org.python.core.ParserFacade;
import org.python.core.Py;
import org.python.core.PyCode;
import org.python.core.PyException;
import org.python.core.PyFile;
import org.python.core.PyFileReader;
import org.python.core.PyFileWriter;
import org.python.core.PyModule;
import org.python.core.PyObject;
import org.python.core.PyString;
import org.python.core.PySystemState;
import org.python.core.__builtin__;
/**
* The PythonInterpreter class is a standard wrapper for a Jython interpreter for embedding in a
* Java application.
*/
public class PythonInterpreter implements AutoCloseable, Closeable {
// Defaults if the interpreter uses thread-local state
protected PySystemState systemState;
PyObject globals;
protected final boolean useThreadLocalState;
protected static ThreadLocal<Object[]> threadLocals = new ThreadLocal<Object[]>() {
@Override
protected Object[] initialValue() {
return new Object[1];
}
};
protected CompilerFlags cflags = new CompilerFlags();
private volatile boolean closed = false;
/**
* Initializes the Jython runtime. This should only be called once, before any other Python
* objects (including PythonInterpreter) are created.
*
* @param preProperties A set of properties. Typically System.getProperties() is used.
* preProperties override properties from the registry file.
* @param postProperties Another set of properties. Values like python.home, python.path and all
* other values from the registry files can be added to this property set.
* postProperties override system properties and registry properties.
* @param argv Command line arguments, assigned to sys.argv.
*/
public static void
initialize(Properties preProperties, Properties postProperties, String[] argv) {
PySystemState.initialize(preProperties, postProperties, argv);
}
/**
* Creates a new interpreter with an empty local namespace.
*/
public PythonInterpreter() {
this(null, null);
}
/**
* Creates a new interpreter with the ability to maintain a separate local namespace for each
* thread (set by invoking setLocals()).
*
* @param dict a Python mapping object (e.g., a dictionary) for use as the default namespace
*/
public static PythonInterpreter threadLocalStateInterpreter(PyObject dict) {
return new PythonInterpreter(dict, new PySystemState(), true);
}
/**
* Creates a new interpreter with a specified local namespace.
*
* @param dict a Python mapping object (e.g., a dictionary) for use as the namespace
*/
public PythonInterpreter(PyObject dict) {
this(dict, null);
}
public PythonInterpreter(PyObject dict, PySystemState systemState) {
this(dict, systemState, false);
}
protected PythonInterpreter(PyObject dict, PySystemState systemState,
boolean useThreadLocalState) {
globals = dict != null ? dict : Py.newStringMap();
this.systemState = systemState != null ? systemState : Py.getSystemState();
setSystemState();
this.useThreadLocalState = useThreadLocalState;
PyModule module = new PyModule("__main__", globals);
this.systemState.modules.__setitem__("__main__", module);
if (Options.Qnew) {
cflags.setFlag(CodeFlag.CO_FUTURE_DIVISION);
}
Py.importSiteIfSelected();
}
public PySystemState getSystemState() {
return systemState;
}
protected void setSystemState() {
Py.setSystemState(getSystemState());
}
/**
* Sets a Python object to use for the standard input stream, <code>sys.stdin</code>. This
* stream is used in a byte-oriented way, through calls to <code>read</code> and
* <code>readline</code> on the object.
*
* @param inStream a Python file-like object to use as the input stream
*/
public void setIn(PyObject inStream) {
getSystemState().stdin = inStream;
}
/**
* Sets a {@link Reader} to use for the standard input stream, <code>sys.stdin</code>. This
* stream is wrapped such that characters will be narrowed to bytes. A character greater than
* <code>U+00FF</code> will raise a Java <code>IllegalArgumentException</code> from within
* {@link PyString}.
*
* @param inStream to use as the input stream
*/
public void setIn(java.io.Reader inStream) {
setIn(new PyFileReader(inStream));
}
/**
* Sets a {@link java.io.InputStream} to use for the standard input stream.
*
* @param inStream InputStream to use as input stream
*/
public void setIn(java.io.InputStream inStream) {
setIn(new PyFile(inStream));
}
/**
* Sets a Python object to use for the standard output stream, <code>sys.stdout</code>. This
* stream is used in a byte-oriented way (mostly) that depends on the type of file-like object.
* The behaviour as implemented is:
* <table border=1>
* <caption>Stream behaviour for various object types</caption>
* <tr align=center>
* <td></td>
* <td colspan=3>Python type of object <code>o</code> written</td>
* </tr>
* <tr align=left>
* <th></th>
* <th><code>str/bytes</code></th>
* <th><code>unicode</code></th>
* <th>Any other type</th>
* </tr>
* <tr align=left>
* <th>{@link PyFile}</th>
* <td>as bytes directly</td>
* <td>respect {@link PyFile#encoding}</td>
* <td>call <code>str(o)</code> first</td>
* </tr>
* <tr align=left>
* <th>{@link PyFileWriter}</th>
* <td>each byte value as a <code>char</code></td>
* <td>write as Java <code>char</code>s</td>
* <td>call <code>o.toString()</code> first</td>
* </tr>
* <tr align=left>
* <th>Other {@link PyObject} <code>f</code></th>
* <td>invoke <code>f.write(str(o))</code></td>
* <td>invoke <code>f.write(o)</code></td>
* <td>invoke <code>f.write(str(o))</code></td>
* </tr>
* </table>
*
* @param outStream Python file-like object to use as the output stream
*/
public void setOut(PyObject outStream) {
getSystemState().stdout = outStream;
}
/**
* Sets a {@link java.io.Writer} to use for the standard output stream, <code>sys.stdout</code>.
* The behaviour as implemented is to output each object <code>o</code> by calling
* <code>o.toString()</code> and writing this as UTF-16.
*
* @param outStream to use as the output stream
*/
public void setOut(java.io.Writer outStream) {
setOut(new PyFileWriter(outStream));
}
/**
* Sets a {@link java.io.OutputStream} to use for the standard output stream.
*
* @param outStream OutputStream to use as output stream
*/
public void setOut(java.io.OutputStream outStream) {
setOut(new PyFile(outStream));
}
/**
* Sets a Python object to use for the standard output stream, <code>sys.stderr</code>. This
* stream is used in a byte-oriented way (mostly) that depends on the type of file-like object,
* in the same way as {@link #setOut(PyObject)}.
*
* @param outStream Python file-like object to use as the error output stream
*/
public void setErr(PyObject outStream) {
getSystemState().stderr = outStream;
}
/**
* Sets a {@link java.io.Writer} to use for the standard output stream, <code>sys.stdout</code>.
* The behaviour as implemented is to output each object <code>o</code> by calling
* <code>o.toString()</code> and writing this as UTF-16.
*
* @param outStream to use as the error output stream
*/
public void setErr(java.io.Writer outStream) {
setErr(new PyFileWriter(outStream));
}
public void setErr(java.io.OutputStream outStream) {
setErr(new PyFile(outStream));
}
/**
* Evaluates a string as a Python expression and returns the result.
*/
public PyObject eval(String s) {
setSystemState();
return __builtin__.eval(new PyString(s), getLocals());
}
/**
* Evaluates a Python code object and returns the result.
*/
public PyObject eval(PyObject code) {
setSystemState();
return __builtin__.eval(code, getLocals());
}
/**
* Executes a string of Python source in the local namespace.
*
* In some environments, such as Windows, Unicode characters in the script will be converted
* into ascii question marks (?). This can be avoided by first compiling the fragment using
* PythonInterpreter.compile(), and using the overridden form of this method which takes a
* PyCode object. Code page declarations are not supported.
*/
public void exec(String s) {
setSystemState();
Py.exec(Py.compile_flags(s, "<string>", CompileMode.exec, cflags), getLocals(), null);
Py.flushLine();
}
/**
* Executes a Python code object in the local namespace.
*/
public void exec(PyObject code) {
setSystemState();
Py.exec(code, getLocals(), null);
Py.flushLine();
}
/**
* Executes a file of Python source in the local namespace.
*/
public void execfile(String filename) {
PyObject locals = getLocals();
setSystemState();
__builtin__.execfile_flags(filename, locals, locals, cflags);
Py.flushLine();
}
public void execfile(java.io.InputStream s) {
execfile(s, "<iostream>");
}
public void execfile(java.io.InputStream s, String name) {
setSystemState();
Py.runCode(Py.compile_flags(s, name, CompileMode.exec, cflags), null, getLocals());
Py.flushLine();
}
/**
* Compiles a string of Python source as either an expression (if possible) or a module.
*
* Designed for use by a JSR 223 implementation: "the Scripting API does not distinguish between
* scripts which return values and those which do not, nor do they make the corresponding
* distinction between evaluating or executing objects." (SCR.4.2.1)
*/
public PyCode compile(String script) {
return compile(script, "<script>");
}
public PyCode compile(Reader reader) {
return compile(reader, "<script>");
}
public PyCode compile(String script, String filename) {
return compile(new StringReader(script), filename);
}
public PyCode compile(Reader reader, String filename) {
mod node = ParserFacade.parseExpressionOrModule(reader, filename, cflags);
setSystemState();
return Py.compile_flags(node, filename, CompileMode.eval, cflags);
}
public PyObject getLocals() {
if (!useThreadLocalState) {
return globals;
} else {
PyObject locals = (PyObject)threadLocals.get()[0];
if (locals != null) {
return locals;
}
return globals;
}
}
public void setLocals(PyObject d) {
if (!useThreadLocalState) {
globals = d;
} else {
threadLocals.get()[0] = d;
}
}
/**
* Sets a variable in the local namespace.
*
* @param name the name of the variable
* @param value the object to set the variable to (as converted to an appropriate Python object)
*/
public void set(String name, Object value) {
getLocals().__setitem__(name.intern(), Py.java2py(value));
}
/**
* Sets a variable in the local namespace.
*
* @param name the name of the variable
* @param value the Python object to set the variable to
*/
public void set(String name, PyObject value) {
getLocals().__setitem__(name.intern(), value);
}
/**
* Returns the value of a variable in the local namespace.
*
* @param name the name of the variable
* @return the value of the variable, or null if that name isn't assigned
*/
public PyObject get(String name) {
return getLocals().__finditem__(name.intern());
}
/**
* Returns the value of a variable in the local namespace.
*
* The value will be returned as an instance of the given Java class.
* <code>interp.get("foo", Object.class)</code> will return the most appropriate generic Java
* object.
*
* @param name the name of the variable
* @param javaclass the class of object to return
* @return the value of the variable as the given class, or null if that name isn't assigned
*/
public <T> T get(String name, Class<T> javaclass) {
PyObject val = getLocals().__finditem__(name.intern());
if (val == null) {
return null;
}
return Py.tojava(val, javaclass);
}
public void cleanup() {
setSystemState();
PySystemState sys = Py.getSystemState();
sys.callExitFunc();
try {
sys.stdout.invoke("flush");
} catch (PyException pye) {
// fall through
}
try {
sys.stderr.invoke("flush");
} catch (PyException pye) {
// fall through
}
threadLocals.remove();
sys.cleanup();
}
@Override
public void close() {
if (!closed) {
closed = true;
cleanup();
}
}
}