Topics

How do I send a parameter to a Jython script?

danielb987
 

Logix has the ability to run a script as an action. For LogixNG, I want to extend that and therefore I need to send a parameter to the script. The parameter will be of a class I create for the purpose.

I can run a script:

public void testScript() throws ScriptException {
jmri.script.JmriScriptEngineManager scriptEngine = jmri.script.JmriScriptEngineManager.getDefault();
String myScript = "print Hello\n"
scriptEngine.eval(myScript, scriptEngine.getEngineByExtension("py"));
}

But I want to do something like:

public void testScript() throws ScriptException {
String myString = "Hello world";

jmri.script.JmriScriptEngineManager scriptEngine = jmri.script.JmriScriptEngineManager.getDefault();
String myScript = "print myString\n"
scriptEngine.eval(myScript, scriptEngine.getEngineByExtension("py"));
}

Maybe one of these methods in JmriScriptEngineManager is what I'm looking at:
public Object eval(File file, Bindings bindings);
public Object eval(File file, ScriptContext context);

But these variants are depricated:
public Object eval(Reader reader, ScriptEngine engine);
public Object eval(Reader reader, ScriptEngine engine, Bindings bindings);

Why is these depricated? Or can I create a File object without have it on the harddrive? If not, I can accept the need to put the script on disk. The important thing is to be able to send a variable to the script.

Can a script return a value to the caller? If not, I can use an AtomicBoolean or similar to return the value.
In some cases I want to return a boolean, in other cases a double or a String.

Daniel

Randall Wood
 

Did you read the deprecation comments in the Javadocs? They are deprecated in favor of other methods run against one of the parameters of the deprecated method, as pointed out in the Javadocs.

If you look at the deprecated methods, you will see that they merely call the method recommended to be used instead of them.

Put another way I do not need to define `Foo.bar(Bar, int) { Bar.bar(int); }` when any caller of that method can call `Bar.bar(int)` directly.

Randall

On Nov 19, 2019, at 22:18, danielb987 <db123@...> wrote:

Logix has the ability to run a script as an action. For LogixNG, I want to extend that and therefore I need to send a parameter to the script. The parameter will be of a class I create for the purpose.

I can run a script:

public void testScript() throws ScriptException {
jmri.script.JmriScriptEngineManager scriptEngine = jmri.script.JmriScriptEngineManager.getDefault();
String myScript = "print Hello\n"
scriptEngine.eval(myScript, scriptEngine.getEngineByExtension("py"));
}

But I want to do something like:

public void testScript() throws ScriptException {
String myString = "Hello world";

jmri.script.JmriScriptEngineManager scriptEngine = jmri.script.JmriScriptEngineManager.getDefault();
String myScript = "print myString\n"
scriptEngine.eval(myScript, scriptEngine.getEngineByExtension("py"));
}

Maybe one of these methods in JmriScriptEngineManager is what I'm looking at:
public Object eval(File file, Bindings bindings);
public Object eval(File file, ScriptContext context);

But these variants are depricated:
public Object eval(Reader reader, ScriptEngine engine);
public Object eval(Reader reader, ScriptEngine engine, Bindings bindings);

Why is these depricated? Or can I create a File object without have it on the harddrive? If not, I can accept the need to put the script on disk. The important thing is to be able to send a variable to the script.

Can a script return a value to the caller? If not, I can use an AtomicBoolean or similar to return the value.
In some cases I want to return a boolean, in other cases a double or a String.

Daniel


Bob Jacobsen
 

Jython lives in the same heap as Java. Your script can reach anything a Java object can. For example, you could use InstanceManager to access the LogixNG manager and from that set or get variables that the script could then also access.

Bob

--
Bob Jacobsen
@BobJacobsen

danielb987
 

Thanks for the answer.

The problem is that that requires hardcoding some value into the script, for example the system name of the LogixNG that calls it.

I hoped that there was a way to just give the script a parameter on the stack, for example the LogixNG object that's calling it. But maybe that's not possible?

Daniel


2019-11-20 05:40 skrev Bob Jacobsen:

