Issue2507

classification
Title: Run "invokeFunction" in a thread does not inherit the "ScriptContext"
Type: behaviour Severity: normal
Components: Core Versions: Jython 2.7
Milestone: Jython 2.7.2
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: jjdelcerro
Priority: Keywords:

Created on 2016-07-02.17:47:28 by jjdelcerro, last changed 2016-09-06.05:19:23 by zyasoft.

Messages
msg10872 (view) Author: Joaquin Jose del Cerro Murciano (jjdelcerro) Date: 2016-07-02.17:47:26
I using "javax.script.ScriptEngine" (jsr223) to access python scripts.
I set in the context a writer (setWriter and setErrorWriter) to get the 
stdout and stderr of the python script.
When call the funcion of the script from a thread don't' use the writer set in the context. 

I patch the invokeFunction method of PyScriptEngine setting in, out, err and locals and creating a ThreadState and passing it as the first argument of __call__.

    public Object invokeFunction(String name, Object... args) throws ScriptException,
            NoSuchMethodException {
        try {
            PyObject function = interp.get(name);
            if (function == null) {
                throw new NoSuchMethodException(name);
            }
            interp.setIn(context.getReader());
            interp.setOut(context.getWriter());
            interp.setErr(context.getErrorWriter());
            interp.setLocals(new PyScriptEngineScope(this, context));
            ThreadState state = new ThreadState(this.interp.getSystemState());
            PyObject result;
            if(args != null) {
                result = function.__call__(state,Py.javas2pys(args));
            } else {
                result = function.__call__(state);
            }
            return result.__tojava__(Object.class);
        } catch (PyException pye) {
            throw scriptException(pye);
        }
    }

This solves my problem but do not know if it's right.
If correct, it might have to do the same in method InvokeMethod.

The code to reproduce the problem is:

package org.python.jsr223;

import java.io.IOException;
import java.io.Writer;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.Invocable;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

public class TestInvokeFunction {

    public static final String code = "print \"module\"\n"
            + "\n"
            + "def main(*args):\n"
            + "    print \"main\"\n";

    public static class Output extends Writer {
        private final String label;

        public Output(String label) {
            this.label = label;
        }
        @Override
        public void write(char[] cbuf, int off, int len) throws IOException {
            String s = new String(cbuf,off,len);
            System.out.println(label + " " + s);
        }

        @Override
        public void flush() throws IOException {
        }

        @Override
        public void close() throws IOException {
        }
        
    }
    
    public static void main(String[] args) throws Exception {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("python");
        ScriptContext context = engine.getContext();
        context.setWriter(new Output("[STDOUT]"));
        context.setErrorWriter(new Output("[STDERR]"));
        
        Compilable compilable = (Compilable) engine;
        CompiledScript compiledCode = compilable.compile(code);
        compiledCode.eval();
     
        final Invocable invocable = (Invocable) engine;
        // The output of this is ok
        Object r = invocable.invokeFunction("main", (Object[]) null);

        Runnable process = new Runnable() {
            @Override
            public void run() {
                try {
                    // The output of this is not get with the "Output" class.
                    Object r = invocable.invokeFunction("main", (Object[]) null);
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        };
        Thread thread = new Thread(process);
        thread.setDaemon(false);
        thread.start();
    }

}


And the output is:

[STDOUT] module
[STDOUT] 

[STDOUT] main
[STDOUT] 

main

Sorry for my english.
Joaquin
History
Date User Action Args
2016-09-06 05:19:23zyasoftsetmilestone: Jython 2.7.1 -> Jython 2.7.2
2016-07-02 17:47:28jjdelcerrocreate