Merge pull request #5819 from luchua-bc/java/jpython-injection

Java: CWE-094 Jython code injection
This commit is contained in:
Chris Smowton
2021-05-18 16:38:40 +01:00
committed by GitHub
19 changed files with 1213 additions and 1 deletions

View File

@@ -0,0 +1,49 @@
import org.python.util.PythonInterpreter;
public class JythonInjection extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/plain");
String code = request.getParameter("code");
PythonInterpreter interpreter = null;
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
interpreter = new PythonInterpreter();
interpreter.setOut(out);
interpreter.setErr(out);
// BAD: allow execution of arbitrary Python code
interpreter.exec(code);
out.flush();
response.getWriter().print(out.toString());
} catch(PyException ex) {
response.getWriter().println(ex.getMessage());
} finally {
if (interpreter != null) {
interpreter.close();
}
out.close();
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/plain");
String code = request.getParameter("code");
PythonInterpreter interpreter = null;
try {
interpreter = new PythonInterpreter();
// BAD: allow execution of arbitrary Python code
PyObject py = interpreter.eval(code);
response.getWriter().print(py.toString());
} catch(PyException ex) {
response.getWriter().println(ex.getMessage());
} finally {
if (interpreter != null) {
interpreter.close();
}
}
}
}

View File

@@ -0,0 +1,34 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Python has been the most widely used programming language in recent years, and Jython
(formerly known as JPython) is a popular Java implementation of Python. It allows
embedded Python scripting inside Java applications and provides an interactive interpreter
that can be used to interact with Java packages or with running Java applications. If an
expression is built using attacker-controlled data and then evaluated, it may allow the
attacker to run arbitrary code.</p>
</overview>
<recommendation>
<p>In general, including user input in Jython expression should be avoided. If user input
must be included in an expression, it should be then evaluated in a safe context that
doesn't allow arbitrary code invocation.</p>
</recommendation>
<example>
<p>The following code could execute arbitrary code in Jython Interpreter</p>
<sample src="JythonInjection.java" />
</example>
<references>
<li>
Jython Organization: <a href="https://jython.readthedocs.io/en/latest/JythonAndJavaIntegration/">Jython and Java Integration</a>
</li>
<li>
PortSwigger: <a href="https://portswigger.net/kb/issues/00100f10_python-code-injection">Python code injection</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,112 @@
/**
* @name Injection in Jython
* @description Evaluation of a user-controlled malicious expression in Java Python
* interpreter may lead to remote code execution.
* @kind path-problem
* @id java/jython-injection
* @tags security
* external/cwe/cwe-094
* external/cwe/cwe-095
*/
import java
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.frameworks.spring.SpringController
import DataFlow::PathGraph
/** The class `org.python.util.PythonInterpreter`. */
class PythonInterpreter extends RefType {
PythonInterpreter() { this.hasQualifiedName("org.python.util", "PythonInterpreter") }
}
/** A method that evaluates, compiles or executes a Jython expression. */
class InterpretExprMethod extends Method {
InterpretExprMethod() {
this.getDeclaringType().getAnAncestor*() instanceof PythonInterpreter and
getName().matches(["exec%", "run%", "eval", "compile"])
}
}
/** The class `org.python.core.BytecodeLoader`. */
class BytecodeLoader extends RefType {
BytecodeLoader() { this.hasQualifiedName("org.python.core", "BytecodeLoader") }
}
/** Holds if a Jython expression if evaluated, compiled or executed. */
predicate runsCode(MethodAccess ma, Expr sink) {
exists(Method m | m = ma.getMethod() |
m instanceof InterpretExprMethod and
sink = ma.getArgument(0)
)
}
/** A method that loads Java class data. */
class LoadClassMethod extends Method {
LoadClassMethod() {
this.getDeclaringType().getAnAncestor*() instanceof BytecodeLoader and
hasName(["makeClass", "makeCode"])
}
}
/**
* Holds if `ma` is a call to a class-loading method, and `sink` is the byte array
* representing the class to be loaded.
*/
predicate loadsClass(MethodAccess ma, Expr sink) {
exists(Method m, int i | m = ma.getMethod() |
m instanceof LoadClassMethod and
m.getParameter(i).getType() instanceof Array and // makeClass(java.lang.String name, byte[] data, ...)
sink = ma.getArgument(i)
)
}
/** The class `org.python.core.Py`. */
class Py extends RefType {
Py() { this.hasQualifiedName("org.python.core", "Py") }
}
/** A method declared on class `Py` or one of its descendants that compiles Python code. */
class PyCompileMethod extends Method {
PyCompileMethod() {
this.getDeclaringType().getAnAncestor*() instanceof Py and
getName().matches("compile%")
}
}
/** Holds if source code is compiled with `PyCompileMethod`. */
predicate compile(MethodAccess ma, Expr sink) {
exists(Method m | m = ma.getMethod() |
m instanceof PyCompileMethod and
sink = ma.getArgument(0)
)
}
/** An expression loaded by Jython. */
class CodeInjectionSink extends DataFlow::ExprNode {
MethodAccess methodAccess;
CodeInjectionSink() {
runsCode(methodAccess, this.getExpr()) or
loadsClass(methodAccess, this.getExpr()) or
compile(methodAccess, this.getExpr())
}
MethodAccess getMethodAccess() { result = methodAccess }
}
/**
* A taint configuration for tracking flow from `RemoteFlowSource` to a Jython method call
* `CodeInjectionSink` that executes injected code.
*/
class CodeInjectionConfiguration extends TaintTracking::Configuration {
CodeInjectionConfiguration() { this = "CodeInjectionConfiguration" }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) { sink instanceof CodeInjectionSink }
}
from DataFlow::PathNode source, DataFlow::PathNode sink, CodeInjectionConfiguration conf
where conf.hasFlowPath(source, sink)
select sink.getNode().(CodeInjectionSink).getMethodAccess(), source, sink, "Jython evaluate $@.",
source.getNode(), "user input"