Jython lives in the same heap as Java. Your script can reach anything
a Java object can. For example, you could use InstanceManager to
access the LogixNG manager and from that set or get variables that the
script could then also access.
Bob
--
Bob Jacobsen
@BobJacobsen

Bob Jacobsen
 

Scripts don’t have parameters or stacks. They just have an address space, which they share with everything else.

If you want to really pass parameters, you could (by convention) insist that the scripts define a particular function or class, and then (by convention) invoke that function or class while passing parameters.

Bob

On Nov 19, 2019, at 9:04 PM, danielb987 <db123@...> wrote:

Thanks for the answer.

The problem is that that requires hardcoding some value into the script, for example the system name of the LogixNG that calls it.

I hoped that there was a way to just give the script a parameter on the stack, for example the LogixNG object that's calling it. But maybe that's not possible?

Daniel


2019-11-20 05:40 skrev Bob Jacobsen:
Jython lives in the same heap as Java. Your script can reach anything
a Java object can. For example, you could use InstanceManager to
access the LogixNG manager and from that set or get variables that the
script could then also access.
Bob
--
Bob Jacobsen
@BobJacobsen

--
Bob Jacobsen
@BobJacobsen

Dave Sand
 

I use memory variables to pass parameters and return results.

Dave Sand

----- Original message -----
From: Bob Jacobsen <@BobJacobsen>
To: jmri@jmri-developers.groups.io
Subject: Re: [jmri-developers] How do I send a parameter to a Jython script?
Date: Tuesday, November 19, 2019 11:13 PM

Scripts don’t have parameters or stacks. They just have an address space, which they share with everything else.

If you want to really pass parameters, you could (by convention) insist that the scripts define a particular function or class, and then (by convention) invoke that function or class while passing parameters.

Bob

On Nov 19, 2019, at 9:04 PM, danielb987 <db123@...> wrote:

Thanks for the answer.

The problem is that that requires hardcoding some value into the script, for example the system name of the LogixNG that calls it.

I hoped that there was a way to just give the script a parameter on the stack, for example the LogixNG object that's calling it. But maybe that's not possible?

Daniel


2019-11-20 05:40 skrev Bob Jacobsen:
Jython lives in the same heap as Java. Your script can reach anything
a Java object can. For example, you could use InstanceManager to
access the LogixNG manager and from that set or get variables that the
script could then also access.
Bob
--
Bob Jacobsen
@BobJacobsen

--
Bob Jacobsen
@BobJacobsen

danielb987
 

Thanks!

The following works fine:

public class MyClass {
public final String hello = "Hello World";
public final AtomicReference result = new AtomicReference();
}

public void testScript() throws ScriptException {

JmriScriptEngineManager scriptEngine = JmriScriptEngineManager.getDefault();
String myScript =
"print param.hello\n" +
"param.result.set(\"A new result\")";

Bindings bindings = new SimpleBindings();
MyClass param = new MyClass();
bindings.put("param", param); // Give the script access to the local variable 'param'
scriptEngine.getEngineByExtension("py").eval(myScript, bindings);
System.out.format("The result of the script: %s%n", param.result.get());
}

The local variable "param" is accessible by the script 'myScript' only.

Daniel


2019-11-20 05:36 skrev Randall Wood via Groups.Io:

