-
Notifications
You must be signed in to change notification settings - Fork 52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement #@import directive #295
Comments
I took a first attempt at implementing this on the @ctrueden some open questions:
|
Perfect. Yes, this is what we should do, because as you say, we must attach the scope. One thing you could do if you want to be more future-proof would be to invent a
I believe so. I also noticed this problem but did not investigate yet. You could try putting |
@ctrueden wrote:
Good point, I will do that.
Indeed, see d48cc75. I also added a |
@imagejan Where I said |
Further progress on the I can now run the following Groovy script: #@script(name="utils", menuPath="")
#@output myfun
myfun = {
println 42
} and then run it from another one: #@import("utils")
myfun() // will print: 42 This latter script will also work when run in Javascript, but not currently in Beanshell or Python, where it throws an exception, e.g. from Python:
Likewise, I can do with a Python script: #@script(name="pyutil", menuPath="")
#@output myfun
def myfun():
print 42 and run: #@import("pyutil")
myfun() which in turn doesn't work in Groovy (the So now I'd need your input again, @ctrueden:
|
I think so. Certainly we can tackle each case by case. For import java.util.Arrays;
import java.util.function.Function;
import org.python.core.Py;
import org.python.core.PyObject;
import org.scijava.Context;
import org.scijava.script.ScriptModule;
import org.scijava.script.ScriptService;
public class PyFunctionAdapter {
public static void main(final String... args) throws Throwable {
final Context ctx = new Context();
// Define a Python function as a script output.
final String script = "#@output Object hello\n" + //
"\n" + //
"def hello(name):\n" + //
"\treturn \"Hello, \" + str(name) + \"!\"\n" + //
"\n";
// Execute the script.
final ScriptModule m = ctx.service(ScriptService.class).run("func.py",
script, false).get();
// Extract the Python function object.
final Object hello = m.getOutput("hello");
final PyObject pyFunc = (PyObject) hello;
if (!pyFunc.isCallable()) {
throw new IllegalStateException("expected callable Python object");
}
// Convert the Python function to a Java function.
final Function<Object, Object> func = t -> pyFunc.__call__(
t instanceof Object[] ? pyArgs((Object[]) t) : pyArgs(t));
// Try out our shiny new Java function.
System.out.println(func.apply("Curtis"));
}
private static PyObject[] pyArgs(final Object... o) {
if (o == null) return null;
return Arrays.stream(o).map(Py::java2py).toArray(PyObject[]::new);
}
} Some of the code above should become a case in the Getting late and I need sleep, so I'll try to tackle your other two questions tomorrow/soon. |
Let's update the Once we do that, the I am out of time for today, but wanted to post this still-incomplete response. I will investigate making |
I started working on the update to
Unfortunately, I still didn't have time to dig hard into this. However, my 2-minute idea is to use |
Using private static long containerID;
public static <T, R> Object createContainingObject(final Object object,
final String fieldName) throws Exception
{
final ClassPool pool = ClassPool.getDefault();
final CtClass ctc = pool.makeClass("ObjectContainer" + containerID++);
ctc.addField( CtField.make("public Object " + fieldName + ";", ctc));
final Class<?> c = ctc.toClass();
final Object container = c.newInstance();
c.getField(fieldName).set(container, object);
return container;
} It would be nice not to drag in a javassist dependency, though. More importantly, the above won't solve the cross-language issue; it simply assigns an object into a field of another object instance. The My next idea is two invent two new script-language-agnostic classes: So cross-language support for functions is much nastier than I thought. Let's stick to same-language usage for now. But cross-language could be a hackathon topic. Finally, for the sake of completeness, and so my explorations just now are not lost, here is the code I was playing around with along several of the above lines: import java.lang.reflect.Proxy;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
import org.python.core.PyObjectDerived;
import org.scijava.Context;
import org.scijava.script.ScriptService;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
public class Sandbox {
private static long containerID;
public static <T, R> Object createContainingObject(final Object object,
final String fieldName) throws Exception
{
final ClassPool pool = ClassPool.getDefault();
final CtClass ctc = pool.makeClass("ObjectContainer" + containerID++);
ctc.addField( CtField.make("public Object " + fieldName + ";", ctc));
final Class<?> c = ctc.toClass();
final Object container = c.newInstance();
c.getField(fieldName).set(container, object);
return container;
}
public static void demo() throws Exception {
final Context ctx = new Context();
final ScriptService ss = ctx.service(ScriptService.class);
// Create a Proxy object and see if we can use it in a script.
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class<?>[] interfaces = {java.util.List.class};
Object proxyObject = Proxy.newProxyInstance(loader, interfaces, (proxy, method, args) -> {
return "Result from " + method.getName() + " with " + args.length + " args";
});
final String proxyScript = "" + //
"#@ Object functions\n" +
"#@output String result\n" +
"result = functions.get(5)\n"; // .get(x) works, but .gget(x) fails; not a List method
final Object proxyResult = captureOutput(ss, "proxyTest.py", proxyScript, "result", "functions", proxyObject);
System.out.println("proxy result = " + proxyResult);
// Define a script that produces an object with functions.
final String producingScript = "" + //
"#@output Object functions\n" + //
"class functions(object):\n" + //
" def greet(__self__, name):\n" + //
" return 'hello, ' + str(name)\n" + //
" def wave(__self__):\n" + //
" return '*waves goodbye*'\n" + //
"functions = functions()";
final Object functions = //
captureOutput(ss, "producer.py", producingScript, "functions");
// NB: Python object type is PyObjectDerived. If you leave
// off the "functions = functions()" then it will be PyType.
System.out.println("Python object type = " + functions.getClass());
// Define and execute a Python script which uses the functions object.
final String pythonScript = "" + //
"#@input Object greeter\n" + //
"#@output Object greeting\n" + //
"greeting = greeter.greet('Chuckles')\n";
final Object pythonGreeting = //
captureOutput(ss, "script.py", pythonScript, "greeting", //
"greeter", functions);
System.out.println("Python greeting = " + pythonGreeting);
// Where is the function inside the object? The dictionary is empty.
System.out.println("Dict = " + ((PyObjectDerived) functions).getDict());
// Define and execute a Groovy script which uses the functions object.
final String groovyScript = "" + //
"#@input Object greeter\n" + //
"#@output Object greeting\n" + //
"greeting = greeter.greet('Chuckles')\n";
final Object groovyGreeting = //
captureOutput(ss, "script.groovy", groovyScript, "greeting", //
"greeter", functions);
System.out.println("Groovy greeting = " + groovyGreeting);
if (true) return;
///////////////////////////////////////////////////////
final Function<String, String> f1 = new Function<String, String>() {
@Override
public String apply(final String name) {
return "[1]Hello, " + name;
}
};
final Function<String, String> f2 = (String name) -> "[2]Hello, " + name;
final Function<String, String> f3 = name -> "[3]Hello, " + name;
final Object container1 = createContainingObject(f1, "greet");
final Object container2 = createContainingObject(f2, "greet");
final Object container3 = createContainingObject(f3, "greet");
}
private static Object captureOutput(final ScriptService ss, final String path,
final String script, final String outputName, Object... inputs)
throws InterruptedException, ExecutionException
{
return ss.run(path, script, false, inputs).get().getOutput(outputName);
}
public static void main(final String... args) throws Exception {
demo();
}
} |
This features seems very cool! I don't fully understand. In the context of supporting SciJava script in ImJoy, I guess this can potentially enable the following:
|
This issue has been mentioned on Image.sc Forum. There might be relevant details there: |
This issue has been mentioned on Image.sc Forum. There might be relevant details there: https://forum.image.sc/t/is-it-possible-to-supress-the-display-of-output-parameters/87991/4 |
As discussed with @ctrueden, this should allow scripts to import the output objects of other scripts.
Given a script:
This should be importable like this:
or with a local scope:
Subtasks:
info.set(String, String)
ScriptImportPreprocessor implements PreprocessorPlugin
ScriptModule
, stop.for importName in ((ScriptInfo) module.getInfo()).getProperty("imports")
:moduleService.run(moduleService.getModuleByName(importName)
((ScriptModule) module).getEngine() .getBindings(ScriptContext.ENGINE_SCOPE).putAll(outputs)
ModuleService#getModuleByName: getModules().stream().filter(m -> name.equals(m.getName()).limit(1)...
ScriptInfo.getProperty(String)
/ScriptInfo.setProperty(String, Object)
The text was updated successfully, but these errors were encountered: