diff --git a/src/org/python/jsr223/PyScriptEngine.java b/src/org/python/jsr223/PyScriptEngine.java index 1909321..cbc8498 100644 --- a/src/org/python/jsr223/PyScriptEngine.java +++ b/src/org/python/jsr223/PyScriptEngine.java @@ -21,12 +21,10 @@ public class PyScriptEngine extends AbstractScriptEngine implements Compilable, private final PythonInterpreter interp; private final ScriptEngineFactory factory; - private final PyModule module; PyScriptEngine(ScriptEngineFactory factory) { this.factory = factory; - interp = new PythonInterpreter(new PyScriptEngineScope(this, context)); - module = (PyModule)Py.getSystemState().modules.__finditem__("__main__"); + interp = PythonInterpreter.threadLocalStateInterpreter(new PyScriptEngineScope(this, context)); } public Object eval(String script, ScriptContext context) throws ScriptException { @@ -120,7 +118,8 @@ public class PyScriptEngine extends AbstractScriptEngine implements Compilable, } public T getInterface(Class clazz) { - return getInterface(module, clazz); + // XXX will this work? will it even have been created? + return getInterface(new PyModule("__main__", interp.getLocals()), clazz); } public T getInterface(Object obj, Class clazz) { diff --git a/src/org/python/jsr223/PyScriptEngineScope.java b/src/org/python/jsr223/PyScriptEngineScope.java index 9362797..8be8935 100644 --- a/src/org/python/jsr223/PyScriptEngineScope.java +++ b/src/org/python/jsr223/PyScriptEngineScope.java @@ -38,15 +38,13 @@ public final class PyScriptEngineScope extends PyObject { @ExposedMethod public PyObject scope_keys() { PyList members = new PyList(); - synchronized (context) { - List scopes = context.getScopes(); - for (int scope : scopes) { - Bindings bindings = context.getBindings(scope); - if (bindings == null) - continue; - for (String key : bindings.keySet()) - members.append(new PyString(key)); - } + List scopes = context.getScopes(); + for (int scope : scopes) { + Bindings bindings = context.getBindings(scope); + if (bindings == null) + continue; + for (String key : bindings.keySet()) + members.append(new PyString(key)); } members.sort(); return members; @@ -63,12 +61,10 @@ public final class PyScriptEngineScope extends PyObject { } public PyObject __finditem__(String key) { - synchronized (context) { - int scope = context.getAttributesScope(key); - if (scope == -1) - return null; - return Py.java2py(context.getAttribute(key, scope)); - } + int scope = context.getAttributesScope(key); + if (scope == -1) + return null; + return Py.java2py(context.getAttribute(key, scope)); } @ExposedMethod @@ -77,12 +73,10 @@ public final class PyScriptEngineScope extends PyObject { } public void __setitem__(String key, PyObject value) { - synchronized (context) { - int scope = context.getAttributesScope(key); - if (scope == -1) - scope = ScriptContext.ENGINE_SCOPE; - context.setAttribute(key, value.__tojava__(Object.class), scope); - } + int scope = context.getAttributesScope(key); + if (scope == -1) + scope = ScriptContext.ENGINE_SCOPE; + context.setAttribute(key, value.__tojava__(Object.class), scope); } @ExposedMethod @@ -91,11 +85,9 @@ public final class PyScriptEngineScope extends PyObject { } public void __delitem__(String key) { - synchronized (context) { - int scope = context.getAttributesScope(key); - if (scope == -1) - throw Py.KeyError(key); - context.removeAttribute(key, scope); - } + int scope = context.getAttributesScope(key); + if (scope == -1) + throw Py.KeyError(key); + context.removeAttribute(key, scope); } } diff --git a/src/org/python/util/PythonInterpreter.java b/src/org/python/util/PythonInterpreter.java index 985ae8f..cd8246d 100644 --- a/src/org/python/util/PythonInterpreter.java +++ b/src/org/python/util/PythonInterpreter.java @@ -20,6 +20,7 @@ import org.python.core.PyStringMap; import org.python.core.PySystemState; import org.python.core.__builtin__; import org.python.core.PyFileReader; +import org.python.core.ThreadState; /** * The PythonInterpreter class is a standard wrapper for a Jython interpreter for use embedding in a @@ -27,40 +28,53 @@ import org.python.core.PyFileReader; */ public class PythonInterpreter { + // These variables are null if the interpreter uses thread-local state. PyModule module; - protected PySystemState systemState; - PyObject locals; + PyObject globals; + + // ... and these are used instead. + // (note we have a PySystemState per thread, but use the existing ThreadState) + // XXX is "module" needed? if so, why? + protected ThreadLocal threadSystemState; + protected ThreadLocal threadLocals; protected CompilerFlags cflags = new CompilerFlags(); /** - * Initializes the jython runtime. This should only be called once, and should be called before - * any other python objects are created (including a PythonInterpreter). + * Initializes the Jython runtime. This should only be called once, and should be called before + * any other Python objects are created (including a PythonInterpreter). * * @param preProperties * A set of properties. Typically System.getProperties() is used. * PreProperties override properties from the registry file. * @param postProperties * An other 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 + * values from the registry files can be added to this property set. postProperties * will override system properties and registry properties. * @param argv - * Command line argument. These values will assigned to sys.argv. + * Command line argument. These values will be assigned to sys.argv. */ public static void initialize(Properties preProperties, Properties postProperties, String[] argv) { PySystemState.initialize(preProperties, postProperties, argv); } /** - * Create a new Interpreter with an empty dictionary + * Create a new interpreter with an empty local dictionary */ public PythonInterpreter() { this(null, null); } /** + * Create a new interpreter with per-thread, rather than shared, locals + */ + public static PythonInterpreter threadLocalStateInterpreter(PyObject dict) { + return new PythonInterpreter(dict, null, true); + } + + /** * Create a new interpreter with the given dictionary to use as its namespace */ public PythonInterpreter(PyObject dict) { @@ -68,24 +82,42 @@ public class PythonInterpreter { } public PythonInterpreter(PyObject dict, PySystemState systemState) { + this(dict, systemState, false); + } + + protected PythonInterpreter(PyObject dict, PySystemState systemState, boolean useThreadLocalState) { if (dict == null) { dict = new PyStringMap(); } - if (systemState == null) { - systemState = Py.getSystemState(); - if (systemState == null) { - systemState = new PySystemState(); - } + globals = dict; + + if (useThreadLocalState) { + threadSystemState = new ThreadLocal(); + threadLocals = new ThreadLocal(); + return; } + + if (systemState == null) + systemState = Py.getSystemState(); this.systemState = systemState; - setState(); + setSystemState(); module = new PyModule("__main__", dict); systemState.modules.__setitem__("__main__", module); - locals = dict; } - protected void setState() { - Py.setSystemState(systemState); + public PySystemState getSystemState() { + if (systemState != null) + return systemState; + PySystemState systemState = threadSystemState.get(); + if (systemState != null) + return systemState; + systemState = new PySystemState(); + threadSystemState.set(systemState); + return systemState; + } + + protected void setSystemState() { + Py.setSystemState(getSystemState()); } /** @@ -95,7 +127,7 @@ public class PythonInterpreter { * Python file-like object to use as input stream */ public void setIn(PyObject inStream) { - systemState.stdin = inStream; + getSystemState().stdin = inStream; } public void setIn(java.io.Reader inStream) { @@ -119,7 +151,7 @@ public class PythonInterpreter { * Python file-like object to use as output stream */ public void setOut(PyObject outStream) { - systemState.stdout = outStream; + getSystemState().stdout = outStream; } public void setOut(java.io.Writer outStream) { @@ -137,7 +169,7 @@ public class PythonInterpreter { } public void setErr(PyObject outStream) { - systemState.stderr = outStream; + getSystemState().stderr = outStream; } public void setErr(java.io.Writer outStream) { @@ -152,24 +184,24 @@ public class PythonInterpreter { * Evaluate a string as Python source and return the result */ public PyObject eval(String s) { - setState(); - return __builtin__.eval(new PyString(s), locals); + setSystemState(); + return __builtin__.eval(new PyString(s), globals, getLocals()); } /** * Evaluate a Python code object and return the result */ public PyObject eval(PyObject code) { - setState(); - return __builtin__.eval(code, locals, locals); + setSystemState(); + return __builtin__.eval(code, globals, getLocals()); } /** * Execute a string of Python source in the local namespace */ public void exec(String s) { - setState(); - Py.exec(Py.compile_flags(s, "", CompileMode.exec, cflags), locals, locals); + setSystemState(); + Py.exec(Py.compile_flags(s, "", CompileMode.exec, cflags), globals, getLocals()); Py.flushLine(); } @@ -177,8 +209,8 @@ public class PythonInterpreter { * Execute a Python code object in the local namespace */ public void exec(PyObject code) { - setState(); - Py.exec(code, locals, locals); + setSystemState(); + Py.exec(code, globals, getLocals()); Py.flushLine(); } @@ -186,8 +218,8 @@ public class PythonInterpreter { * Execute a file of Python source in the local namespace */ public void execfile(String filename) { - setState(); - __builtin__.execfile_flags(filename, locals, locals, cflags); + setSystemState(); + __builtin__.execfile_flags(filename, globals, getLocals(), cflags); Py.flushLine(); } @@ -196,8 +228,8 @@ public class PythonInterpreter { } public void execfile(java.io.InputStream s, String name) { - setState(); - Py.runCode(Py.compile_flags(s, name, CompileMode.exec, cflags), locals, locals); + setSystemState(); + Py.runCode(Py.compile_flags(s, name, CompileMode.exec, cflags), globals, getLocals()); Py.flushLine(); } @@ -219,17 +251,27 @@ public class PythonInterpreter { } public PyCode compile(Reader reader, String filename) { mod node = ParserFacade.parseExpressionOrModule(reader, filename, cflags); - setState(); + setSystemState(); return Py.compile_flags(node, filename, CompileMode.eval, cflags); } public PyObject getLocals() { + if (globals != null) + return globals; + PyObject locals = threadLocals.get(); + if (locals != null) + return locals; + locals = new PyStringMap(); + threadLocals.set(locals); return locals; } public void setLocals(PyObject d) { - locals = d; + if (globals != null) + globals = d; + else + threadLocals.set(d); } /** @@ -242,7 +284,7 @@ public class PythonInterpreter { * appropriate Python object. */ public void set(String name, Object value) { - locals.__setitem__(name.intern(), Py.java2py(value)); + getLocals().__setitem__(name.intern(), Py.java2py(value)); } /** @@ -254,7 +296,7 @@ public class PythonInterpreter { * the value to set the variable to */ public void set(String name, PyObject value) { - locals.__setitem__(name.intern(), value); + getLocals().__setitem__(name.intern(), value); } /** @@ -265,7 +307,7 @@ public class PythonInterpreter { * @return the value of the variable, or null if that name isn't assigned */ public PyObject get(String name) { - return locals.__finditem__(name.intern()); + return getLocals().__finditem__(name.intern()); } /** @@ -280,7 +322,7 @@ public class PythonInterpreter { * @return the value of the variable as the given class, or null if that name isn't assigned */ public T get(String name, Class javaclass) { - PyObject val = locals.__finditem__(name.intern()); + PyObject val = getLocals().__finditem__(name.intern()); if (val == null) { return null; } @@ -288,6 +330,7 @@ public class PythonInterpreter { } public void cleanup() { + PySystemState systemState = getSystemState(); systemState.callExitFunc(); try { Py.getSystemState().stdout.invoke("flush");