Did you read the deprecation comments in the Javadocs? They are
deprecated in favor of other methods run against one of the parameters
of the deprecated method, as pointed out in the Javadocs.
If you look at the deprecated methods, you will see that they merely
call the method recommended to be used instead of them.
Put another way I do not need to define `Foo.bar(Bar, int) {
Bar.bar(int); }` when any caller of that method can call
`Bar.bar(int)` directly.
Randall
On Nov 19, 2019, at 22:18, danielb987 <db123@...> wrote:
Logix has the ability to run a script as an action. For LogixNG, I want to extend that and therefore I need to send a parameter to the script. The parameter will be of a class I create for the purpose.
I can run a script:
public void testScript() throws ScriptException {
jmri.script.JmriScriptEngineManager scriptEngine = jmri.script.JmriScriptEngineManager.getDefault();
String myScript = "print Hello\n"
scriptEngine.eval(myScript, scriptEngine.getEngineByExtension("py"));
}
But I want to do something like:
public void testScript() throws ScriptException {
String myString = "Hello world";
jmri.script.JmriScriptEngineManager scriptEngine = jmri.script.JmriScriptEngineManager.getDefault();
String myScript = "print myString\n"
scriptEngine.eval(myScript, scriptEngine.getEngineByExtension("py"));
}
Maybe one of these methods in JmriScriptEngineManager is what I'm looking at:
public Object eval(File file, Bindings bindings);
public Object eval(File file, ScriptContext context);
But these variants are depricated:
public Object eval(Reader reader, ScriptEngine engine);
public Object eval(Reader reader, ScriptEngine engine, Bindings bindings);
Why is these depricated? Or can I create a File object without have it on the harddrive? If not, I can accept the need to put the script on disk. The important thing is to be able to send a variable to the script.
Can a script return a value to the caller? If not, I can use an AtomicBoolean or similar to return the value.
In some cases I want to return a boolean, in other cases a double or a String.
Daniel

Randall Wood
 

Some comments inline:

On 20-Nov-2019, at 03:05, danielb987 <db123@...> wrote:

Thanks!

The following works fine:

public class MyClass {
public final String hello = "Hello World";
public final AtomicReference result = new AtomicReference();
}

public void testScript() throws ScriptException {

JmriScriptEngineManager scriptEngine = JmriScriptEngineManager.getDefault();
Note that the EngineManager is not an Engine, but a manager (this could cause some confusion, if you were to retain the actual engine later).

String myScript =
"print param.hello\n" +
"param.result.set(\"A new result\")";

Bindings bindings = new SimpleBindings();
You will likely want to build on the default bindings to provide ready access to common things (so scripts can rely on more context), so this should be:
Bindings bindings = new SimpleBindings(scriptEngine.getDefaultContext().getBindings());

MyClass param = new MyClass();
bindings.put("param", param); // Give the script access to the local variable 'param'
scriptEngine.getEngineByExtension("py").eval(myScript, bindings);
You want to use Engine names instead of relying on extensions (names avoids extra lookups), so:
scriptEngine.getEngineByName(JmriScriptEngineManager.PYTHON).eval(myScript, bindings);

System.out.format("The result of the script: %s%n", param.result.get());
}

The local variable "param" is accessible by the script 'myScript' only.

Daniel


2019-11-20 05:36 skrev Randall Wood via Groups.Io:
Did you read the deprecation comments in the Javadocs? They are
deprecated in favor of other methods run against one of the parameters
of the deprecated method, as pointed out in the Javadocs.
If you look at the deprecated methods, you will see that they merely
call the method recommended to be used instead of them.
Put another way I do not need to define `Foo.bar(Bar, int) {
Bar.bar(int); }` when any caller of that method can call
`Bar.bar(int)` directly.
Randall
On Nov 19, 2019, at 22:18, danielb987 <db123@...> wrote:
Logix has the ability to run a script as an action. For LogixNG, I want to extend that and therefore I need to send a parameter to the script. The parameter will be of a class I create for the purpose.
I can run a script:
public void testScript() throws ScriptException {
jmri.script.JmriScriptEngineManager scriptEngine = jmri.script.JmriScriptEngineManager.getDefault();
String myScript = "print Hello\n"
scriptEngine.eval(myScript, scriptEngine.getEngineByExtension("py"));
}
But I want to do something like:
public void testScript() throws ScriptException {
String myString = "Hello world";
jmri.script.JmriScriptEngineManager scriptEngine = jmri.script.JmriScriptEngineManager.getDefault();
String myScript = "print myString\n"
scriptEngine.eval(myScript, scriptEngine.getEngineByExtension("py"));
}
Maybe one of these methods in JmriScriptEngineManager is what I'm looking at:
public Object eval(File file, Bindings bindings);
public Object eval(File file, ScriptContext context);
But these variants are depricated:
public Object eval(Reader reader, ScriptEngine engine);
public Object eval(Reader reader, ScriptEngine engine, Bindings bindings);
Why is these depricated? Or can I create a File object without have it on the harddrive? If not, I can accept the need to put the script on disk. The important thing is to be able to send a variable to the script.
Can a script return a value to the caller? If not, I can use an AtomicBoolean or similar to return the value.
In some cases I want to return a boolean, in other cases a double or a String.
Daniel