View File

@@ -0,0 +1,21 @@
edges
| JythonInjection.java:28:23:28:50 | getParameter(...) : String | JythonInjection.java:36:30:36:33 | code |
| JythonInjection.java:53:23:53:50 | getParameter(...) : String | JythonInjection.java:58:44:58:47 | code |
| JythonInjection.java:73:23:73:50 | getParameter(...) : String | JythonInjection.java:81:35:81:38 | code |
| JythonInjection.java:97:23:97:50 | getParameter(...) : String | JythonInjection.java:106:61:106:75 | getBytes(...) |
nodes
| JythonInjection.java:28:23:28:50 | getParameter(...) : String | semmle.label | getParameter(...) : String |
| JythonInjection.java:36:30:36:33 | code | semmle.label | code |
| JythonInjection.java:53:23:53:50 | getParameter(...) : String | semmle.label | getParameter(...) : String |
| JythonInjection.java:58:44:58:47 | code | semmle.label | code |
| JythonInjection.java:73:23:73:50 | getParameter(...) : String | semmle.label | getParameter(...) : String |
| JythonInjection.java:81:35:81:38 | code | semmle.label | code |
| JythonInjection.java:97:23:97:50 | getParameter(...) : String | semmle.label | getParameter(...) : String |
| JythonInjection.java:106:61:106:75 | getBytes(...) | semmle.label | getBytes(...) |
| JythonInjection.java:131:40:131:63 | getInputStream(...) | semmle.label | getInputStream(...) |
#select
| JythonInjection.java:36:13:36:34 | exec(...) | JythonInjection.java:28:23:28:50 | getParameter(...) : String | JythonInjection.java:36:30:36:33 | code | Jython evaluate $@. | JythonInjection.java:28:23:28:50 | getParameter(...) | user input |
| JythonInjection.java:58:27:58:48 | eval(...) | JythonInjection.java:53:23:53:50 | getParameter(...) : String | JythonInjection.java:58:44:58:47 | code | Jython evaluate $@. | JythonInjection.java:53:23:53:50 | getParameter(...) | user input |
| JythonInjection.java:81:13:81:39 | runsource(...) | JythonInjection.java:73:23:73:50 | getParameter(...) : String | JythonInjection.java:81:35:81:38 | code | Jython evaluate $@. | JythonInjection.java:73:23:73:50 | getParameter(...) | user input |
| JythonInjection.java:106:29:106:134 | makeCode(...) | JythonInjection.java:97:23:97:50 | getParameter(...) : String | JythonInjection.java:106:61:106:75 | getBytes(...) | Jython evaluate $@. | JythonInjection.java:97:23:97:50 | getParameter(...) | user input |
| JythonInjection.java:131:29:131:109 | compile(...) | JythonInjection.java:131:40:131:63 | getInputStream(...) | JythonInjection.java:131:40:131:63 | getInputStream(...) | Jython evaluate $@. | JythonInjection.java:131:40:131:63 | getInputStream(...) | user input |

