Python Binding for OMS
The Python binding is an OMS module that allows to run Python scripts as OMS components. In this way, Python OMS components can be integrated into bigger multi-languages modeling solutions.
Because still under testing, the Python binding is temporarily implemented in a forked OMS repository only, available You must login to see this link. Register now, if you have no user account yet..
Intro
Jep
The Python binding is based on Jep back-ends.
Jep (Java Embedded Python) uses the native Python interpreter embedding CPython in Java through JNI. It is multi-thread safe (actually the provided documentation describes it as safe in a heavily threaded environment). It is much faster than alternatives because based on the native Python interpreter.
Requirements
The Python binding module is part of the OMS docker image. You need to install Docker and Git following this procedure to process Python OMS compliant components
Python OMS compliant components
How to modify a Python script to become an OMS-compliant component
A standard Python script needs to be slightly modified in order to get parsed by the Python binding module.
First, adjust the Python script following these simple rules:
- Identify inputs and outputs of the Python components;
- Declare inputs and outputs at the very start of the Python script;
- If the script IS NOT split in functions, bind it into a main function excluding input/output declarations and packages loading;
- If the script IS split in functions, identify the main function that has to be executed;
- The outputs must also be declared global at the very start of the main function;
- point 5 is not required if input variables are also output variables (see Listing 2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 |
import sys
import os
# @In("int")
inVar
# @Out("int")
outVar
# @Execute
def execute():
global outVar
...python script...
outVar = inVar + 3
...python script...
return
|
Listing 1: Simple Python OMS-compliant script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 |
import sys
import os
# @In("int")
# @Out("int")
simpleInOut
# @Out("int")
onlyOutVar
# @Execute
def execute():
global onlyOutVar
...python script...
simpleInOut += 35
outVar = simpleInOut + 3
...python script...
return
|
Listing 2: Input variable is also output variable
Second, add OMS annotations into the modified script:
- Because the Python language doesn't have data types, the optional field for @In @Out annotations is REQUIRED. The optional field specifies which Java data type that variable corresponds to;
- The @Execute annotation must be located above the signature of the main function;
- No annotation required by other functions and scripts called from the main one.
Available data types
The bi-directional data transfer between Java and Python supports the following data types:
Java-side |
Python-side |
Python import |
int |
int |
|
Integer |
Integer |
|
double |
double |
|
Double |
Double |
|
String |
String |
|
int |
jarray(length, JINT_ID, initialValue) |
from jep import jarray, JINT_ID |
double |
jarray(length, JDOUBLE_ID, initialValue) |
from jep import jarray, JDOUBLE_ID |
String |
jarray(length, JSTRING_ID, initialValue) |
from jep import jarray, JSTRING_ID |
List<Integer> |
[] |
|
List<Double> |
[] |
|
List<String> |
[] |
|
List<Object> |
[] |
|
Map<Integer, Object> |
dictionary |
|
Map<Object, Object> |
dictionary |
|
Main example
This is a very basic example that shows how to write a Python OMS-compliant component and how to connect it with two Java classes. You must login to see this link. Register now, if you have no user account yet.. A usage example of each available data type is provided.
Flow chart
This is a simple flow chart of the modeling solution. The orange boxes represent Java components, the green boxes represent Python components.
DirectedAcyclicGraph plugin failed: Could not find plugin DirectedAcyclicGraph
(com.ecyrd.jspwiki.plugin.PluginException:Could not find plugin DirectedAcyclicGraph, java.lang.ClassNotFoundException:Class 'DirectedAcyclicGraph' not found in search path!)
This Java component allocates and initializes each variable.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85 |
package py_simpleTest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List
import java.util.Map;
import oms3.annotations.Execute;
import oms3.annotations.Out;
public class Input {
// Integer
@Out
public int intVal;
@Out
public int[] intVectVal;
@Out
public Integer integerVal;
// Double
@Out
public double dbVal;
@Out
public double[] dbVectVal;
@Out
public Double doubleVal;
// String
@Out
public String strVal;
@Out
public String[] strVectVal;
// List
@Out
public List intList;
@Out
public List doubleList;
@Out
public List strList;
// Map
@Out
public Map intMap;
@Execute
public void inputProcessing() {
// Integer
intVal = 3;
intVectVal = new int[]{3,4,5};
integerVal = 33;
// Double
dbVal = 3.54;
dbVectVal = new double[]{3.33,4.44,5.55};
doubleVal = 3.35;
// String
strVal = "Testing jep";
strVectVal = new String[]{"Testing", "jep"};
// List
intList = new ArrayList();
intList.add(4);
intList.add(19);
intList.add(1);
intList.add(11);
doubleList = new ArrayList();
doubleList.add(23.41);
doubleList.add(23.19);
doubleList.add(98.33);
strList = new ArrayList();
strList.add("test");
strList.add("jep");
// Map
intMap = new HashMap();
intMap.put(54, "Pasini");
intMap.put(99, "Cassano");
intMap.put(21, "Bayliss");
}
}
|
inout1.py
This Python OMS-compliant component simply manipulates the input variables and returns the new modified ones.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124 |
from jep import jarray, JDOUBLE_ID, JINT_ID, JSTRING_ID
# @In("int")
intVal
# @Out("int")
valint
# @In("int[]")
intvectval
# @Out("int[]")
valvectint
# @In("Integer")
integerval
# @Out("Integer")
valinteger
# @In("double")
dbval
# @Out("double")
valdb
# @In("double[]")
dbvectval
# @Out("double[]")
valvectdb
# @In("Double")
doubleval
# @Out("Double")
valdouble
# @In("String")
strval
# @Out("String")
valstr
# @In("String[]")
strvectval
# @Out("String[]")
valvectstr
# @In("List")
intlist
# @Out("List")
listint
# @In("List")
doublelist
# @Out("List")
listdouble
# @In("List")
strlist
# @Out("List")
liststr
# @In("Map")
intmap
# @Out("Map")
mapint
# @Execute
def execute():
# Output integer
global valint
global valvectint
global valinteger
# Output double
global valdb
global valvectdb
global valdouble
# Output string
global valstr
global valvectstr
# Output list
global listint
global listdouble
global liststr
# Output map
global mapint
# Integer manipulation
valint = intval + 3
valinteger = integerval + 3
valvectint = jarray(len(intvectval), JINT_ID, 0)
for i in range(len(intvectval)):
valvectint[i] = intvectval[i] * 3
# Double manipulation
valdb = dbval * 3.4
valdouble = doubleval * 8.54
valvectdb = jarray(len(dbvectval), JDOUBLE_ID, 0.0)
for i in range(len(dbvectval)):
valvectdb[i] = dbvectval[i] * 11.756
# String manipulation
valstr = strval + ": works!"
valvectstr = jarray(4, JSTRING_ID, "")
for i in range(len(strvectval)):
valvectstr[i] = strvectval[i]
valvectstr[2] = ":"
valvectstr[3] = "works"
# List manipulation
listint = [1,2,3,4]
for i in range(len(listint)):
listint[i] = intlist[i] * listint[i]
tmpdouble = [1.43,2.156,3.199]
listdouble = []
for i in range(len(doublelist)):
listdouble.append(doublelist[i])
for i in range(len(doublelist)):
tmpval = tmpdouble[i] * doublelist[i]
listdouble.append(tmpval)
liststr = ["work"]
liststr.insert(0, strlist[1])
liststr.insert(0, strlist[0])
# Dictionary manipulation
mapint = intmap
mapint[18] = "Manning"
|
Output.java
This Java class checks each variable with expected values. It returns error when actual and expected values differs.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142 |
package py_simpleTest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List
import java.util.Map;
import oms3.annotations.*;
public class Output {
// Integer
@In
public int intVal;
@In
public int[] intVectVal;
@In
public Integer integerVal;
// Double
@In
public double dbVal;
@In
public double[] dbVectVal;
@In
public Double doubleVal;
// String
@In
public String strVal;
@In
public String[] strVectVal;
// List
@In
public List intList;
@In
public List doubleList;
@In
public List strList;
// Map
@In
public Map intMap;
@Execute
public void exec() {
integerCheck();
doubleCheck();
stringCheck();
listCheck();
mapCheck();
}
private void integerCheck() {
System.out.println("Checking for Integer data structures interchange...");
if (intVal != 6)
throw new RuntimeException();
int[] tmp = new int[]{9,12,15};
for (int i=0; ilength; i++) {
if (tmp[i]!=intVectVal[i]) {
throw new RuntimeException();
}
}
if (!integerVal.equals(36))
throw new RuntimeException();
System.out.println("Integer data structures correctly interchanged.");
}
private void doubleCheck() {
System.out.println("Checking for Double data structures interchange...");
if (dbVal != 12.036)
throw new RuntimeException();
double[] tmpd = new double[]{39.14748,52.19664,65.2458};
for (int i=0; ilength; i++) {
if (tmpd[i]!=dbVectVal[i]) {
throw new RuntimeException();
}
}
if (!doubleVal.equals(28.609))
throw new RuntimeException();
System.out.println("Double data structures correctly interchanged.");
}
private void stringCheck() {
System.out.println("Checking for String data structures interchange...");
if (!strVal.equals("Testing jep: works!"))
throw new RuntimeException();
String[] tmps = new String[]{"Testing","jep",":","works"};
for (int i=0; ilength; i++) {
if (!strVectVal[i].equals(tmps[i])) {
throw new RuntimeException();
}
}
System.out.println("String data structures correctly interchanged.");
}
private void listCheck() {
System.out.println("Checking for List data structures interchange...");
List intFinalList = new ArrayList();
intFinalList.add(4*1);
intFinalList.add(19*2);
intFinalList.add(1*3);
intFinalList.add(11*4);
if (!intList.equals(intFinalList))
throw new RuntimeException();
List doubleFinalList = new ArrayList();
doubleFinalList.add(23.41);
doubleFinalList.add(23.19);
doubleFinalList.add(98.33);
doubleFinalList.add(33.4763);
doubleFinalList.add(49.99764);
doubleFinalList.add(314.55768);
if (!doubleList.equals(doubleFinalList))
throw new RuntimeException();
List strFinalList = new ArrayList();
strFinalList.add("test");
strFinalList.add("jep");
strFinalList.add("work");
if (!strList.equals(strFinalList))
throw new RuntimeException();
System.out.println("List data structures correctly interchanged.");
}
private void mapCheck() {
System.out.println("Checking for Map data structures interchange...");
Map intFinalMap = new HashMap();
intFinalMap.add(54, "Pasini");
intFinalMap.add(99, "Cassano");
intFinalMap.add(21, "Bayliss");
intFinalMap.add(18, "Manning");
if (!intMapList.equals(intFinalMap))
throw new RuntimeException();
System.out.println("Map data structures correctly interchanged.");
}
}
|
The Simulation file (.sim) excercises the components above
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 |
import static oms3.SimBuilder.instance as OMS3
def home = oms_prj
/*
* Python component connected to two Java components.
* 'input' component instantiates and initializes each variable
* and passes them further to 'component1' which manipulates each
* variable accordingly to its data type. Then 'component1' passes
* each variable further to 'output' which compares them to expected values.
*/
OMS3.sim {
resource "${home}/lib"
build()
model {
components {
"input" "py_simpleTest.Input"
"output" "py_simpleTest.Output"
"component1" "py_simpleTest.inout1"
}
connect {
// componentname,outfieldname" -> "componentname,infieldname"
"intput.intVal" "component1.intval"
"intput.intVectVal" "component1.intvectval"
"intput.integerVal" "component1.integerval"
"intput.dbVal" "component1.dbval"
"intput.dbVectVal" "component1.dbvectval"
"intput.doubleVal" "component1.doubleval"
"intput.strVal" "component1.strval"
"intput.strVectVal" "component1.strvectval"
"intput.intList" "component1.intlist"
"intput.doubleList" "component1.doublelist"
"intput.strList" "component1.strlist"
"intput.intMap" "component1.intmap"
"component1.valint" "output.intVal"
"component1.valvectint" "output.intVectVal"
"component1.valinteger" "output.integerVal"
"component1.valdb" "output.dbVal"
"component1.valvectdb" "output.dbVectVal"
"component1.valdouble" "output.doubleVal"
"component1.valstr" "output.strVal"
"component1.valvectstr" "output.strVectVal"
"component1.listint" "output.intList"
"component1.listdouble" "output.doubleList"
"component1.liststr" "output.strList"
"component1.mapint" "output.intMap"
}
parameter {
}
}
}
|
Other examples
Another example is made available at the You must login to see this link. Register now, if you have no user account yet. repository for exercising generic data types List<Object>, Map<Object, Object> for bi-directional interchange Java-Python.
@TODO