danielb987
 

Thanks.

Bindings bindings = new SimpleBindings();
You will likely want to build on the default bindings
to provide ready access to common things (so scripts
can rely on more context), so this should be:
Bindings bindings = new SimpleBindings(scriptEngine.getDefaultContext().getBindings());
I tested that and I have all the default bindings even if I create my own Bindings object without adding the default bindings to it. For example, this line works fine:

t = turnouts.provideTurnout("12")

If I however creates my own Context, I will get a clean environment for my script, without the default bindings.

Daniel


2019-11-20 13:33 skrev Randall Wood via Groups.Io:
Some comments inline:

On 20-Nov-2019, at 03:05, danielb987 <db123@...> wrote:
Thanks!
The following works fine:
public class MyClass {
public final String hello = "Hello World";
public final AtomicReference result = new AtomicReference();
}
public void testScript() throws ScriptException {
JmriScriptEngineManager scriptEngine = JmriScriptEngineManager.getDefault();
Note that the EngineManager is not an Engine, but a manager (this
could cause some confusion, if you were to retain the actual engine
later).

String myScript =
"print param.hello\n" +
"param.result.set(\"A new result\")";
Bindings bindings = new SimpleBindings();
You will likely want to build on the default bindings to provide ready
access to common things (so scripts can rely on more context), so this
should be:
Bindings bindings = new
SimpleBindings(scriptEngine.getDefaultContext().getBindings());

MyClass param = new MyClass();
bindings.put("param", param); // Give the script access to the local variable 'param'
scriptEngine.getEngineByExtension("py").eval(myScript, bindings);
You want to use Engine names instead of relying on extensions (names
avoids extra lookups), so:
scriptEngine.getEngineByName(JmriScriptEngineManager.PYTHON).eval(myScript,
bindings);

System.out.format("The result of the script: %s%n", param.result.get());
}
The local variable "param" is accessible by the script 'myScript' only.
Daniel
2019-11-20 05:36 skrev Randall Wood via Groups.Io:
Did you read the deprecation comments in the Javadocs? They are
deprecated in favor of other methods run against one of the parameters
of the deprecated method, as pointed out in the Javadocs.
If you look at the deprecated methods, you will see that they merely
call the method recommended to be used instead of them.
Put another way I do not need to define `Foo.bar(Bar, int) {
Bar.bar(int); }` when any caller of that method can call
`Bar.bar(int)` directly.
Randall
On Nov 19, 2019, at 22:18, danielb987 <db123@...> wrote:
Logix has the ability to run a script as an action. For LogixNG, I want to extend that and therefore I need to send a parameter to the script. The parameter will be of a class I create for the purpose.
I can run a script:
public void testScript() throws ScriptException {
jmri.script.JmriScriptEngineManager scriptEngine = jmri.script.JmriScriptEngineManager.getDefault();
String myScript = "print Hello\n"
scriptEngine.eval(myScript, scriptEngine.getEngineByExtension("py"));
}
But I want to do something like:
public void testScript() throws ScriptException {
String myString = "Hello world";
jmri.script.JmriScriptEngineManager scriptEngine = jmri.script.JmriScriptEngineManager.getDefault();
String myScript = "print myString\n"
scriptEngine.eval(myScript, scriptEngine.getEngineByExtension("py"));
}
Maybe one of these methods in JmriScriptEngineManager is what I'm looking at:
public Object eval(File file, Bindings bindings);
public Object eval(File file, ScriptContext context);
But these variants are depricated:
public Object eval(Reader reader, ScriptEngine engine);
public Object eval(Reader reader, ScriptEngine engine, Bindings bindings);
Why is these depricated? Or can I create a File object without have it on the harddrive? If not, I can accept the need to put the script on disk. The important thing is to be able to send a variable to the script.
Can a script return a value to the caller? If not, I can use an AtomicBoolean or similar to return the value.
In some cases I want to return a boolean, in other cases a double or a String.
Daniel