View File

@@ -0,0 +1,144 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.python.core.BytecodeLoader;
import org.python.core.Py;
import org.python.core.PyCode;
import org.python.core.PyException;
import org.python.core.PyObject;
import org.python.util.InteractiveInterpreter;
import org.python.util.PythonInterpreter;
public class JythonInjection extends HttpServlet {
private static final long serialVersionUID = 1L;
public JythonInjection() {
super();
}
// BAD: allow execution of arbitrary Python code
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/plain");
String code = request.getParameter("code");
PythonInterpreter interpreter = null;
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
interpreter = new PythonInterpreter();
interpreter.setOut(out);
interpreter.setErr(out);
interpreter.exec(code);
out.flush();
response.getWriter().print(out.toString());
} catch(PyException ex) {
response.getWriter().println(ex.getMessage());
} finally {
if (interpreter != null) {
interpreter.close();
}
out.close();
}
}
// BAD: allow execution of arbitrary Python code
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/plain");
String code = request.getParameter("code");
PythonInterpreter interpreter = null;
try {
interpreter = new PythonInterpreter();
PyObject py = interpreter.eval(code);
response.getWriter().print(py.toString());
} catch(PyException ex) {
response.getWriter().println(ex.getMessage());
} finally {
if (interpreter != null) {
interpreter.close();
}
}
}
// BAD: allow arbitrary Jython expression to run
protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/plain");
String code = request.getParameter("code");
InteractiveInterpreter interpreter = null;
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
interpreter = new InteractiveInterpreter();
interpreter.setOut(out);
interpreter.setErr(out);
interpreter.runsource(code);
out.flush();
response.getWriter().print(out.toString());
} catch(PyException ex) {
response.getWriter().println(ex.getMessage());
} finally {
if (interpreter != null) {
interpreter.close();
}
}
}
// BAD: load arbitrary class file to execute
protected void doTrace(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/plain");
String code = request.getParameter("code");
PythonInterpreter interpreter = null;
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
interpreter = new PythonInterpreter();
interpreter.setOut(out);
interpreter.setErr(out);
PyCode pyCode = BytecodeLoader.makeCode("test", code.getBytes(), getServletContext().getRealPath("/com/example/test.pyc"));
interpreter.exec(pyCode);
out.flush();
response.getWriter().print(out.toString());
} catch(PyException ex) {
response.getWriter().println(ex.getMessage());
} finally {
if (interpreter != null) {
interpreter.close();
}
}
}
// BAD: Compile Python code to execute
protected void doHead(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/plain");
PythonInterpreter interpreter = null;
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
interpreter = new PythonInterpreter();
interpreter.setOut(out);
interpreter.setErr(out);
PyCode pyCode = Py.compile(request.getInputStream(), "Test.py", org.python.core.CompileMode.eval);
interpreter.exec(pyCode);
out.flush();
response.getWriter().print(out.toString());
} catch(PyException ex) {
response.getWriter().println(ex.getMessage());
} finally {
if (interpreter != null) {
interpreter.close();
}
}
}
}

View File

@@ -0,0 +1 @@
experimental/Security/CWE/CWE-094/JythonInjection.ql

View File

@@ -1,2 +1,2 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../../stubs/mvel2-2.4.7:${testdir}/../../../../stubs/jsr223-api:${testdir}/../../../../stubs/apache-commons-jexl-2.1.1:${testdir}/../../../../stubs/apache-commons-jexl-3.1:${testdir}/../../../../stubs/scriptengine:${testdir}/../../../../stubs/java-ee-el:${testdir}/../../../../stubs/juel-2.2:${testdir}/../../../stubs/groovy-all-3.0.7:${testdir}/../../../../stubs/servlet-api-2.4
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../../stubs/mvel2-2.4.7:${testdir}/../../../../stubs/jsr223-api:${testdir}/../../../../stubs/apache-commons-jexl-2.1.1:${testdir}/../../../../stubs/apache-commons-jexl-3.1:${testdir}/../../../../stubs/scriptengine:${testdir}/../../../../stubs/java-ee-el:${testdir}/../../../../stubs/juel-2.2:${testdir}/../../../stubs/groovy-all-3.0.7:${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/jython-2.7.2

View File

@@ -0,0 +1,5 @@
// Autogenerated AST node
package org.python.antlr.base;
public abstract class mod {
}

View File

@@ -0,0 +1,47 @@
// Copyright (c) Corporation for National Research Initiatives
package org.python.core;
import java.util.List;
/**
* Utility class for loading compiled Python modules and Java classes defined in Python modules.
*/
public class BytecodeLoader {
/**
* Turn the Java class file data into a Java class.
*
* @param name fully-qualified binary name of the class
* @param data a class file as a byte array
* @param referents super-classes and interfaces that the new class will reference.
*/
@SuppressWarnings("unchecked")
public static Class<?> makeClass(String name, byte[] data, Class<?>... referents) {
return null;
}
/**
* Turn the Java class file data into a Java class.
*
* @param name the name of the class
* @param referents super-classes and interfaces that the new class will reference.
* @param data a class file as a byte array
*/
public static Class<?> makeClass(String name, List<Class<?>> referents, byte[] data) {
return null;
}
/**
* Turn the Java class file data for a compiled Python module into a {@code PyCode} object, by
* constructing an instance of the named class and calling the instance's
* {@link PyRunnable#getMain()}.
*
* @param name fully-qualified binary name of the class
* @param data a class file as a byte array
* @param filename to provide to the constructor of the named class
* @return the {@code PyCode} object produced by the named class' {@code getMain}
*/
public static PyCode makeCode(String name, byte[] data, String filename) {
return null;
}
}

View File

@@ -0,0 +1,11 @@
package org.python.core;
public enum CompileMode {
eval,
single,
exec;
public static CompileMode getMode(String mode) {
return null;
}
}

View File

@@ -0,0 +1,17 @@
// At some future point this will also be extended - in conjunction with
// Py#compileFlags - to add
// support for a compiler factory that user code can choose in place of the
// normal compiler.
// (Perhaps a better name might have been "CompilerOptions".)
package org.python.core;
import java.io.Serializable;
public class CompilerFlags implements Serializable {
public CompilerFlags() {
}
public CompilerFlags(int co_flags) {
}
}

View File

@@ -0,0 +1,134 @@
// Copyright (c) Corporation for National Research Initiatives
package org.python.core;
import java.io.InputStream;
import java.io.Serializable;
import org.python.antlr.base.mod;
public final class Py {
/**
Convert a given <code>PyObject</code> to an instance of a Java class.
Identical to <code>o.__tojava__(c)</code> except that it will
raise a <code>TypeError</code> if the conversion fails.
@param o the <code>PyObject</code> to convert.
@param c the class to convert it to.
**/
@SuppressWarnings("unchecked")
public static <T> T tojava(PyObject o, Class<T> c) {
return null;
}
// ??pending: was @deprecated but is actually used by proxie code.
// Can get rid of it?
public static Object tojava(PyObject o, String s) {
return null;
}
/**
* Uses the PyObjectAdapter passed to {@link PySystemState#initialize} to turn o into a PyObject.
*
* @see ClassicPyObjectAdapter - default PyObjectAdapter type
*/
public static PyObject java2py(Object o) {
return null;
}
/**
* Uses the PyObjectAdapter passed to {@link PySystemState#initialize} to turn
* <code>objects</code> into an array of PyObjects.
*
* @see ClassicPyObjectAdapter - default PyObjectAdapter type
*/
public static PyObject[] javas2pys(Object... objects) {
return null;
}
public static PyObject makeClass(String name, PyObject[] bases, PyCode code,
PyObject[] closure_cells) {
return null;
}
public static PyObject makeClass(String name, PyObject base, PyObject dict) {
return null;
}
/**
* Create a new Python class.
*
* @param name the String name of the class
* @param bases an array of PyObject base classes
* @param dict the class's namespace, containing the class body
* definition
* @return a new Python Class PyObject
*/
public static PyObject makeClass(String name, PyObject[] bases, PyObject dict) {
return null;
}
public static CompilerFlags getCompilerFlags() {
return null;
}
public static CompilerFlags getCompilerFlags(int flags, boolean dont_inherit) {
return null;
}
public static CompilerFlags getCompilerFlags(CompilerFlags flags, boolean dont_inherit) {
return null;
}
// w/o compiler-flags
public static PyCode compile(InputStream istream, String filename, CompileMode kind) {
return null;
}
/**
* Entry point for compiling modules.
*
* @param node Module node, coming from the parsing process
* @param name Internal name for the compiled code. Typically generated by
* calling {@link #getName()}.
* @param filename Source file name
* @param linenumbers True to track source line numbers on the generated
* code
* @param printResults True to call the sys.displayhook on the result of
* the code
* @param cflags Compiler flags
* @return Code object for the compiled module
*/
public static PyCode compile_flags(mod node, String name, String filename,
boolean linenumbers, boolean printResults,
CompilerFlags cflags) {
return null;
}
public static PyCode compile_flags(mod node, String filename,
CompileMode kind, CompilerFlags cflags) {
return null;
}
/**
* Compiles python source code coming from a file or another external stream
*/
public static PyCode compile_flags(InputStream istream, String filename,
CompileMode kind, CompilerFlags cflags) {
return null;
}
/**
* Compiles python source code coming from String (raw bytes) data.
*
* If the String is properly decoded (from PyUnicode) the PyCF_SOURCE_IS_UTF8 flag
* should be specified.
*/
public static PyCode compile_flags(String data, String filename,
CompileMode kind, CompilerFlags cflags) {
return null;
}
public static PyObject compile_command_flags(String string, String filename,
CompileMode kind, CompilerFlags cflags, boolean stdprompt) {
return null;
}
}

View File

@@ -0,0 +1,43 @@
// Copyright (c) Corporation for National Research Initiatives
package org.python.core;
/**
* A super class for all python code implementations.
*/
public abstract class PyCode extends PyObject
{
abstract public PyObject call(ThreadState state,
PyObject args[], String keywords[],
PyObject globals, PyObject[] defaults,
PyObject closure);
abstract public PyObject call(ThreadState state,
PyObject self, PyObject args[],
String keywords[],
PyObject globals, PyObject[] defaults,
PyObject closure);
abstract public PyObject call(ThreadState state,
PyObject globals, PyObject[] defaults,
PyObject closure);
abstract public PyObject call(ThreadState state,
PyObject arg1, PyObject globals,
PyObject[] defaults, PyObject closure);
abstract public PyObject call(ThreadState state,
PyObject arg1, PyObject arg2,
PyObject globals, PyObject[] defaults,
PyObject closure);
abstract public PyObject call(ThreadState state,
PyObject arg1, PyObject arg2, PyObject arg3,
PyObject globals, PyObject[] defaults,
PyObject closure);
abstract public PyObject call(ThreadState state,
PyObject arg1, PyObject arg2, PyObject arg3, PyObject arg4,
PyObject globals, PyObject[] defaults,
PyObject closure);
}

View File

@@ -0,0 +1,12 @@
// Copyright (c) Corporation for National Research Initiatives
package org.python.core;
import java.io.*;
/**
* A wrapper for all python exception. Note that the well-known python exceptions are <b>not</b>
* subclasses of PyException. Instead the python exception class is stored in the <code>type</code>
* field and value or class instance is stored in the <code>value</code> field.
*/
public class PyException extends RuntimeException
{
}

View File

@@ -0,0 +1,11 @@
// Copyright (c) Corporation for National Research Initiatives
package org.python.core;
import java.io.Serializable;
/**
* All objects known to the Jython runtime system are represented by an instance of the class
* {@code PyObject} or one of its subclasses.
*/
public class PyObject implements Serializable {
}

View File

@@ -0,0 +1,177 @@
// Copyright (c) Corporation for National Research Initiatives
package org.python.core;
import java.io.File;
import java.io.IOException;
import java.util.Properties;
/**
* The "sys" module.
*/
// xxx Many have lamented, this should really be a module!
// but it will require some refactoring to see this wish come true.
public class PySystemState extends PyObject {
public PySystemState() {
}
public static void classDictInit(PyObject dict) {
}
public ClassLoader getSyspathJavaLoader() {
return null;
}
// xxx fix this accessors
public PyObject __findattr_ex__(String name) {
return null;
}
public void __setattr__(String name, PyObject value) {
}
public void __delattr__(String name) {
}
public PyObject gettrace() {
return null;
}
public void settrace(PyObject tracefunc) {
}
/**
* Change the current working directory to the specified path.
*
* path is assumed to be absolute and canonical (via os.path.realpath).
*
* @param path a path String
*/
public void setCurrentWorkingDir(String path) {
}
/**
* Return a string representing the current working directory.
*
* @return a path String
*/
public String getCurrentWorkingDir() {
return null;
}
/**
* Resolve a path. Returns the full path taking the current working directory into account.
*
* @param path a path String
* @return a resolved path String
*/
public String getPath(String path) {
return null;
}
/**
* Resolve a path, returning a {@link File}, taking the current working directory into account.
*
* @param path a path <code>String</code>
* @return a resolved <code>File</code>
*/
public File getFile(String path) {
return null;
}
public ClassLoader getClassLoader() {
return null;
}
public void setClassLoader(ClassLoader classLoader) {
}
public static Properties getBaseProperties() {
return null;
}
public static synchronized void initialize() {
}
public static synchronized void initialize(Properties preProperties,
Properties postProperties) {
}
public static synchronized void initialize(Properties preProperties, Properties postProperties,
String[] argv) {
}
public static synchronized void initialize(Properties preProperties, Properties postProperties,
String[] argv, ClassLoader classLoader) {
}
/**
* Add a classpath directory to the list of places that are searched for java packages.
* <p>
* <b>Note</b>. Classes found in directory and sub-directory are not made available to jython by
* this call. It only makes the java package found in the directory available. This call is
* mostly useful if jython is embedded in an application that deals with its own class loaders.
* A servlet container is a very good example. Calling
* {@code add_classdir("<context>/WEB-INF/classes")} makes the java packages in WEB-INF classes
* available to jython import. However the actual class loading is completely handled by the
* servlet container's context classloader.
*/
public static void add_classdir(String directoryPath) {
}
/**
* Add a .jar and .zip directory to the list of places that are searched for java .jar and .zip
* files. The .jar and .zip files found will not be cached.
* <p>
* <b>Note</b>. Classes in .jar and .zip files found in the directory are not made available to
* jython by this call. See the note for add_classdir(dir) for more details.
*
* @param directoryPath The name of a directory.
*
* @see #add_classdir
*/
public static void add_extdir(String directoryPath) {
}
/**
* Add a .jar and .zip directory to the list of places that are searched for java .jar and .zip
* files.
* <p>
* <b>Note</b>. Classes in .jar and .zip files found in the directory are not made available to
* jython by this call. See the note for add_classdir(dir) for more details.
*
* @param directoryPath The name of a directory.
* @param cache Controls if the packages in the zip and jar file should be cached.
*
* @see #add_classdir
*/
public static void add_extdir(String directoryPath, boolean cache) {
}
// Not public by design. We can't rebind the displayhook if
// a reflected function is inserted in the class dict.
/**
* Exit a Python program with the given status.
*
* @param status the value to exit with
* @throws PyException {@code SystemExit} always throws this exception. When caught at top level
* the program will exit.
*/
public static void exit(PyObject status) {
}
/**
* Exit a Python program with the status 0.
*/
public static void exit() {
}
public static void exc_clear() {
}
public void cleanup() {
}
public void close() {
}
}

View File

@@ -0,0 +1,28 @@
// Copyright (c) Corporation for National Research Initiatives
package org.python.core;
// a ThreadState refers to one PySystemState; this weak ref allows for tracking all ThreadState objects
// that refer to a given PySystemState
public class ThreadState {
public PyException exception;
public ThreadState(PySystemState systemState) {
setSystemState(systemState);
}
public void setSystemState(PySystemState systemState) {
}
public PySystemState getSystemState() {
return null;
}
public boolean enterRepr(PyObject obj) {
return false;
}
public void exitRepr(PyObject obj) {
}
}

View File

@@ -0,0 +1,114 @@
// Copyright (c) Corporation for National Research Initiatives
package org.python.util;
import org.python.core.*;
/**
* This class provides the interface for compiling and running code that supports an interactive
* interpreter.
*/
// Based on CPython-1.5.2's code module
public class InteractiveInterpreter extends PythonInterpreter {
/**
* Construct an InteractiveInterpreter with all default characteristics: default state (from
* {@link Py#getSystemState()}), and a new empty dictionary of local variables.
* */
public InteractiveInterpreter() {
}
/**
* Construct an InteractiveInterpreter with state (from {@link Py#getSystemState()}), and the
* specified dictionary of local variables.
*
* @param locals dictionary to use, or if <code>null</code>, a new empty one will be created
*/
public InteractiveInterpreter(PyObject locals) {
}
/**
* Construct an InteractiveInterpreter with, and system state the specified dictionary of local
* variables.
*
* @param locals dictionary to use, or if <code>null</code>, a new empty one will be created
* @param systemState interpreter state, or if <code>null</code> use {@link Py#getSystemState()}
*/
public InteractiveInterpreter(PyObject locals, PySystemState systemState) {
}
/**
* Compile and run some source in the interpreter, in the mode {@link CompileMode#single} which
* is used for incremental compilation at the interactive console, known as {@code <input>}.
*
* @param source Python code
* @return <code>true</code> to indicate a partial statement was entered
*/
public boolean runsource(String source) {
return false;
}
/**
* Compile and run some source in the interpreter, in the mode {@link CompileMode#single} which
* is used for incremental compilation at the interactive console.
*
* @param source Python code
* @param filename name with which to label this console input (e.g. in error messages).
* @return <code>true</code> to indicate a partial statement was entered
*/
public boolean runsource(String source, String filename) {
return false;
}
/**
* Compile and run some source in the interpreter, according to the {@link CompileMode} given.
* This method supports incremental compilation and interpretation through the return value,
* where {@code true} signifies that more input is expected in order to complete the Python
* statement. An interpreter can use this to decide whether to use {@code sys.ps1}
* ("{@code >>> }") or {@code sys.ps2} ("{@code ... }") to prompt the next line. The arguments
* are the same as the mandatory ones in the Python {@code compile()} command.
* <p>
* One the following can happen:
* <ol>
* <li>The input is incorrect; compilation raised an exception (SyntaxError or OverflowError). A
* syntax traceback will be printed by calling {@link #showexception(PyException)}. Return is
* {@code false}.</li>
*
* <li>The input is incomplete, and more input is required; compilation returned no code.
* Nothing happens. Return is {@code true}.</li>
*
* <li>The input is complete; compilation returned a code object. The code is executed by
* calling {@link #runcode(PyObject)} (which also handles run-time exceptions, except for
* SystemExit). Return is {@code false}.</li>
* </ol>
*
* @param source Python code
* @param filename name with which to label this console input (e.g. in error messages).
* @param kind of compilation required: {@link CompileMode#eval}, {@link CompileMode#exec} or
* {@link CompileMode#single}
* @return {@code true} to indicate a partial statement was provided
*/
public boolean runsource(String source, String filename, CompileMode kind) {
return false;
}
/**
* Execute a code object. When an exception occurs, {@link #showexception(PyException)} is
* called to display a stack trace, except in the case of SystemExit, which is re-raised.
* <p>
* A note about KeyboardInterrupt: this exception may occur elsewhere in this code, and may not
* always be caught. The caller should be prepared to deal with it.
**/
// Make this run in another thread somehow????
public void runcode(PyObject code) {
}
public void showexception(PyException exc) {
}
public void write(String data) {
}
public void resetbuffer() {
}
}

View File

@@ -0,0 +1,252 @@
package org.python.util;
import java.io.Closeable;
import java.io.Reader;
import java.io.StringReader;
import java.util.Properties;
import org.python.core.PyCode;
import org.python.core.PyObject;
/**
* The PythonInterpreter class is a standard wrapper for a Jython interpreter for embedding in a
* Java application.
*/
public class PythonInterpreter implements Closeable {
/**
* 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) {
}
/**
* Creates a new interpreter with an empty local namespace.
*/
public PythonInterpreter() {
}
/**
* 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 null;
}
/**
* 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) {
}
/**
* 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) {
}
/**
* 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) {
}
/**
* 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) {
}
/**
* 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) {
}
/**
* 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) {
}
public void setErr(java.io.OutputStream outStream) {
}
/**
* Evaluates a string as a Python expression and returns the result.
*/
public PyObject eval(String s) {
return null;
}
/**
* Evaluates a Python code object and returns the result.
*/
public PyObject eval(PyObject code) {
return null;
}
/**
* 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) {
}
/**
* Executes a Python code object in the local namespace.
*/
public void exec(PyObject code) {
}
/**
* Executes a file of Python source in the local namespace.
*/
public void execfile(String filename) {
}
public void execfile(java.io.InputStream s) {
}
public void execfile(java.io.InputStream s, String name) {
}
/**
* 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 null;
}
public PyCode compile(Reader reader) {
return null;
}
public PyCode compile(String script, String filename) {
return null;
}
public PyCode compile(Reader reader, String filename) {
return null;
}
/**
* 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) {
}
/**
* 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) {
}
/**
* 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 null;
}
/**
* 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) {
return null;
}
public void cleanup() {
}
public void close() {
}
}