Python4j (#422)
* types * pom fix * basic exec + tests * safe exec * exec fixes + tests * prim tests * lists and dicts * collections tests * list test * api * exec and return all vars * context manager + fixes * leak fixes * jobs tests * gc basic working * more gc fixed * copyright headers * try-catch-finally * gc fixes * validate var name (startswith _collapsed..) * try block refac * pythonexecutioner nits * hashset->set * call() gc fix * gc fixes * type check fix * types fixes * refacs * rem numpyarray * threadsafety check * private->public * threadsafe checks * pythonGC test * threading fixes + tests * threading tests+ * threading test fixes * make PythonException unchecked * nits * docstrings * path fixesmaster
parent
0bc9785508
commit
bde0a4ec98
1
pom.xml
1
pom.xml
|
@ -137,6 +137,7 @@
|
||||||
<module>jumpy</module>
|
<module>jumpy</module>
|
||||||
<module>pydatavec</module>
|
<module>pydatavec</module>
|
||||||
<module>pydl4j</module>
|
<module>pydl4j</module>
|
||||||
|
<module>python4j</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<scm>
|
<scm>
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
~ Copyright (c) 2020 Konduit K.K.
|
||||||
|
~
|
||||||
|
~ This program and the accompanying materials are made available under the
|
||||||
|
~ terms of the Apache License, Version 2.0 which is available at
|
||||||
|
~ https://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
~ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
~ License for the specific language governing permissions and limitations
|
||||||
|
~ under the License.
|
||||||
|
~
|
||||||
|
~ SPDX-License-Identifier: Apache-2.0
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
|
||||||
|
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>deeplearning4j</artifactId>
|
||||||
|
<groupId>org.deeplearning4j</groupId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>org.eclipse</groupId>
|
||||||
|
<artifactId>python4j-parent</artifactId>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
<modules>
|
||||||
|
<module>python4j-core</module>
|
||||||
|
<module>python4j-numpy</module>
|
||||||
|
</modules>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>${lombok.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-classic</artifactId>
|
||||||
|
<version>${logback.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>${junit.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-io</groupId>
|
||||||
|
<artifactId>commons-io</artifactId>
|
||||||
|
<version>${commons-io.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.code.findbugs</groupId>
|
||||||
|
<artifactId>jsr305</artifactId>
|
||||||
|
<version>3.0.2</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
|
@ -0,0 +1,44 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
~ Copyright (c) 2020 Konduit K.K.
|
||||||
|
~
|
||||||
|
~ This program and the accompanying materials are made available under the
|
||||||
|
~ terms of the Apache License, Version 2.0 which is available at
|
||||||
|
~ https://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
~ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
~ License for the specific language governing permissions and limitations
|
||||||
|
~ under the License.
|
||||||
|
~
|
||||||
|
~ SPDX-License-Identifier: Apache-2.0
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
|
||||||
|
|
||||||
|
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>python4j-parent</artifactId>
|
||||||
|
<groupId>org.eclipse</groupId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>python4j-core</artifactId>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.json</groupId>
|
||||||
|
<artifactId>json</artifactId>
|
||||||
|
<version>20190722</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.bytedeco</groupId>
|
||||||
|
<artifactId>cpython-platform</artifactId>
|
||||||
|
<version>${cpython-platform.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
|
@ -0,0 +1,611 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2020 Konduit K.K.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Apache License, Version 2.0 which is available at
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
package org.eclipse.python4j;
|
||||||
|
|
||||||
|
import org.bytedeco.cpython.PyObject;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.bytedeco.cpython.global.python.*;
|
||||||
|
|
||||||
|
|
||||||
|
public class Python {
|
||||||
|
|
||||||
|
static {
|
||||||
|
new PythonExecutioner();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Imports a python module, similar to python import statement.
|
||||||
|
*
|
||||||
|
* @param moduleName name of the module to be imported
|
||||||
|
* @return reference to the module object
|
||||||
|
*/
|
||||||
|
public static PythonObject importModule(String moduleName) {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
PythonObject module = new PythonObject(PyImport_ImportModule(moduleName));
|
||||||
|
if (module.isNone()) {
|
||||||
|
throw new PythonException("Error importing module: " + moduleName);
|
||||||
|
}
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a builtins attribute
|
||||||
|
*
|
||||||
|
* @param attrName Attribute name
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject attr(String attrName) {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
PyObject builtins = PyImport_ImportModule("builtins");
|
||||||
|
try {
|
||||||
|
return new PythonObject(PyObject_GetAttrString(builtins, attrName));
|
||||||
|
} finally {
|
||||||
|
Py_DecRef(builtins);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the size of a PythonObject. similar to len() in python.
|
||||||
|
*
|
||||||
|
* @param pythonObject
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject len(PythonObject pythonObject) {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
long n = PyObject_Size(pythonObject.getNativePythonObject());
|
||||||
|
if (n < 0) {
|
||||||
|
throw new PythonException("Object has no length: " + pythonObject);
|
||||||
|
}
|
||||||
|
return PythonTypes.INT.toPython(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the string representation of an object.
|
||||||
|
*
|
||||||
|
* @param pythonObject
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject str(PythonObject pythonObject) {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
try {
|
||||||
|
return PythonTypes.STR.toPython(pythonObject.toString());
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an empty string
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject str() {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
try {
|
||||||
|
return PythonTypes.STR.toPython("");
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the str type object
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject strType() {
|
||||||
|
return attr("str");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a floating point number from a number or a string.
|
||||||
|
* @param pythonObject
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject float_(PythonObject pythonObject) {
|
||||||
|
return PythonTypes.FLOAT.toPython(PythonTypes.FLOAT.toJava(pythonObject));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reutrns 0.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject float_() {
|
||||||
|
try {
|
||||||
|
return PythonTypes.FLOAT.toPython(0d);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the float type object
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject floatType() {
|
||||||
|
return attr("float");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a value to a Boolean value i.e., True or False, using the standard truth testing procedure.
|
||||||
|
* @param pythonObject
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject bool(PythonObject pythonObject) {
|
||||||
|
return PythonTypes.BOOL.toPython(PythonTypes.BOOL.toJava(pythonObject));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns False.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject bool() {
|
||||||
|
return PythonTypes.BOOL.toPython(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the bool type object
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject boolType() {
|
||||||
|
return attr("bool");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an integer from a number or a string.
|
||||||
|
* @param pythonObject
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject int_(PythonObject pythonObject) {
|
||||||
|
return PythonTypes.INT.toPython(PythonTypes.INT.toJava(pythonObject));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns 0
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject int_() {
|
||||||
|
return PythonTypes.INT.toPython(0L);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the int type object
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject intType() {
|
||||||
|
return attr("int");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes sequence types and converts them to lists.
|
||||||
|
* @param pythonObject
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject list(PythonObject pythonObject) {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
try (PythonGC _ = PythonGC.watch()) {
|
||||||
|
PythonObject listF = attr("list");
|
||||||
|
PythonObject ret = listF.call(pythonObject);
|
||||||
|
if (ret.isNone()) {
|
||||||
|
throw new PythonException("Object is not iterable: " + pythonObject.toString());
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns empty list.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject list() {
|
||||||
|
return PythonTypes.LIST.toPython(Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns list type object.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject listType() {
|
||||||
|
return attr("list");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a dictionary.
|
||||||
|
* @param pythonObject
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject dict(PythonObject pythonObject) {
|
||||||
|
PythonObject dictF = attr("dict");
|
||||||
|
PythonObject ret = dictF.call(pythonObject);
|
||||||
|
if (ret.isNone()) {
|
||||||
|
throw new PythonException("Cannot build dict from object: " + pythonObject.toString());
|
||||||
|
}
|
||||||
|
dictF.del();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns empty dict
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject dict() {
|
||||||
|
return PythonTypes.DICT.toPython(Collections.emptyMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns dict type object.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject dictType() {
|
||||||
|
return attr("dict");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a set.
|
||||||
|
* @param pythonObject
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject set(PythonObject pythonObject) {
|
||||||
|
PythonObject setF = attr("set");
|
||||||
|
PythonObject ret = setF.call(pythonObject);
|
||||||
|
if (ret.isNone()) {
|
||||||
|
throw new PythonException("Cannot build set from object: " + pythonObject.toString());
|
||||||
|
}
|
||||||
|
setF.del();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns empty set.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject set() {
|
||||||
|
PythonObject setF = attr("set");
|
||||||
|
PythonObject ret;
|
||||||
|
ret = setF.call();
|
||||||
|
setF.del();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns empty set.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject setType() {
|
||||||
|
return attr("set");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a bytearray.
|
||||||
|
* @param pythonObject
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject bytearray(PythonObject pythonObject) {
|
||||||
|
PythonObject baF = attr("bytearray");
|
||||||
|
PythonObject ret = baF.call(pythonObject);
|
||||||
|
if (ret.isNone()) {
|
||||||
|
throw new PythonException("Cannot build bytearray from object: " + pythonObject.toString());
|
||||||
|
}
|
||||||
|
baF.del();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns empty bytearray.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject bytearray() {
|
||||||
|
PythonObject baF = attr("bytearray");
|
||||||
|
PythonObject ret;
|
||||||
|
ret = baF.call();
|
||||||
|
baF.del();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns bytearray type object
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject bytearrayType() {
|
||||||
|
return attr("bytearray");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a memoryview.
|
||||||
|
* @param pythonObject
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject memoryview(PythonObject pythonObject) {
|
||||||
|
PythonObject mvF = attr("memoryview");
|
||||||
|
PythonObject ret = mvF.call(pythonObject);
|
||||||
|
if (ret.isNone()) {
|
||||||
|
throw new PythonException("Cannot build memoryview from object: " + pythonObject.toString());
|
||||||
|
}
|
||||||
|
mvF.del();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns memoryview type object.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject memoryviewType() {
|
||||||
|
return attr("memoryview");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a byte string.
|
||||||
|
* @param pythonObject
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject bytes(PythonObject pythonObject) {
|
||||||
|
PythonObject bytesF = attr("bytes");
|
||||||
|
PythonObject ret = bytesF.call(pythonObject);
|
||||||
|
if (ret.isNone()) {
|
||||||
|
throw new PythonException("Cannot build bytes from object: " + pythonObject.toString());
|
||||||
|
}
|
||||||
|
bytesF.del();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns empty byte string.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject bytes() {
|
||||||
|
PythonObject bytesF = attr("bytes");
|
||||||
|
PythonObject ret;
|
||||||
|
ret = bytesF.call();
|
||||||
|
bytesF.del();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns bytes type object
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject bytesType() {
|
||||||
|
return attr("bytes");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a tuple.
|
||||||
|
* @param pythonObject
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject tuple(PythonObject pythonObject) {
|
||||||
|
PythonObject tupleF = attr("tupleF");
|
||||||
|
PythonObject ret = tupleF.call(pythonObject);
|
||||||
|
if (ret.isNone()) {
|
||||||
|
throw new PythonException("Cannot build tuple from object: " + pythonObject.toString());
|
||||||
|
}
|
||||||
|
tupleF.del();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns empty tuple.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject tuple() {
|
||||||
|
PythonObject tupleF = attr("tuple");
|
||||||
|
PythonObject ret;
|
||||||
|
ret = tupleF.call();
|
||||||
|
tupleF.del();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns tuple type object
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject tupleType() {
|
||||||
|
return attr("tuple");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an Exception
|
||||||
|
* @param pythonObject
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject Exception(PythonObject pythonObject) {
|
||||||
|
PythonObject excF = attr("Exception");
|
||||||
|
PythonObject ret = excF.call(pythonObject);
|
||||||
|
excF.del();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an Exception
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject Exception() {
|
||||||
|
PythonObject excF = attr("Exception");
|
||||||
|
PythonObject ret;
|
||||||
|
ret = excF.call();
|
||||||
|
excF.del();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns Exception type object
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject ExceptionType() {
|
||||||
|
return attr("Exception");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the globals dictionary.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject globals() {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
PyObject main = PyImport_ImportModule("__main__");
|
||||||
|
PyObject globals = PyModule_GetDict(main);
|
||||||
|
Py_DecRef(main);
|
||||||
|
return new PythonObject(globals, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the type of an object.
|
||||||
|
* @param pythonObject
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject type(PythonObject pythonObject) {
|
||||||
|
PythonObject typeF = attr("type");
|
||||||
|
PythonObject ret = typeF.call(pythonObject);
|
||||||
|
typeF.del();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns True if the specified object is of the specified type, otherwise False.
|
||||||
|
* @param obj
|
||||||
|
* @param type
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean isinstance(PythonObject obj, PythonObject... type) {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
PyObject argsTuple = PyTuple_New(type.length);
|
||||||
|
try {
|
||||||
|
for (int i = 0; i < type.length; i++) {
|
||||||
|
PythonObject x = type[i];
|
||||||
|
Py_IncRef(x.getNativePythonObject());
|
||||||
|
PyTuple_SetItem(argsTuple, i, x.getNativePythonObject());
|
||||||
|
}
|
||||||
|
return PyObject_IsInstance(obj.getNativePythonObject(), argsTuple) != 0;
|
||||||
|
} finally {
|
||||||
|
Py_DecRef(argsTuple);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates the specified expression.
|
||||||
|
* @param expression
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject eval(String expression) {
|
||||||
|
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
PyObject compiledCode = Py_CompileString(expression, "", Py_eval_input);
|
||||||
|
PyObject main = PyImport_ImportModule("__main__");
|
||||||
|
PyObject globals = PyModule_GetDict(main);
|
||||||
|
PyObject locals = PyDict_New();
|
||||||
|
try {
|
||||||
|
return new PythonObject(PyEval_EvalCode(compiledCode, globals, locals));
|
||||||
|
} finally {
|
||||||
|
Py_DecRef(main);
|
||||||
|
Py_DecRef(locals);
|
||||||
|
Py_DecRef(compiledCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the builtins module
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject builtins() {
|
||||||
|
return importModule("builtins");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns None.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject None() {
|
||||||
|
return eval("None");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns True.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject True() {
|
||||||
|
return eval("True");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns False.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject False() {
|
||||||
|
return eval("False");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns True if the object passed is callable callable, otherwise False.
|
||||||
|
* @param pythonObject
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean callable(PythonObject pythonObject) {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
return PyCallable_Check(pythonObject.getNativePythonObject()) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void setContext(String context){
|
||||||
|
PythonContextManager.setContext(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getCurrentContext() {
|
||||||
|
return PythonContextManager.getCurrentContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void deleteContext(String context){
|
||||||
|
PythonContextManager.deleteContext(context);
|
||||||
|
}
|
||||||
|
public static void resetContext() {
|
||||||
|
PythonContextManager.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a string of code.
|
||||||
|
* @param code
|
||||||
|
* @throws PythonException
|
||||||
|
*/
|
||||||
|
public static void exec(String code) throws PythonException {
|
||||||
|
PythonExecutioner.exec(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a string of code.
|
||||||
|
* @param code
|
||||||
|
* @param inputs
|
||||||
|
* @param outputs
|
||||||
|
*/
|
||||||
|
public static void exec(String code, List<PythonVariable> inputs, List<PythonVariable> outputs){
|
||||||
|
PythonExecutioner.exec(code, inputs, outputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,241 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2020 Konduit K.K.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Apache License, Version 2.0 which is available at
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
package org.eclipse.python4j;
|
||||||
|
|
||||||
|
import javax.lang.model.SourceVersion;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emulates multiples interpreters in a single interpreter.
|
||||||
|
* This works by simply obfuscating/de-obfuscating variable names
|
||||||
|
* such that only the required subset of the global namespace is "visible"
|
||||||
|
* at any given time.
|
||||||
|
* By default, there exists a "main" context emulating the default interpreter
|
||||||
|
*
|
||||||
|
* @author Fariz Rahman
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
public class PythonContextManager {
|
||||||
|
|
||||||
|
private static Set<String> contexts = new HashSet<>();
|
||||||
|
private static AtomicBoolean init = new AtomicBoolean(false);
|
||||||
|
private static String currentContext;
|
||||||
|
private static final String MAIN_CONTEXT = "main";
|
||||||
|
private static final String COLLAPSED_KEY = "__collapsed__";
|
||||||
|
|
||||||
|
static {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void init() {
|
||||||
|
if (init.get()) return;
|
||||||
|
new PythonExecutioner();
|
||||||
|
init.set(true);
|
||||||
|
currentContext = MAIN_CONTEXT;
|
||||||
|
contexts.add(currentContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new context.
|
||||||
|
* @param contextName
|
||||||
|
*/
|
||||||
|
public static void addContext(String contextName) {
|
||||||
|
if (!validateContextName(contextName)) {
|
||||||
|
throw new PythonException("Invalid context name: " + contextName);
|
||||||
|
}
|
||||||
|
contexts.add(contextName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if context exists, else false.
|
||||||
|
* @param contextName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean hasContext(String contextName) {
|
||||||
|
return contexts.contains(contextName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean validateContextName(String s) {
|
||||||
|
return SourceVersion.isIdentifier(s) && !s.startsWith(COLLAPSED_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getContextPrefix(String contextName) {
|
||||||
|
return COLLAPSED_KEY + contextName + "__";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getCollapsedVarNameForContext(String varName, String contextName) {
|
||||||
|
return getContextPrefix(contextName) + varName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String expandCollapsedVarName(String varName, String contextName) {
|
||||||
|
String prefix = COLLAPSED_KEY + contextName + "__";
|
||||||
|
return varName.substring(prefix.length());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void collapseContext(String contextName) {
|
||||||
|
try (PythonGC _ = PythonGC.watch()) {
|
||||||
|
PythonObject globals = Python.globals();
|
||||||
|
PythonObject pop = globals.attr("pop");
|
||||||
|
PythonObject keysF = globals.attr("keys");
|
||||||
|
PythonObject keys = keysF.call();
|
||||||
|
PythonObject keysList = Python.list(keys);
|
||||||
|
int numKeys = Python.len(keysList).toInt();
|
||||||
|
for (int i = 0; i < numKeys; i++) {
|
||||||
|
PythonObject key = keysList.get(i);
|
||||||
|
String keyStr = key.toString();
|
||||||
|
if (!((keyStr.startsWith("__") && keyStr.endsWith("__")) || keyStr.startsWith("__collapsed_"))) {
|
||||||
|
String collapsedKey = getCollapsedVarNameForContext(keyStr, contextName);
|
||||||
|
PythonObject val = pop.call(key);
|
||||||
|
|
||||||
|
PythonObject pyNewKey = new PythonObject(collapsedKey);
|
||||||
|
globals.set(pyNewKey, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception pe) {
|
||||||
|
throw new RuntimeException(pe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void expandContext(String contextName) {
|
||||||
|
try (PythonGC _ = PythonGC.watch()) {
|
||||||
|
String prefix = getContextPrefix(contextName);
|
||||||
|
PythonObject globals = Python.globals();
|
||||||
|
PythonObject pop = globals.attr("pop");
|
||||||
|
PythonObject keysF = globals.attr("keys");
|
||||||
|
|
||||||
|
PythonObject keys = keysF.call();
|
||||||
|
|
||||||
|
PythonObject keysList = Python.list(keys);
|
||||||
|
try (PythonGC __ = PythonGC.pause()) {
|
||||||
|
int numKeys = Python.len(keysList).toInt();
|
||||||
|
|
||||||
|
for (int i = 0; i < numKeys; i++) {
|
||||||
|
PythonObject key = keysList.get(i);
|
||||||
|
String keyStr = key.toString();
|
||||||
|
if (keyStr.startsWith(prefix)) {
|
||||||
|
String expandedKey = expandCollapsedVarName(keyStr, contextName);
|
||||||
|
PythonObject val = pop.call(key);
|
||||||
|
PythonObject newKey = new PythonObject(expandedKey);
|
||||||
|
globals.set(newKey, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activates the specified context
|
||||||
|
* @param contextName
|
||||||
|
*/
|
||||||
|
public static void setContext(String contextName) {
|
||||||
|
if (contextName.equals(currentContext)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!hasContext(contextName)) {
|
||||||
|
addContext(contextName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
collapseContext(currentContext);
|
||||||
|
|
||||||
|
expandContext(contextName);
|
||||||
|
currentContext = contextName;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activates the main context
|
||||||
|
*/
|
||||||
|
public static void setMainContext() {
|
||||||
|
setContext(MAIN_CONTEXT);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current context's name.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String getCurrentContext() {
|
||||||
|
return currentContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the current context.
|
||||||
|
*/
|
||||||
|
public static void reset() {
|
||||||
|
String tempContext = "___temp__context___";
|
||||||
|
String currContext = currentContext;
|
||||||
|
setContext(tempContext);
|
||||||
|
deleteContext(currContext);
|
||||||
|
setContext(currContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the specified context.
|
||||||
|
* @param contextName
|
||||||
|
*/
|
||||||
|
public static void deleteContext(String contextName) {
|
||||||
|
if (contextName.equals(currentContext)) {
|
||||||
|
throw new PythonException("Cannot delete current context!");
|
||||||
|
}
|
||||||
|
if (!contexts.contains(contextName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String prefix = getContextPrefix(contextName);
|
||||||
|
PythonObject globals = Python.globals();
|
||||||
|
PythonObject keysList = Python.list(globals.attr("keys").call());
|
||||||
|
int numKeys = Python.len(keysList).toInt();
|
||||||
|
for (int i = 0; i < numKeys; i++) {
|
||||||
|
PythonObject key = keysList.get(i);
|
||||||
|
String keyStr = key.toString();
|
||||||
|
if (keyStr.startsWith(prefix)) {
|
||||||
|
globals.attr("__delitem__").call(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contexts.remove(contextName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all contexts except the main context.
|
||||||
|
*/
|
||||||
|
public static void deleteNonMainContexts() {
|
||||||
|
setContext(MAIN_CONTEXT); // will never fail
|
||||||
|
for (String c : contexts.toArray(new String[0])) {
|
||||||
|
if (!c.equals(MAIN_CONTEXT)) {
|
||||||
|
deleteContext(c); // will never fail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the names of all contexts.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String[] getContexts() {
|
||||||
|
return contexts.toArray(new String[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2020 Konduit K.K.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Apache License, Version 2.0 which is available at
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
package org.eclipse.python4j;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when an exception occurs in python land
|
||||||
|
*/
|
||||||
|
public class PythonException extends RuntimeException {
|
||||||
|
public PythonException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getExceptionString(PythonObject exception) {
|
||||||
|
try (PythonGC gc = PythonGC.watch()) {
|
||||||
|
if (Python.isinstance(exception, Python.ExceptionType())) {
|
||||||
|
String exceptionClass = Python.type(exception).attr("__name__").toString();
|
||||||
|
String message = exception.toString();
|
||||||
|
return exceptionClass + ": " + message;
|
||||||
|
}
|
||||||
|
return exception.toString();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("An error occurred while trying to create a PythonException.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PythonException(PythonObject exception) {
|
||||||
|
this(getExceptionString(exception));
|
||||||
|
}
|
||||||
|
|
||||||
|
public PythonException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PythonException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,342 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2020 Konduit K.K.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Apache License, Version 2.0 which is available at
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
package org.eclipse.python4j;
|
||||||
|
|
||||||
|
import org.bytedeco.cpython.PyObject;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.bytedeco.cpython.global.python;
|
||||||
|
|
||||||
|
import static org.bytedeco.cpython.global.python.*;
|
||||||
|
import static org.bytedeco.cpython.global.python.PyImport_ImportModule;
|
||||||
|
import static org.bytedeco.cpython.helper.python.Py_SetPath;
|
||||||
|
|
||||||
|
|
||||||
|
public class PythonExecutioner {
|
||||||
|
private final static String PYTHON_EXCEPTION_KEY = "__python_exception__";
|
||||||
|
private static AtomicBoolean init = new AtomicBoolean(false);
|
||||||
|
private final static String DEFAULT_PYTHON_PATH_PROPERTY = "org.eclipse.python4j.path";
|
||||||
|
private final static String JAVACPP_PYTHON_APPEND_TYPE = "org.eclipse.python4j.path.append";
|
||||||
|
private final static String DEFAULT_APPEND_TYPE = "before";
|
||||||
|
|
||||||
|
static {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static synchronized void init() {
|
||||||
|
if (init.get()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
init.set(true);
|
||||||
|
initPythonPath();
|
||||||
|
PyEval_InitThreads();
|
||||||
|
Py_InitializeEx(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a variable.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
public static void setVariable(String name, PythonObject value) {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
PyObject main = PyImport_ImportModule("__main__");
|
||||||
|
PyObject globals = PyModule_GetDict(main);
|
||||||
|
PyDict_SetItemString(globals, name, value.getNativePythonObject());
|
||||||
|
Py_DecRef(main);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets given list of PythonVariables in the interpreter.
|
||||||
|
*
|
||||||
|
* @param pyVars
|
||||||
|
*/
|
||||||
|
public static void setVariables(List<PythonVariable> pyVars) {
|
||||||
|
for (PythonVariable pyVar : pyVars)
|
||||||
|
setVariable(pyVar.getName(), pyVar.getPythonObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets given list of PythonVariables in the interpreter.
|
||||||
|
*
|
||||||
|
* @param pyVars
|
||||||
|
*/
|
||||||
|
public static void setVariables(PythonVariable... pyVars) {
|
||||||
|
setVariables(Arrays.asList(pyVars));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the given list of PythonVariables from the interpreter.
|
||||||
|
*
|
||||||
|
* @param pyVars
|
||||||
|
*/
|
||||||
|
public static void getVariables(List<PythonVariable> pyVars) {
|
||||||
|
for (PythonVariable pyVar : pyVars)
|
||||||
|
pyVar.setValue(getVariable(pyVar.getName(), pyVar.getType()).getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the given list of PythonVariables from the interpreter.
|
||||||
|
*
|
||||||
|
* @param pyVars
|
||||||
|
*/
|
||||||
|
public static void getVariables(PythonVariable... pyVars) {
|
||||||
|
getVariables(Arrays.asList(pyVars));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the variable with the given name from the interpreter.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static PythonObject getVariable(String name) {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
PyObject main = PyImport_ImportModule("__main__");
|
||||||
|
PyObject globals = PyModule_GetDict(main);
|
||||||
|
PyObject pyName = PyUnicode_FromString(name);
|
||||||
|
try {
|
||||||
|
if (PyDict_Contains(globals, pyName) == 1) {
|
||||||
|
return new PythonObject(PyObject_GetItem(globals, pyName), false);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
Py_DecRef(main);
|
||||||
|
//Py_DecRef(globals);
|
||||||
|
Py_DecRef(pyName);
|
||||||
|
}
|
||||||
|
return new PythonObject(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the variable with the given name from the interpreter.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static <T> PythonVariable<T> getVariable(String name, PythonType<T> type) {
|
||||||
|
PythonObject val = getVariable(name);
|
||||||
|
return new PythonVariable<>(name, type, type.toJava(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a string of code
|
||||||
|
*
|
||||||
|
* @param code
|
||||||
|
*/
|
||||||
|
public static synchronized void simpleExec(String code) {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
int result = PyRun_SimpleStringFlags(code, null);
|
||||||
|
if (result != 0) {
|
||||||
|
throw new PythonException("Execution failed, unable to retrieve python exception.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void throwIfExecutionFailed() {
|
||||||
|
PythonObject ex = getVariable(PYTHON_EXCEPTION_KEY);
|
||||||
|
if (ex != null && !ex.isNone() && !ex.toString().isEmpty()) {
|
||||||
|
setVariable(PYTHON_EXCEPTION_KEY, PythonTypes.STR.toPython(""));
|
||||||
|
throw new PythonException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static String getWrappedCode(String code) {
|
||||||
|
|
||||||
|
try (InputStream is = PythonExecutioner.class
|
||||||
|
.getResourceAsStream("pythonexec/pythonexec.py")) {
|
||||||
|
String base = IOUtils.toString(is, StandardCharsets.UTF_8);
|
||||||
|
String indentedCode = " " + code.replace("\n", "\n ");
|
||||||
|
String out = base.replace(" pass", indentedCode);
|
||||||
|
return out;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IllegalStateException("Unable to read python code!", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a string of code. Throws PythonException if execution fails.
|
||||||
|
*
|
||||||
|
* @param code
|
||||||
|
*/
|
||||||
|
public static void exec(String code) {
|
||||||
|
simpleExec(getWrappedCode(code));
|
||||||
|
throwIfExecutionFailed();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void exec(String code, List<PythonVariable> inputs, List<PythonVariable> outputs) {
|
||||||
|
if (inputs != null) {
|
||||||
|
setVariables(inputs.toArray(new PythonVariable[0]));
|
||||||
|
}
|
||||||
|
exec(code);
|
||||||
|
if (outputs != null) {
|
||||||
|
getVariables(outputs.toArray(new PythonVariable[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return list of all supported variables in the interpreter.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static List<PythonVariable> getAllVariables() {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
List<PythonVariable> ret = new ArrayList<>();
|
||||||
|
PyObject main = PyImport_ImportModule("__main__");
|
||||||
|
PyObject globals = PyModule_GetDict(main);
|
||||||
|
PyObject keys = PyDict_Keys(globals);
|
||||||
|
PyObject keysIter = PyObject_GetIter(keys);
|
||||||
|
try {
|
||||||
|
|
||||||
|
long n = PyObject_Size(globals);
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
PyObject pyKey = PyIter_Next(keysIter);
|
||||||
|
try {
|
||||||
|
if (!new PythonObject(pyKey, false).toString().startsWith("_")) {
|
||||||
|
|
||||||
|
PyObject pyVal = PyObject_GetItem(globals, pyKey); // TODO check ref count
|
||||||
|
PythonType pt;
|
||||||
|
try {
|
||||||
|
pt = PythonTypes.getPythonTypeForPythonObject(new PythonObject(pyVal, false));
|
||||||
|
|
||||||
|
} catch (PythonException pe) {
|
||||||
|
pt = null;
|
||||||
|
}
|
||||||
|
if (pt != null) {
|
||||||
|
ret.add(
|
||||||
|
new PythonVariable<>(
|
||||||
|
new PythonObject(pyKey, false).toString(),
|
||||||
|
pt,
|
||||||
|
pt.toJava(new PythonObject(pyVal, false))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
Py_DecRef(pyKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
Py_DecRef(keysIter);
|
||||||
|
Py_DecRef(keys);
|
||||||
|
Py_DecRef(main);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a string of code and returns a list of all supported variables.
|
||||||
|
*
|
||||||
|
* @param code
|
||||||
|
* @param inputs
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static List<PythonVariable> execAndReturnAllVariables(String code, List<PythonVariable> inputs) {
|
||||||
|
setVariables(inputs);
|
||||||
|
simpleExec(getWrappedCode(code));
|
||||||
|
return getAllVariables();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a string of code and returns a list of all supported variables.
|
||||||
|
*
|
||||||
|
* @param code
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static List<PythonVariable> execAndReturnAllVariables(String code) {
|
||||||
|
simpleExec(getWrappedCode(code));
|
||||||
|
return getAllVariables();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static synchronized void initPythonPath() {
|
||||||
|
try {
|
||||||
|
String path = System.getProperty(DEFAULT_PYTHON_PATH_PROPERTY);
|
||||||
|
if (path == null) {
|
||||||
|
File[] packages = cachePackages();
|
||||||
|
|
||||||
|
//// TODO: fix in javacpp
|
||||||
|
File sitePackagesWindows = new File(python.cachePackage(), "site-packages");
|
||||||
|
File[] packages2 = new File[packages.length + 1];
|
||||||
|
for (int i = 0; i < packages.length; i++) {
|
||||||
|
//System.out.println(packages[i].getAbsolutePath());
|
||||||
|
packages2[i] = packages[i];
|
||||||
|
}
|
||||||
|
packages2[packages.length] = sitePackagesWindows;
|
||||||
|
//System.out.println(sitePackagesWindows.getAbsolutePath());
|
||||||
|
packages = packages2;
|
||||||
|
//////////
|
||||||
|
|
||||||
|
Py_SetPath(packages);
|
||||||
|
} else {
|
||||||
|
StringBuffer sb = new StringBuffer();
|
||||||
|
File[] packages = cachePackages();
|
||||||
|
JavaCppPathType pathAppendValue = JavaCppPathType.valueOf(System.getProperty(JAVACPP_PYTHON_APPEND_TYPE, DEFAULT_APPEND_TYPE).toUpperCase());
|
||||||
|
switch (pathAppendValue) {
|
||||||
|
case BEFORE:
|
||||||
|
for (File cacheDir : packages) {
|
||||||
|
sb.append(cacheDir);
|
||||||
|
sb.append(java.io.File.pathSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append(path);
|
||||||
|
break;
|
||||||
|
case AFTER:
|
||||||
|
sb.append(path);
|
||||||
|
|
||||||
|
for (File cacheDir : packages) {
|
||||||
|
sb.append(cacheDir);
|
||||||
|
sb.append(java.io.File.pathSeparator);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NONE:
|
||||||
|
sb.append(path);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_SetPath(sb.toString());
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum JavaCppPathType {
|
||||||
|
BEFORE, AFTER, NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
private static File[] cachePackages() throws IOException {
|
||||||
|
File[] path = org.bytedeco.cpython.global.python.cachePackages();
|
||||||
|
path = Arrays.copyOf(path, path.length + 1);
|
||||||
|
path[path.length - 1] = cachePackage();
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,137 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2020 Konduit K.K.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Apache License, Version 2.0 which is available at
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
package org.eclipse.python4j;
|
||||||
|
|
||||||
|
import org.bytedeco.cpython.PyObject;
|
||||||
|
import org.bytedeco.javacpp.Pointer;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.bytedeco.cpython.global.python.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap your code in a try-with-PythonGC block for automatic GC:
|
||||||
|
* ```
|
||||||
|
* try(PythonGC gc = PythonGC.lock()){
|
||||||
|
* // your code here
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* If a PythonObject created inside such a block has to be used outside
|
||||||
|
* the block, use PythonGC.keep() to exclude that object from GC.
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* PythonObject pyObj;
|
||||||
|
*
|
||||||
|
* try(PythonGC gc = PythonG.lock()){
|
||||||
|
* // do stuff
|
||||||
|
* pyObj = someFunction();
|
||||||
|
* PythonGC.keep(pyObj);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class PythonGC implements Closeable {
|
||||||
|
|
||||||
|
private PythonGC previousFrame = null;
|
||||||
|
private boolean active = true;
|
||||||
|
private static PythonGC currentFrame = new PythonGC();
|
||||||
|
|
||||||
|
private Set<PyObject> objects = new HashSet<>();
|
||||||
|
|
||||||
|
private boolean alreadyRegistered(PyObject pyObject) {
|
||||||
|
if (objects.contains(pyObject)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (previousFrame == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return previousFrame.alreadyRegistered(pyObject);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addObject(PythonObject pythonObject) {
|
||||||
|
if (!active) return;
|
||||||
|
if (Pointer.isNull(pythonObject.getNativePythonObject()))return;
|
||||||
|
if (alreadyRegistered(pythonObject.getNativePythonObject())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
objects.add(pythonObject.getNativePythonObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void register(PythonObject pythonObject) {
|
||||||
|
currentFrame.addObject(pythonObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void keep(PythonObject pythonObject) {
|
||||||
|
currentFrame.objects.remove(pythonObject.getNativePythonObject());
|
||||||
|
if (currentFrame.previousFrame != null) {
|
||||||
|
currentFrame.previousFrame.addObject(pythonObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PythonGC() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PythonGC watch() {
|
||||||
|
PythonGC ret = new PythonGC();
|
||||||
|
ret.previousFrame = currentFrame;
|
||||||
|
ret.active = currentFrame.active;
|
||||||
|
currentFrame = ret;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void collect() {
|
||||||
|
for (PyObject pyObject : objects) {
|
||||||
|
// TODO find out how globals gets collected here
|
||||||
|
if (pyObject.equals(Python.globals().getNativePythonObject())) continue;
|
||||||
|
// try{
|
||||||
|
// System.out.println(PythonTypes.STR.toJava(new PythonObject(pyObject, false)));
|
||||||
|
// }catch (Exception e){}
|
||||||
|
Py_DecRef(pyObject);
|
||||||
|
|
||||||
|
}
|
||||||
|
this.objects = new HashSet<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
if (active) collect();
|
||||||
|
currentFrame = previousFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isWatching() {
|
||||||
|
if (!currentFrame.active) return false;
|
||||||
|
return currentFrame.previousFrame != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PythonGC pause() {
|
||||||
|
PythonGC pausedFrame = new PythonGC();
|
||||||
|
pausedFrame.active = false;
|
||||||
|
pausedFrame.previousFrame = currentFrame;
|
||||||
|
currentFrame = pausedFrame;
|
||||||
|
return pausedFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void resume() {
|
||||||
|
if (currentFrame.active) {
|
||||||
|
throw new RuntimeException("GC not paused!");
|
||||||
|
}
|
||||||
|
currentFrame = currentFrame.previousFrame;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2020 Konduit K.K.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Apache License, Version 2.0 which is available at
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
package org.eclipse.python4j;
|
||||||
|
|
||||||
|
|
||||||
|
import org.bytedeco.cpython.PyThreadState;
|
||||||
|
import org.omg.SendingContext.RunTime;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
import static org.bytedeco.cpython.global.python.*;
|
||||||
|
|
||||||
|
|
||||||
|
public class PythonGIL implements AutoCloseable {
|
||||||
|
private static PyThreadState mainThreadState;
|
||||||
|
private static final AtomicBoolean acquired = new AtomicBoolean();
|
||||||
|
private boolean acquiredByMe = false;
|
||||||
|
private static long defaultThreadId = -1;
|
||||||
|
|
||||||
|
public static void assertThreadSafe() {
|
||||||
|
if (acquired.get()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (defaultThreadId == -1) {
|
||||||
|
defaultThreadId = Thread.currentThread().getId();
|
||||||
|
} else if (defaultThreadId != Thread.currentThread().getId()) {
|
||||||
|
throw new RuntimeException("Attempt to use Python4j from multiple threads without " +
|
||||||
|
"acquiring GIL. Enclose your code in a try(PythonGIL gil = PythonGIL.lock()){...}" +
|
||||||
|
" block to ensure that GIL is acquired in multi-threaded environments.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
new PythonExecutioner();
|
||||||
|
}
|
||||||
|
|
||||||
|
private PythonGIL() {
|
||||||
|
while (acquired.get()) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(10);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
acquire();
|
||||||
|
acquired.set(true);
|
||||||
|
acquiredByMe = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
if (acquiredByMe) {
|
||||||
|
release();
|
||||||
|
acquired.set(false);
|
||||||
|
acquiredByMe = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized PythonGIL lock() {
|
||||||
|
return new PythonGIL();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static synchronized void acquire() {
|
||||||
|
mainThreadState = PyEval_SaveThread();
|
||||||
|
PyThreadState ts = PyThreadState_New(mainThreadState.interp());
|
||||||
|
PyEval_RestoreThread(ts);
|
||||||
|
PyThreadState_Swap(ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void release() { // do not synchronize!
|
||||||
|
PyEval_SaveThread();
|
||||||
|
PyEval_RestoreThread(mainThreadState);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,175 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2020 Konduit K.K.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Apache License, Version 2.0 which is available at
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
package org.eclipse.python4j;
|
||||||
|
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
/**
|
||||||
|
* PythonJob is the right abstraction for executing multiple python scripts
|
||||||
|
* in a multi thread stateful environment. The setup-and-run mode allows your
|
||||||
|
* "setup" code (imports, model loading etc) to be executed only once.
|
||||||
|
*/
|
||||||
|
public class PythonJob {
|
||||||
|
|
||||||
|
private String code;
|
||||||
|
private String name;
|
||||||
|
private String context;
|
||||||
|
private boolean setupRunMode;
|
||||||
|
private PythonObject runF;
|
||||||
|
|
||||||
|
static {
|
||||||
|
new PythonExecutioner();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
/**
|
||||||
|
* @param name Name for the python job.
|
||||||
|
* @param code Python code.
|
||||||
|
* @param setupRunMode If true, the python code is expected to have two methods: setup(), which takes no arguments,
|
||||||
|
* and run() which takes some or no arguments. setup() method is executed once,
|
||||||
|
* and the run() method is called with the inputs(if any) per transaction, and is expected to return a dictionary
|
||||||
|
* mapping from output variable names (str) to output values.
|
||||||
|
* If false, the full script is run on each transaction and the output variables are obtained from the global namespace
|
||||||
|
* after execution.
|
||||||
|
*/
|
||||||
|
public PythonJob(@Nonnull String name, @Nonnull String code, boolean setupRunMode){
|
||||||
|
this.name = name;
|
||||||
|
this.code = code;
|
||||||
|
this.setupRunMode = setupRunMode;
|
||||||
|
context = "__job_" + name;
|
||||||
|
if (PythonContextManager.hasContext(context)) {
|
||||||
|
throw new PythonException("Unable to create python job " + name + ". Context " + context + " already exists!");
|
||||||
|
}
|
||||||
|
if (setupRunMode) setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all variables in current context and calls setup()
|
||||||
|
*/
|
||||||
|
public void clearState(){
|
||||||
|
String context = this.context;
|
||||||
|
PythonContextManager.setContext("main");
|
||||||
|
PythonContextManager.deleteContext(context);
|
||||||
|
this.context = context;
|
||||||
|
setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setup(){
|
||||||
|
try (PythonGIL gil = PythonGIL.lock()) {
|
||||||
|
PythonContextManager.setContext(context);
|
||||||
|
PythonObject runF = PythonExecutioner.getVariable("run");
|
||||||
|
if (runF == null || runF.isNone() || !Python.callable(runF)) {
|
||||||
|
PythonExecutioner.exec(code);
|
||||||
|
runF = PythonExecutioner.getVariable("run");
|
||||||
|
}
|
||||||
|
if (runF.isNone() || !Python.callable(runF)) {
|
||||||
|
throw new PythonException("run() method not found! " +
|
||||||
|
"If a PythonJob is created with 'setup and run' " +
|
||||||
|
"mode enabled, the associated python code is " +
|
||||||
|
"expected to contain a run() method " +
|
||||||
|
"(with or without arguments).");
|
||||||
|
}
|
||||||
|
this.runF = runF;
|
||||||
|
PythonObject setupF = PythonExecutioner.getVariable("setup");
|
||||||
|
if (!setupF.isNone()) {
|
||||||
|
setupF.call();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void exec(List<PythonVariable> inputs, List<PythonVariable> outputs) {
|
||||||
|
try (PythonGIL gil = PythonGIL.lock()) {
|
||||||
|
try (PythonGC _ = PythonGC.watch()) {
|
||||||
|
PythonContextManager.setContext(context);
|
||||||
|
|
||||||
|
if (!setupRunMode) {
|
||||||
|
|
||||||
|
PythonExecutioner.exec(code, inputs, outputs);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PythonExecutioner.setVariables(inputs);
|
||||||
|
|
||||||
|
PythonObject inspect = Python.importModule("inspect");
|
||||||
|
PythonObject getfullargspec = inspect.attr("getfullargspec");
|
||||||
|
PythonObject argspec = getfullargspec.call(runF);
|
||||||
|
PythonObject argsList = argspec.attr("args");
|
||||||
|
PythonObject runargs = Python.dict();
|
||||||
|
int argsCount = Python.len(argsList).toInt();
|
||||||
|
for (int i = 0; i < argsCount; i++) {
|
||||||
|
PythonObject arg = argsList.get(i);
|
||||||
|
PythonObject val = Python.globals().get(arg);
|
||||||
|
if (val.isNone()) {
|
||||||
|
throw new PythonException("Input value not received for run() argument: " + arg.toString());
|
||||||
|
}
|
||||||
|
runargs.set(arg, val);
|
||||||
|
}
|
||||||
|
PythonObject outDict = runF.callWithKwargs(runargs);
|
||||||
|
PythonObject globals = Python.globals();
|
||||||
|
PythonObject updateF = globals.attr("update");
|
||||||
|
updateF.call(outDict);
|
||||||
|
PythonExecutioner.getVariables(outputs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PythonVariable> execAndReturnAllVariables(List<PythonVariable> inputs){
|
||||||
|
try (PythonGIL gil = PythonGIL.lock()) {
|
||||||
|
try (PythonGC _ = PythonGC.watch()) {
|
||||||
|
PythonContextManager.setContext(context);
|
||||||
|
if (!setupRunMode) {
|
||||||
|
return PythonExecutioner.execAndReturnAllVariables(code, inputs);
|
||||||
|
}
|
||||||
|
PythonExecutioner.setVariables(inputs);
|
||||||
|
PythonObject inspect = Python.importModule("inspect");
|
||||||
|
PythonObject getfullargspec = inspect.attr("getfullargspec");
|
||||||
|
PythonObject argspec = getfullargspec.call(runF);
|
||||||
|
PythonObject argsList = argspec.attr("args");
|
||||||
|
PythonObject runargs = Python.dict();
|
||||||
|
int argsCount = Python.len(argsList).toInt();
|
||||||
|
for (int i = 0; i < argsCount; i++) {
|
||||||
|
PythonObject arg = argsList.get(i);
|
||||||
|
PythonObject val = Python.globals().get(arg);
|
||||||
|
if (val.isNone()) {
|
||||||
|
throw new PythonException("Input value not received for run() argument: " + arg.toString());
|
||||||
|
}
|
||||||
|
runargs.set(arg, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
PythonObject outDict = runF.callWithKwargs(runargs);
|
||||||
|
PythonObject globals = Python.globals();
|
||||||
|
PythonObject updateF = globals.attr("update");
|
||||||
|
updateF.call(outDict);
|
||||||
|
return PythonExecutioner.getAllVariables();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,244 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2020 Konduit K.K.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Apache License, Version 2.0 which is available at
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
package org.eclipse.python4j;
|
||||||
|
|
||||||
|
|
||||||
|
import org.bytedeco.cpython.PyObject;
|
||||||
|
import org.bytedeco.javacpp.Pointer;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.bytedeco.cpython.global.python.*;
|
||||||
|
|
||||||
|
public class PythonObject {
|
||||||
|
|
||||||
|
static {
|
||||||
|
new PythonExecutioner();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean owned = true;
|
||||||
|
private PyObject nativePythonObject;
|
||||||
|
|
||||||
|
|
||||||
|
public PythonObject(PyObject nativePythonObject, boolean owned) {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
this.nativePythonObject = nativePythonObject;
|
||||||
|
this.owned = owned;
|
||||||
|
if (owned && nativePythonObject != null) {
|
||||||
|
PythonGC.register(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PythonObject(PyObject nativePythonObject) {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
this.nativePythonObject = nativePythonObject;
|
||||||
|
if (nativePythonObject != null) {
|
||||||
|
PythonGC.register(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public PyObject getNativePythonObject() {
|
||||||
|
return nativePythonObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return PythonTypes.STR.toJava(this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isNone() {
|
||||||
|
if (nativePythonObject == null || Pointer.isNull(nativePythonObject)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
try (PythonGC _ = PythonGC.pause()) {
|
||||||
|
PythonObject type = Python.type(this);
|
||||||
|
boolean ret = Python.type(this).toString().equals("<class 'NoneType'>") && toString().equals("None");
|
||||||
|
Py_DecRef(type.nativePythonObject);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void del() {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
if (owned && nativePythonObject != null && !PythonGC.isWatching()) {
|
||||||
|
Py_DecRef(nativePythonObject);
|
||||||
|
nativePythonObject = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PythonObject callWithArgs(PythonObject args) {
|
||||||
|
return callWithArgsAndKwargs(args, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PythonObject callWithKwargs(PythonObject kwargs) {
|
||||||
|
if (!Python.callable(this)) {
|
||||||
|
throw new PythonException("Object is not callable: " + toString());
|
||||||
|
}
|
||||||
|
PyObject tuple = PyTuple_New(0);
|
||||||
|
PyObject dict = kwargs.nativePythonObject;
|
||||||
|
if (PyObject_IsInstance(dict, new PyObject(PyDict_Type())) != 1) {
|
||||||
|
throw new PythonException("Expected kwargs to be dict. Received: " + kwargs.toString());
|
||||||
|
}
|
||||||
|
PythonObject ret = new PythonObject(PyObject_Call(nativePythonObject, tuple, dict));
|
||||||
|
Py_DecRef(tuple);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PythonObject callWithArgsAndKwargs(PythonObject args, PythonObject kwargs) {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
PyObject tuple = null;
|
||||||
|
boolean ownsTuple = false;
|
||||||
|
try {
|
||||||
|
if (!Python.callable(this)) {
|
||||||
|
throw new PythonException("Object is not callable: " + toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PyObject_IsInstance(args.nativePythonObject, new PyObject(PyTuple_Type())) == 1) {
|
||||||
|
tuple = args.nativePythonObject;
|
||||||
|
} else if (PyObject_IsInstance(args.nativePythonObject, new PyObject(PyList_Type())) == 1) {
|
||||||
|
tuple = PyList_AsTuple(args.nativePythonObject);
|
||||||
|
ownsTuple = true;
|
||||||
|
} else {
|
||||||
|
throw new PythonException("Expected args to be tuple or list. Received: " + args.toString());
|
||||||
|
}
|
||||||
|
if (kwargs != null && PyObject_IsInstance(kwargs.nativePythonObject, new PyObject(PyDict_Type())) != 1) {
|
||||||
|
throw new PythonException("Expected kwargs to be dict. Received: " + kwargs.toString());
|
||||||
|
}
|
||||||
|
return new PythonObject(PyObject_Call(nativePythonObject, tuple, kwargs == null ? null : kwargs.nativePythonObject));
|
||||||
|
} finally {
|
||||||
|
if (ownsTuple) Py_DecRef(tuple);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public PythonObject call(Object... args) {
|
||||||
|
return callWithArgsAndKwargs(Arrays.asList(args), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PythonObject callWithArgs(List args) {
|
||||||
|
return call(args, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PythonObject callWithKwargs(Map kwargs) {
|
||||||
|
return call(null, kwargs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PythonObject callWithArgsAndKwargs(List args, Map kwargs) {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
try (PythonGC _ = PythonGC.watch()) {
|
||||||
|
if (!Python.callable(this)) {
|
||||||
|
throw new PythonException("Object is not callable: " + toString());
|
||||||
|
}
|
||||||
|
PythonObject pyArgs;
|
||||||
|
PythonObject pyKwargs;
|
||||||
|
if (args == null) {
|
||||||
|
pyArgs = new PythonObject(PyTuple_New(0));
|
||||||
|
} else {
|
||||||
|
PythonObject argsList = PythonTypes.convert(args);
|
||||||
|
pyArgs = new PythonObject(PyList_AsTuple(argsList.getNativePythonObject()));
|
||||||
|
}
|
||||||
|
if (kwargs == null) {
|
||||||
|
pyKwargs = null;
|
||||||
|
} else {
|
||||||
|
pyKwargs = PythonTypes.convert(kwargs);
|
||||||
|
}
|
||||||
|
PythonObject ret = new PythonObject(
|
||||||
|
PyObject_Call(
|
||||||
|
nativePythonObject,
|
||||||
|
pyArgs.nativePythonObject,
|
||||||
|
pyKwargs == null ? null : pyKwargs.nativePythonObject
|
||||||
|
)
|
||||||
|
);
|
||||||
|
PythonGC.keep(ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public PythonObject attr(String attrName) {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
return new PythonObject(PyObject_GetAttrString(nativePythonObject, attrName));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public PythonObject(Object javaObject) {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
if (javaObject instanceof PythonObject) {
|
||||||
|
owned = false;
|
||||||
|
nativePythonObject = ((PythonObject) javaObject).nativePythonObject;
|
||||||
|
} else {
|
||||||
|
try (PythonGC _ = PythonGC.pause()) {
|
||||||
|
nativePythonObject = PythonTypes.convert(javaObject).getNativePythonObject();
|
||||||
|
}
|
||||||
|
PythonGC.register(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public int toInt() {
|
||||||
|
return PythonTypes.INT.toJava(this).intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long toLong() {
|
||||||
|
return PythonTypes.INT.toJava(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float toFloat() {
|
||||||
|
return PythonTypes.FLOAT.toJava(this).floatValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public double toDouble() {
|
||||||
|
return PythonTypes.FLOAT.toJava(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean toBoolean() {
|
||||||
|
return PythonTypes.BOOL.toJava(this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public List toList() {
|
||||||
|
return PythonTypes.LIST.toJava(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map toMap() {
|
||||||
|
return PythonTypes.DICT.toJava(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PythonObject get(int key) {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
return new PythonObject(PyObject_GetItem(nativePythonObject, PyLong_FromLong(key)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public PythonObject get(String key) {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
return new PythonObject(PyObject_GetItem(nativePythonObject, PyUnicode_FromString(key)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public PythonObject get(PythonObject key) {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
return new PythonObject(PyObject_GetItem(nativePythonObject, key.nativePythonObject));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(PythonObject key, PythonObject value) {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
PyObject_SetItem(nativePythonObject, key.nativePythonObject, value.nativePythonObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2020 Konduit K.K.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Apache License, Version 2.0 which is available at
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
package org.eclipse.python4j;
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class PythonType<T> {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private final Class<T> javaType;
|
||||||
|
|
||||||
|
public PythonType(String name, Class<T> javaType) {
|
||||||
|
this.name = name;
|
||||||
|
this.javaType = javaType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T adapt(Object javaObject) throws PythonException {
|
||||||
|
return (T) javaObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract T toJava(PythonObject pythonObject);
|
||||||
|
|
||||||
|
public abstract PythonObject toPython(T javaObject);
|
||||||
|
|
||||||
|
public boolean accepts(Object javaObject) {
|
||||||
|
return javaType.isAssignableFrom(javaObject.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,344 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2020 Konduit K.K.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Apache License, Version 2.0 which is available at
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
package org.eclipse.python4j;
|
||||||
|
|
||||||
|
|
||||||
|
import org.bytedeco.cpython.PyObject;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.bytedeco.cpython.global.python.*;
|
||||||
|
import static org.bytedeco.cpython.global.python.Py_DecRef;
|
||||||
|
|
||||||
|
public class PythonTypes {
|
||||||
|
|
||||||
|
|
||||||
|
private static List<PythonType> getPrimitiveTypes() {
|
||||||
|
return Arrays.<PythonType>asList(STR, INT, FLOAT, BOOL);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<PythonType> getCollectionTypes() {
|
||||||
|
return Arrays.<PythonType>asList(LIST, DICT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<PythonType> getExternalTypes() {
|
||||||
|
//TODO service loader
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<PythonType> get() {
|
||||||
|
List<PythonType> ret = new ArrayList<>();
|
||||||
|
ret.addAll(getPrimitiveTypes());
|
||||||
|
ret.addAll(getCollectionTypes());
|
||||||
|
ret.addAll(getExternalTypes());
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PythonType get(String name) {
|
||||||
|
for (PythonType pt : get()) {
|
||||||
|
if (pt.getName().equals(name)) { // TODO use map instead?
|
||||||
|
return pt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new PythonException("Unknown python type: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PythonType getPythonTypeForJavaObject(Object javaObject) {
|
||||||
|
for (PythonType pt : get()) {
|
||||||
|
if (pt.accepts(javaObject)) {
|
||||||
|
return pt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new PythonException("Unable to find python type for java type: " + javaObject.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PythonType getPythonTypeForPythonObject(PythonObject pythonObject) {
|
||||||
|
PyObject pyType = PyObject_Type(pythonObject.getNativePythonObject());
|
||||||
|
try {
|
||||||
|
String pyTypeStr = PythonTypes.STR.toJava(new PythonObject(pyType, false));
|
||||||
|
|
||||||
|
for (PythonType pt : get()) {
|
||||||
|
String pyTypeStr2 = "<class '" + pt.getName() + "'>";
|
||||||
|
if (pyTypeStr.equals(pyTypeStr2)) {
|
||||||
|
return pt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new PythonException("Unable to find converter for python object of type " + pyTypeStr);
|
||||||
|
} finally {
|
||||||
|
Py_DecRef(pyType);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PythonObject convert(Object javaObject) {
|
||||||
|
PythonType pt = getPythonTypeForJavaObject(javaObject);
|
||||||
|
return pt.toPython(pt.adapt(javaObject));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final PythonType<String> STR = new PythonType<String>("str", String.class) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String adapt(Object javaObject) {
|
||||||
|
if (javaObject instanceof String) {
|
||||||
|
return (String) javaObject;
|
||||||
|
}
|
||||||
|
throw new PythonException("Cannot cast object of type " + javaObject.getClass().getName() + " to String");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toJava(PythonObject pythonObject) {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
PyObject repr = PyObject_Str(pythonObject.getNativePythonObject());
|
||||||
|
PyObject str = PyUnicode_AsEncodedString(repr, "utf-8", "~E~");
|
||||||
|
String jstr = PyBytes_AsString(str).getString();
|
||||||
|
Py_DecRef(repr);
|
||||||
|
Py_DecRef(str);
|
||||||
|
return jstr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PythonObject toPython(String javaObject) {
|
||||||
|
return new PythonObject(PyUnicode_FromString(javaObject));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final PythonType<Long> INT = new PythonType<Long>("int", Long.class) {
|
||||||
|
@Override
|
||||||
|
public Long adapt(Object javaObject) {
|
||||||
|
if (javaObject instanceof Number) {
|
||||||
|
return ((Number) javaObject).longValue();
|
||||||
|
}
|
||||||
|
throw new PythonException("Cannot cast object of type " + javaObject.getClass().getName() + " to Long");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long toJava(PythonObject pythonObject) {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
long val = PyLong_AsLong(pythonObject.getNativePythonObject());
|
||||||
|
if (val == -1 && PyErr_Occurred() != null) {
|
||||||
|
throw new PythonException("Could not convert value to int: " + pythonObject.toString());
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean accepts(Object javaObject) {
|
||||||
|
return (javaObject instanceof Integer) || (javaObject instanceof Long);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PythonObject toPython(Long javaObject) {
|
||||||
|
return new PythonObject(PyLong_FromLong(javaObject));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final PythonType<Double> FLOAT = new PythonType<Double>("float", Double.class) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Double adapt(Object javaObject) {
|
||||||
|
if (javaObject instanceof Number) {
|
||||||
|
return ((Number) javaObject).doubleValue();
|
||||||
|
}
|
||||||
|
throw new PythonException("Cannot cast object of type " + javaObject.getClass().getName() + " to Long");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Double toJava(PythonObject pythonObject) {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
double val = PyFloat_AsDouble(pythonObject.getNativePythonObject());
|
||||||
|
if (val == -1 && PyErr_Occurred() != null) {
|
||||||
|
throw new PythonException("Could not convert value to float: " + pythonObject.toString());
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean accepts(Object javaObject) {
|
||||||
|
return (javaObject instanceof Float) || (javaObject instanceof Double);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PythonObject toPython(Double javaObject) {
|
||||||
|
return new PythonObject(PyFloat_FromDouble(javaObject));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public static final PythonType<Boolean> BOOL = new PythonType<Boolean>("bool", Boolean.class) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean adapt(Object javaObject) {
|
||||||
|
if (javaObject instanceof Boolean) {
|
||||||
|
return (Boolean) javaObject;
|
||||||
|
}
|
||||||
|
throw new PythonException("Cannot cast object of type " + javaObject.getClass().getName() + " to Boolean");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean toJava(PythonObject pythonObject) {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
PyObject builtins = PyImport_ImportModule("builtins");
|
||||||
|
PyObject boolF = PyObject_GetAttrString(builtins, "bool");
|
||||||
|
|
||||||
|
PythonObject bool = new PythonObject(boolF, false).call(pythonObject);
|
||||||
|
boolean ret = PyLong_AsLong(bool.getNativePythonObject()) > 0;
|
||||||
|
bool.del();
|
||||||
|
Py_DecRef(boolF);
|
||||||
|
Py_DecRef(builtins);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PythonObject toPython(Boolean javaObject) {
|
||||||
|
return new PythonObject(PyBool_FromLong(javaObject ? 1 : 0));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public static final PythonType<List> LIST = new PythonType<List>("list", List.class) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List adapt(Object javaObject) {
|
||||||
|
if (javaObject instanceof List) {
|
||||||
|
return (List) javaObject;
|
||||||
|
} else if (javaObject instanceof Object[]) {
|
||||||
|
return Arrays.asList((Object[]) javaObject);
|
||||||
|
} else {
|
||||||
|
throw new PythonException("Cannot cast object of type " + javaObject.getClass().getName() + " to List");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List toJava(PythonObject pythonObject) {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
List ret = new ArrayList();
|
||||||
|
long n = PyObject_Size(pythonObject.getNativePythonObject());
|
||||||
|
if (n < 0) {
|
||||||
|
throw new PythonException("Object cannot be interpreted as a List");
|
||||||
|
}
|
||||||
|
for (long i = 0; i < n; i++) {
|
||||||
|
PyObject pyIndex = PyLong_FromLong(i);
|
||||||
|
PyObject pyItem = PyObject_GetItem(pythonObject.getNativePythonObject(),
|
||||||
|
pyIndex);
|
||||||
|
Py_DecRef(pyIndex);
|
||||||
|
PythonType pyItemType = getPythonTypeForPythonObject(new PythonObject(pyItem, false));
|
||||||
|
ret.add(pyItemType.toJava(new PythonObject(pyItem, false)));
|
||||||
|
Py_DecRef(pyItem);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PythonObject toPython(List javaObject) {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
PyObject pyList = PyList_New(javaObject.size());
|
||||||
|
for (int i = 0; i < javaObject.size(); i++) {
|
||||||
|
Object item = javaObject.get(i);
|
||||||
|
PythonObject pyItem;
|
||||||
|
boolean owned;
|
||||||
|
if (item instanceof PythonObject) {
|
||||||
|
pyItem = (PythonObject) item;
|
||||||
|
owned = false;
|
||||||
|
} else if (item instanceof PyObject) {
|
||||||
|
pyItem = new PythonObject((PyObject) item, false);
|
||||||
|
owned = false;
|
||||||
|
} else {
|
||||||
|
pyItem = PythonTypes.convert(item);
|
||||||
|
owned = true;
|
||||||
|
}
|
||||||
|
Py_IncRef(pyItem.getNativePythonObject()); // reference will be stolen by PyList_SetItem()
|
||||||
|
PyList_SetItem(pyList, i, pyItem.getNativePythonObject());
|
||||||
|
if (owned) pyItem.del();
|
||||||
|
}
|
||||||
|
return new PythonObject(pyList);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final PythonType<Map> DICT = new PythonType<Map>("dict", Map.class) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map adapt(Object javaObject) {
|
||||||
|
if (javaObject instanceof Map) {
|
||||||
|
return (Map) javaObject;
|
||||||
|
}
|
||||||
|
throw new PythonException("Cannot cast object of type " + javaObject.getClass().getName() + " to Map");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map toJava(PythonObject pythonObject) {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
HashMap ret = new HashMap();
|
||||||
|
PyObject dictType = new PyObject(PyDict_Type());
|
||||||
|
if (PyObject_IsInstance(pythonObject.getNativePythonObject(), dictType) != 1) {
|
||||||
|
throw new PythonException("Expected dict, received: " + pythonObject.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject keys = PyDict_Keys(pythonObject.getNativePythonObject());
|
||||||
|
PyObject keysIter = PyObject_GetIter(keys);
|
||||||
|
PyObject vals = PyDict_Values(pythonObject.getNativePythonObject());
|
||||||
|
PyObject valsIter = PyObject_GetIter(vals);
|
||||||
|
try {
|
||||||
|
long n = PyObject_Size(pythonObject.getNativePythonObject());
|
||||||
|
for (long i = 0; i < n; i++) {
|
||||||
|
PythonObject pyKey = new PythonObject(PyIter_Next(keysIter), false);
|
||||||
|
PythonObject pyVal = new PythonObject(PyIter_Next(valsIter), false);
|
||||||
|
PythonType pyKeyType = getPythonTypeForPythonObject(pyKey);
|
||||||
|
PythonType pyValType = getPythonTypeForPythonObject(pyVal);
|
||||||
|
ret.put(pyKeyType.toJava(pyKey), pyValType.toJava(pyVal));
|
||||||
|
Py_DecRef(pyKey.getNativePythonObject());
|
||||||
|
Py_DecRef(pyVal.getNativePythonObject());
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
Py_DecRef(keysIter);
|
||||||
|
Py_DecRef(valsIter);
|
||||||
|
Py_DecRef(keys);
|
||||||
|
Py_DecRef(vals);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PythonObject toPython(Map javaObject) {
|
||||||
|
PythonGIL.assertThreadSafe();
|
||||||
|
PyObject pyDict = PyDict_New();
|
||||||
|
for (Object k : javaObject.keySet()) {
|
||||||
|
PythonObject pyKey;
|
||||||
|
if (k instanceof PythonObject) {
|
||||||
|
pyKey = (PythonObject) k;
|
||||||
|
} else if (k instanceof PyObject) {
|
||||||
|
pyKey = new PythonObject((PyObject) k);
|
||||||
|
} else {
|
||||||
|
pyKey = PythonTypes.convert(k);
|
||||||
|
}
|
||||||
|
Object v = javaObject.get(k);
|
||||||
|
PythonObject pyVal;
|
||||||
|
pyVal = PythonTypes.convert(v);
|
||||||
|
int errCode = PyDict_SetItem(pyDict, pyKey.getNativePythonObject(), pyVal.getNativePythonObject());
|
||||||
|
if (errCode != 0) {
|
||||||
|
String keyStr = pyKey.toString();
|
||||||
|
pyKey.del();
|
||||||
|
pyVal.del();
|
||||||
|
throw new PythonException("Unable to create python dictionary. Unhashable key: " + keyStr);
|
||||||
|
}
|
||||||
|
pyKey.del();
|
||||||
|
pyVal.del();
|
||||||
|
}
|
||||||
|
return new PythonObject(pyDict);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2020 Konduit K.K.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Apache License, Version 2.0 which is available at
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
package org.eclipse.python4j;
|
||||||
|
|
||||||
|
@lombok.Data
|
||||||
|
public class PythonVariable<T> {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private String type;
|
||||||
|
private T value;
|
||||||
|
|
||||||
|
private static boolean validateVariableName(String s) {
|
||||||
|
if (s.isEmpty()) return false;
|
||||||
|
if (!Character.isJavaIdentifierStart(s.charAt(0))) return false;
|
||||||
|
for (int i = 1; i < s.length(); i++)
|
||||||
|
if (!Character.isJavaIdentifierPart(s.charAt(i)))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PythonVariable(String name, PythonType<T> type, Object value) {
|
||||||
|
if (!validateVariableName(name)) {
|
||||||
|
throw new PythonException("Invalid identifier: " + name);
|
||||||
|
}
|
||||||
|
this.name = name;
|
||||||
|
this.type = type.getName();
|
||||||
|
setValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PythonVariable(String name, PythonType<T> type) {
|
||||||
|
this(name, type, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PythonType<T> getType() {
|
||||||
|
return PythonTypes.get(this.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getValue() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(Object value) {
|
||||||
|
this.value = value == null ? null : getType().adapt(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PythonObject getPythonObject() {
|
||||||
|
return getType().toPython(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
# /*******************************************************************************
|
||||||
|
# * Copyright (c) 2019 Konduit K.K.
|
||||||
|
# *
|
||||||
|
# * This program and the accompanying materials are made available under the
|
||||||
|
# * terms of the Apache License, Version 2.0 which is available at
|
||||||
|
# * https://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
# *
|
||||||
|
# * Unless required by applicable law or agreed to in writing, software
|
||||||
|
# * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# * License for the specific language governing permissions and limitations
|
||||||
|
# * under the License.
|
||||||
|
# *
|
||||||
|
# * SPDX-License-Identifier: Apache-2.0
|
||||||
|
# ******************************************************************************/
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
import json
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
__python_exception__ = ""
|
||||||
|
try:
|
||||||
|
pass
|
||||||
|
sys.stdout.flush()
|
||||||
|
sys.stderr.flush()
|
||||||
|
except Exception as ex:
|
||||||
|
__python_exception__ = ex
|
||||||
|
try:
|
||||||
|
exc_info = sys.exc_info()
|
||||||
|
finally:
|
||||||
|
print(ex)
|
||||||
|
traceback.print_exception(*exc_info)
|
||||||
|
sys.stdout.flush()
|
||||||
|
sys.stderr.flush()
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2020 Konduit K.K.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Apache License, Version 2.0 which is available at
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
import org.eclipse.python4j.*;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.NotThreadSafe;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@NotThreadSafe
|
||||||
|
public class PythonBasicExecutionTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSimpleExec() {
|
||||||
|
String code = "print('Hello World')";
|
||||||
|
PythonExecutioner.exec(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBadCode() throws Exception {
|
||||||
|
try {
|
||||||
|
String code = "printx('Hello world')";
|
||||||
|
PythonExecutioner.exec(code);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Assert.assertEquals("NameError: name 'printx' is not defined", e.getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new Exception("Bad code did not throw!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExecWithInputs() {
|
||||||
|
List<PythonVariable> inputs = new ArrayList<>();
|
||||||
|
inputs.add(new PythonVariable<>("x", PythonTypes.STR, "Hello "));
|
||||||
|
inputs.add(new PythonVariable<>("y", PythonTypes.STR, "World"));
|
||||||
|
String code = "print(x + y)";
|
||||||
|
PythonExecutioner.exec(code, inputs, null);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExecWithInputsAndOutputs() {
|
||||||
|
List<PythonVariable> inputs = new ArrayList<>();
|
||||||
|
inputs.add(new PythonVariable<>("x", PythonTypes.STR, "Hello "));
|
||||||
|
inputs.add(new PythonVariable<>("y", PythonTypes.STR, "World"));
|
||||||
|
PythonVariable out = new PythonVariable<>("z", PythonTypes.STR);
|
||||||
|
String code = "z = x + y";
|
||||||
|
PythonExecutioner.exec(code, inputs, Collections.singletonList(out));
|
||||||
|
Assert.assertEquals("Hello World", out.getValue());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExecAndReturnAllVariables() {
|
||||||
|
PythonContextManager.reset();
|
||||||
|
String code = "a = 5\nb = '10'\nc = 20.0";
|
||||||
|
List<PythonVariable> vars = PythonExecutioner.execAndReturnAllVariables(code);
|
||||||
|
|
||||||
|
Assert.assertEquals("a", vars.get(0).getName());
|
||||||
|
Assert.assertEquals(PythonTypes.INT, vars.get(0).getType());
|
||||||
|
Assert.assertEquals(5L, (long) vars.get(0).getValue());
|
||||||
|
|
||||||
|
Assert.assertEquals("b", vars.get(1).getName());
|
||||||
|
Assert.assertEquals(PythonTypes.STR, vars.get(1).getType());
|
||||||
|
Assert.assertEquals("10", vars.get(1).getValue().toString());
|
||||||
|
|
||||||
|
Assert.assertEquals("c", vars.get(2).getName());
|
||||||
|
Assert.assertEquals(PythonTypes.FLOAT, vars.get(2).getType());
|
||||||
|
Assert.assertEquals(20.0, (double) vars.get(2).getValue(), 1e-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExecWithInputsAndReturnAllVariables() {
|
||||||
|
PythonContextManager.reset();
|
||||||
|
List<PythonVariable> inputs = new ArrayList<>();
|
||||||
|
inputs.add(new PythonVariable<>("a", PythonTypes.INT, 5));
|
||||||
|
String code = "b = '10'\nc = 20.0 + a";
|
||||||
|
List<PythonVariable> vars = PythonExecutioner.execAndReturnAllVariables(code, inputs);
|
||||||
|
|
||||||
|
Assert.assertEquals("a", vars.get(0).getName());
|
||||||
|
Assert.assertEquals(PythonTypes.INT, vars.get(0).getType());
|
||||||
|
Assert.assertEquals(5L, (long) vars.get(0).getValue());
|
||||||
|
|
||||||
|
Assert.assertEquals("b", vars.get(1).getName());
|
||||||
|
Assert.assertEquals(PythonTypes.STR, vars.get(1).getType());
|
||||||
|
Assert.assertEquals("10", vars.get(1).getValue().toString());
|
||||||
|
|
||||||
|
Assert.assertEquals("c", vars.get(2).getName());
|
||||||
|
Assert.assertEquals(PythonTypes.FLOAT, vars.get(2).getType());
|
||||||
|
Assert.assertEquals(25.0, (double) vars.get(2).getValue(), 1e-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2020 Konduit K.K.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Apache License, Version 2.0 which is available at
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
import org.eclipse.python4j.PythonException;
|
||||||
|
import org.eclipse.python4j.PythonObject;
|
||||||
|
import org.eclipse.python4j.PythonTypes;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
|
||||||
|
@javax.annotation.concurrent.NotThreadSafe
|
||||||
|
public class PythonCollectionsTest {
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPythonDictFromMap() throws PythonException {
|
||||||
|
Map map = new HashMap();
|
||||||
|
map.put("a", 1);
|
||||||
|
map.put(1, "a");
|
||||||
|
map.put("list1", Arrays.asList(1, 2.0, 3, 4f));
|
||||||
|
Map innerMap = new HashMap();
|
||||||
|
innerMap.put("b", 2);
|
||||||
|
innerMap.put(2, "b");
|
||||||
|
map.put("innermap", innerMap);
|
||||||
|
map.put("list2", Arrays.asList(4, "5", innerMap, false, true));
|
||||||
|
PythonObject dict = PythonTypes.convert(map);
|
||||||
|
Map map2 = PythonTypes.DICT.toJava(dict);
|
||||||
|
Assert.assertEquals(map.toString(), map2.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPythonListFromList() throws PythonException{
|
||||||
|
List<Object> list = new ArrayList<>();
|
||||||
|
list.add(1);
|
||||||
|
list.add("2");
|
||||||
|
list.add(Arrays.asList("a", 1.0, 2f, 10, true, false));
|
||||||
|
Map map = new HashMap();
|
||||||
|
map.put("a", 1);
|
||||||
|
map.put(1, "a");
|
||||||
|
map.put("list1", Arrays.asList(1, 2.0, 3, 4f));
|
||||||
|
list.add(map);
|
||||||
|
PythonObject dict = PythonTypes.convert(list);
|
||||||
|
List list2 = PythonTypes.LIST.toJava(dict);
|
||||||
|
Assert.assertEquals(list.toString(), list2.toString());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2020 Konduit K.K.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Apache License, Version 2.0 which is available at
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
import org.eclipse.python4j.Python;
|
||||||
|
import org.eclipse.python4j.PythonContextManager;
|
||||||
|
import org.eclipse.python4j.PythonExecutioner;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import javax.annotation.concurrent.NotThreadSafe;
|
||||||
|
|
||||||
|
@NotThreadSafe
|
||||||
|
public class PythonContextManagerTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInt() throws Exception{
|
||||||
|
Python.setContext("context1");
|
||||||
|
Python.exec("a = 1");
|
||||||
|
Python.setContext("context2");
|
||||||
|
Python.exec("a = 2");
|
||||||
|
Python.setContext("context3");
|
||||||
|
Python.exec("a = 3");
|
||||||
|
|
||||||
|
|
||||||
|
Python.setContext("context1");
|
||||||
|
Assert.assertEquals(1, PythonExecutioner.getVariable("a").toInt());
|
||||||
|
|
||||||
|
Python.setContext("context2");
|
||||||
|
Assert.assertEquals(2, PythonExecutioner.getVariable("a").toInt());
|
||||||
|
|
||||||
|
Python.setContext("context3");
|
||||||
|
Assert.assertEquals(3, PythonExecutioner.getVariable("a").toInt());
|
||||||
|
|
||||||
|
PythonContextManager.deleteNonMainContexts();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2020 Konduit K.K.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Apache License, Version 2.0 which is available at
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
import org.eclipse.python4j.Python;
|
||||||
|
import org.eclipse.python4j.PythonGC;
|
||||||
|
import org.eclipse.python4j.PythonObject;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.NotThreadSafe;
|
||||||
|
|
||||||
|
|
||||||
|
@NotThreadSafe
|
||||||
|
public class PythonGCTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGC() throws Exception{
|
||||||
|
PythonObject gcModule = Python.importModule("gc");
|
||||||
|
PythonObject getObjects = gcModule.attr("get_objects");
|
||||||
|
PythonObject pyObjCount1 = Python.len(getObjects.call());
|
||||||
|
long objCount1 = pyObjCount1.toLong();
|
||||||
|
PythonObject pyList = Python.list();
|
||||||
|
pyList.attr("append").call("a");
|
||||||
|
pyList.attr("append").call(1.0);
|
||||||
|
pyList.attr("append").call(true);
|
||||||
|
PythonObject pyObjCount2 = Python.len(getObjects.call());
|
||||||
|
long objCount2 = pyObjCount2.toLong();
|
||||||
|
long diff = objCount2 - objCount1;
|
||||||
|
Assert.assertTrue(diff > 2);
|
||||||
|
try(PythonGC gc = PythonGC.watch()){
|
||||||
|
PythonObject pyList2 = Python.list();
|
||||||
|
pyList2.attr("append").call("a");
|
||||||
|
pyList2.attr("append").call(1.0);
|
||||||
|
pyList2.attr("append").call(true);
|
||||||
|
}
|
||||||
|
PythonObject pyObjCount3 = Python.len(getObjects.call());
|
||||||
|
long objCount3 = pyObjCount3.toLong();
|
||||||
|
diff = objCount3 - objCount2;
|
||||||
|
Assert.assertEquals(2, diff);// 2 objects created during function call
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,287 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2020 Konduit K.K.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Apache License, Version 2.0 which is available at
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
import org.eclipse.python4j.PythonContextManager;
|
||||||
|
import org.eclipse.python4j.PythonJob;
|
||||||
|
import org.eclipse.python4j.PythonTypes;
|
||||||
|
import org.eclipse.python4j.PythonVariable;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
|
||||||
|
@javax.annotation.concurrent.NotThreadSafe
|
||||||
|
public class PythonJobTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPythonJobBasic() throws Exception{
|
||||||
|
PythonContextManager.deleteNonMainContexts();
|
||||||
|
|
||||||
|
String code = "c = a + b";
|
||||||
|
PythonJob job = new PythonJob("job1", code, false);
|
||||||
|
|
||||||
|
List<PythonVariable> inputs = new ArrayList<>();
|
||||||
|
inputs.add(new PythonVariable<>("a", PythonTypes.INT, 2));
|
||||||
|
inputs.add(new PythonVariable<>("b", PythonTypes.INT, 3));
|
||||||
|
|
||||||
|
List<PythonVariable> outputs = new ArrayList<>();
|
||||||
|
outputs.add(new PythonVariable<>("c", PythonTypes.INT));
|
||||||
|
|
||||||
|
|
||||||
|
job.exec(inputs, outputs);
|
||||||
|
assertEquals("c", outputs.get(0).getName());
|
||||||
|
assertEquals(5L, (long)outputs.get(0).getValue());
|
||||||
|
|
||||||
|
inputs = new ArrayList<>();
|
||||||
|
inputs.add(new PythonVariable<>("a", PythonTypes.FLOAT, 3.0));
|
||||||
|
inputs.add(new PythonVariable<>("b", PythonTypes.FLOAT, 4.0));
|
||||||
|
|
||||||
|
outputs = new ArrayList<>();
|
||||||
|
outputs.add(new PythonVariable<>("c", PythonTypes.FLOAT));
|
||||||
|
|
||||||
|
|
||||||
|
job.exec(inputs, outputs);
|
||||||
|
|
||||||
|
assertEquals("c", outputs.get(0).getName());
|
||||||
|
assertEquals(7.0, (double)outputs.get(0).getValue(), 1e-5);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPythonJobReturnAllVariables()throws Exception{
|
||||||
|
PythonContextManager.deleteNonMainContexts();
|
||||||
|
|
||||||
|
String code = "c = a + b";
|
||||||
|
PythonJob job = new PythonJob("job1", code, false);
|
||||||
|
|
||||||
|
List<PythonVariable> inputs = new ArrayList<>();
|
||||||
|
inputs.add(new PythonVariable<>("a", PythonTypes.INT, 2));
|
||||||
|
inputs.add(new PythonVariable<>("b", PythonTypes.INT, 3));
|
||||||
|
|
||||||
|
|
||||||
|
List<PythonVariable> outputs = job.execAndReturnAllVariables(inputs);
|
||||||
|
|
||||||
|
|
||||||
|
assertEquals("a", outputs.get(0).getName());
|
||||||
|
assertEquals(2L, (long)outputs.get(0).getValue());
|
||||||
|
assertEquals("b", outputs.get(1).getName());
|
||||||
|
assertEquals(3L, (long)outputs.get(1).getValue());
|
||||||
|
assertEquals("c", outputs.get(2).getName());
|
||||||
|
assertEquals(5L, (long)outputs.get(2).getValue());
|
||||||
|
|
||||||
|
inputs = new ArrayList<>();
|
||||||
|
inputs.add(new PythonVariable<>("a", PythonTypes.FLOAT, 3.0));
|
||||||
|
inputs.add(new PythonVariable<>("b", PythonTypes.FLOAT, 4.0));
|
||||||
|
outputs = job.execAndReturnAllVariables(inputs);
|
||||||
|
assertEquals("a", outputs.get(0).getName());
|
||||||
|
assertEquals(3.0, (double)outputs.get(0).getValue(), 1e-5);
|
||||||
|
assertEquals("b", outputs.get(1).getName());
|
||||||
|
assertEquals(4.0, (double)outputs.get(1).getValue(), 1e-5);
|
||||||
|
assertEquals("c", outputs.get(2).getName());
|
||||||
|
assertEquals(7.0, (double)outputs.get(2).getValue(), 1e-5);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultiplePythonJobsParallel()throws Exception{
|
||||||
|
PythonContextManager.deleteNonMainContexts();
|
||||||
|
String code1 = "c = a + b";
|
||||||
|
PythonJob job1 = new PythonJob("job1", code1, false);
|
||||||
|
|
||||||
|
String code2 = "c = a - b";
|
||||||
|
PythonJob job2 = new PythonJob("job2", code2, false);
|
||||||
|
|
||||||
|
List<PythonVariable> inputs = new ArrayList<>();
|
||||||
|
inputs.add(new PythonVariable<>("a", PythonTypes.INT, 2));
|
||||||
|
inputs.add(new PythonVariable<>("b", PythonTypes.INT, 3));
|
||||||
|
|
||||||
|
|
||||||
|
List<PythonVariable> outputs = new ArrayList<>();
|
||||||
|
outputs.add(new PythonVariable<>("c", PythonTypes.INT));
|
||||||
|
|
||||||
|
job1.exec(inputs, outputs);
|
||||||
|
|
||||||
|
assertEquals("c", outputs.get(0).getName());
|
||||||
|
assertEquals(5L, (long)outputs.get(0).getValue());
|
||||||
|
|
||||||
|
|
||||||
|
job2.exec(inputs, outputs);
|
||||||
|
|
||||||
|
assertEquals("c", outputs.get(0).getName());
|
||||||
|
assertEquals(-1L, (long)outputs.get(0).getValue());
|
||||||
|
|
||||||
|
inputs = new ArrayList<>();
|
||||||
|
inputs.add(new PythonVariable<>("a", PythonTypes.FLOAT, 3.0));
|
||||||
|
inputs.add(new PythonVariable<>("b", PythonTypes.FLOAT, 4.0));
|
||||||
|
|
||||||
|
outputs = new ArrayList<>();
|
||||||
|
outputs.add(new PythonVariable<>("c", PythonTypes.FLOAT));
|
||||||
|
|
||||||
|
|
||||||
|
job1.exec(inputs, outputs);
|
||||||
|
|
||||||
|
assertEquals("c", outputs.get(0).getName());
|
||||||
|
assertEquals(7.0, (double)outputs.get(0).getValue(), 1e-5);
|
||||||
|
|
||||||
|
job2.exec(inputs, outputs);
|
||||||
|
|
||||||
|
assertEquals("c", outputs.get(0).getName());
|
||||||
|
assertEquals(-1., (double)outputs.get(0).getValue(), 1e-5);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPythonJobSetupRun()throws Exception{
|
||||||
|
|
||||||
|
PythonContextManager.deleteNonMainContexts();
|
||||||
|
String code = "five=None\n" +
|
||||||
|
"def setup():\n" +
|
||||||
|
" global five\n"+
|
||||||
|
" five = 5\n\n" +
|
||||||
|
"def run(a, b):\n" +
|
||||||
|
" c = a + b + five\n"+
|
||||||
|
" return {'c':c}\n\n";
|
||||||
|
PythonJob job = new PythonJob("job1", code, true);
|
||||||
|
|
||||||
|
List<PythonVariable> inputs = new ArrayList<>();
|
||||||
|
inputs.add(new PythonVariable<>("a", PythonTypes.INT, 2));
|
||||||
|
inputs.add(new PythonVariable<>("b", PythonTypes.INT, 3));
|
||||||
|
|
||||||
|
List<PythonVariable> outputs = new ArrayList<>();
|
||||||
|
outputs.add(new PythonVariable<>("c", PythonTypes.INT));
|
||||||
|
job.exec(inputs, outputs);
|
||||||
|
|
||||||
|
assertEquals("c", outputs.get(0).getName());
|
||||||
|
assertEquals(10L, (long)outputs.get(0).getValue());
|
||||||
|
|
||||||
|
|
||||||
|
inputs = new ArrayList<>();
|
||||||
|
inputs.add(new PythonVariable<>("a", PythonTypes.FLOAT, 3.0));
|
||||||
|
inputs.add(new PythonVariable<>("b", PythonTypes.FLOAT, 4.0));
|
||||||
|
|
||||||
|
|
||||||
|
outputs = new ArrayList<>();
|
||||||
|
outputs.add(new PythonVariable<>("c", PythonTypes.FLOAT));
|
||||||
|
|
||||||
|
job.exec(inputs, outputs);
|
||||||
|
|
||||||
|
assertEquals("c", outputs.get(0).getName());
|
||||||
|
assertEquals(12.0, (double)outputs.get(0).getValue(), 1e-5);
|
||||||
|
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
public void testPythonJobSetupRunAndReturnAllVariables()throws Exception{
|
||||||
|
PythonContextManager.deleteNonMainContexts();
|
||||||
|
String code = "five=None\n" +
|
||||||
|
"c=None\n"+
|
||||||
|
"def setup():\n" +
|
||||||
|
" global five\n"+
|
||||||
|
" five = 5\n\n" +
|
||||||
|
"def run(a, b):\n" +
|
||||||
|
" global c\n" +
|
||||||
|
" c = a + b + five\n";
|
||||||
|
PythonJob job = new PythonJob("job1", code, true);
|
||||||
|
|
||||||
|
List<PythonVariable> inputs = new ArrayList<>();
|
||||||
|
inputs.add(new PythonVariable<>("a", PythonTypes.INT, 2));
|
||||||
|
inputs.add(new PythonVariable<>("b", PythonTypes.INT, 3));
|
||||||
|
|
||||||
|
List<PythonVariable> outputs = job.execAndReturnAllVariables(inputs);
|
||||||
|
|
||||||
|
assertEquals("c", outputs.get(1).getName());
|
||||||
|
assertEquals(10L, (long)outputs.get(1).getValue());
|
||||||
|
|
||||||
|
inputs = new ArrayList<>();
|
||||||
|
inputs.add(new PythonVariable<>("a", PythonTypes.FLOAT, 3.0));
|
||||||
|
inputs.add(new PythonVariable<>("b", PythonTypes.FLOAT, 4.0));
|
||||||
|
|
||||||
|
outputs = job.execAndReturnAllVariables(inputs);
|
||||||
|
|
||||||
|
|
||||||
|
assertEquals("c", outputs.get(1).getName());
|
||||||
|
assertEquals(12.0, (double)outputs.get(1).getValue(), 1e-5);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultiplePythonJobsSetupRunParallel()throws Exception{
|
||||||
|
PythonContextManager.deleteNonMainContexts();
|
||||||
|
|
||||||
|
String code1 = "five=None\n" +
|
||||||
|
"def setup():\n" +
|
||||||
|
" global five\n"+
|
||||||
|
" five = 5\n\n" +
|
||||||
|
"def run(a, b):\n" +
|
||||||
|
" c = a + b + five\n"+
|
||||||
|
" return {'c':c}\n\n";
|
||||||
|
PythonJob job1 = new PythonJob("job1", code1, true);
|
||||||
|
|
||||||
|
String code2 = "five=None\n" +
|
||||||
|
"def setup():\n" +
|
||||||
|
" global five\n"+
|
||||||
|
" five = 5\n\n" +
|
||||||
|
"def run(a, b):\n" +
|
||||||
|
" c = a + b - five\n"+
|
||||||
|
" return {'c':c}\n\n";
|
||||||
|
PythonJob job2 = new PythonJob("job2", code2, true);
|
||||||
|
|
||||||
|
List<PythonVariable> inputs = new ArrayList<>();
|
||||||
|
inputs.add(new PythonVariable<>("a", PythonTypes.INT, 2));
|
||||||
|
inputs.add(new PythonVariable<>("b", PythonTypes.INT, 3));
|
||||||
|
|
||||||
|
|
||||||
|
List<PythonVariable> outputs = new ArrayList<>();
|
||||||
|
outputs.add(new PythonVariable<>("c", PythonTypes.INT));
|
||||||
|
|
||||||
|
job1.exec(inputs, outputs);
|
||||||
|
|
||||||
|
assertEquals("c", outputs.get(0).getName());
|
||||||
|
assertEquals(10L, (long)outputs.get(0).getValue());
|
||||||
|
|
||||||
|
job2.exec(inputs, outputs);
|
||||||
|
|
||||||
|
assertEquals("c", outputs.get(0).getName());
|
||||||
|
assertEquals(0L, (long)outputs.get(0).getValue());
|
||||||
|
|
||||||
|
inputs = new ArrayList<>();
|
||||||
|
inputs.add(new PythonVariable<>("a", PythonTypes.FLOAT, 3.0));
|
||||||
|
inputs.add(new PythonVariable<>("b", PythonTypes.FLOAT, 4.0));
|
||||||
|
|
||||||
|
outputs = new ArrayList<>();
|
||||||
|
outputs.add(new PythonVariable<>("c", PythonTypes.FLOAT));
|
||||||
|
|
||||||
|
|
||||||
|
job1.exec(inputs, outputs);
|
||||||
|
|
||||||
|
assertEquals("c", outputs.get(0).getName());
|
||||||
|
assertEquals(12.0, (double)outputs.get(0).getValue(), 1e-5);
|
||||||
|
|
||||||
|
job2.exec(inputs, outputs);
|
||||||
|
|
||||||
|
assertEquals("c", outputs.get(0).getName());
|
||||||
|
assertEquals(2.0, (double)outputs.get(0).getValue(), 1e-5);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,169 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2020 Konduit K.K.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Apache License, Version 2.0 which is available at
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
import org.eclipse.python4j.*;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.NotThreadSafe;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
@NotThreadSafe
|
||||||
|
public class PythonMultiThreadTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultiThreading1()throws Throwable{
|
||||||
|
final List<Throwable> exceptions = Collections.synchronizedList(new ArrayList<Throwable>());
|
||||||
|
Runnable runnable = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try(PythonGIL gil = PythonGIL.lock()){
|
||||||
|
try(PythonGC gc = PythonGC.watch()){
|
||||||
|
List<PythonVariable> inputs = new ArrayList<>();
|
||||||
|
inputs.add(new PythonVariable<>("x", PythonTypes.STR, "Hello "));
|
||||||
|
inputs.add(new PythonVariable<>("y", PythonTypes.STR, "World"));
|
||||||
|
PythonVariable out = new PythonVariable<>("z", PythonTypes.STR);
|
||||||
|
String code = "z = x + y";
|
||||||
|
PythonExecutioner.exec(code, inputs, Collections.singletonList(out));
|
||||||
|
Assert.assertEquals("Hello World", out.getValue());
|
||||||
|
System.out.println(out.getValue() + " From thread " + Thread.currentThread().getId());
|
||||||
|
}
|
||||||
|
}catch (Throwable e){
|
||||||
|
exceptions.add(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int numThreads = 10;
|
||||||
|
Thread[] threads = new Thread[numThreads];
|
||||||
|
for (int i = 0; i < threads.length; i++){
|
||||||
|
threads[i] = new Thread(runnable);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < threads.length; i++){
|
||||||
|
threads[i].start();
|
||||||
|
}
|
||||||
|
Thread.sleep(100);
|
||||||
|
for (int i = 0; i < threads.length; i++){
|
||||||
|
threads[i].join();
|
||||||
|
}
|
||||||
|
if (!exceptions.isEmpty()){
|
||||||
|
throw(exceptions.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
public void testMultiThreading2()throws Throwable{
|
||||||
|
final List<Throwable> exceptions = Collections.synchronizedList(new ArrayList<Throwable>());
|
||||||
|
Runnable runnable = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try(PythonGIL gil = PythonGIL.lock()){
|
||||||
|
try(PythonGC gc = PythonGC.watch()){
|
||||||
|
PythonContextManager.reset();
|
||||||
|
PythonContextManager.reset();
|
||||||
|
List<PythonVariable> inputs = new ArrayList<>();
|
||||||
|
inputs.add(new PythonVariable<>("a", PythonTypes.INT, 5));
|
||||||
|
String code = "b = '10'\nc = 20.0 + a";
|
||||||
|
List<PythonVariable> vars = PythonExecutioner.execAndReturnAllVariables(code, inputs);
|
||||||
|
|
||||||
|
Assert.assertEquals("a", vars.get(0).getName());
|
||||||
|
Assert.assertEquals(PythonTypes.INT, vars.get(0).getType());
|
||||||
|
Assert.assertEquals(5L, (long)vars.get(0).getValue());
|
||||||
|
|
||||||
|
Assert.assertEquals("b", vars.get(1).getName());
|
||||||
|
Assert.assertEquals(PythonTypes.STR, vars.get(1).getType());
|
||||||
|
Assert.assertEquals("10", vars.get(1).getValue().toString());
|
||||||
|
|
||||||
|
Assert.assertEquals("c", vars.get(2).getName());
|
||||||
|
Assert.assertEquals(PythonTypes.FLOAT, vars.get(2).getType());
|
||||||
|
Assert.assertEquals(25.0, (double)vars.get(2).getValue(), 1e-5);
|
||||||
|
}
|
||||||
|
}catch (Throwable e){
|
||||||
|
exceptions.add(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int numThreads = 10;
|
||||||
|
Thread[] threads = new Thread[numThreads];
|
||||||
|
for (int i = 0; i < threads.length; i++){
|
||||||
|
threads[i] = new Thread(runnable);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < threads.length; i++){
|
||||||
|
threads[i].start();
|
||||||
|
}
|
||||||
|
Thread.sleep(100);
|
||||||
|
for (int i = 0; i < threads.length; i++){
|
||||||
|
threads[i].join();
|
||||||
|
}
|
||||||
|
if (!exceptions.isEmpty()){
|
||||||
|
throw(exceptions.get(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultiThreading3() throws Throwable{
|
||||||
|
PythonContextManager.deleteNonMainContexts();
|
||||||
|
|
||||||
|
String code = "c = a + b";
|
||||||
|
final PythonJob job = new PythonJob("job1", code, false);
|
||||||
|
|
||||||
|
final List<Throwable> exceptions = Collections.synchronizedList(new ArrayList<Throwable>());
|
||||||
|
|
||||||
|
class JobThread extends Thread{
|
||||||
|
private int a, b, c;
|
||||||
|
public JobThread(int a, int b, int c){
|
||||||
|
this.a = a;
|
||||||
|
this.b = b;
|
||||||
|
this.c = c;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void run(){
|
||||||
|
try{
|
||||||
|
PythonVariable<Long> out = new PythonVariable<>("c", PythonTypes.INT);
|
||||||
|
job.exec(Arrays.<PythonVariable>asList(new PythonVariable<>("a", PythonTypes.INT, a),
|
||||||
|
new PythonVariable<>("b", PythonTypes.INT, b)),
|
||||||
|
Collections.<PythonVariable>singletonList(out));
|
||||||
|
Assert.assertEquals(c, out.getValue().intValue());
|
||||||
|
}catch (Exception e){
|
||||||
|
exceptions.add(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int numThreads = 10;
|
||||||
|
JobThread[] threads = new JobThread[numThreads];
|
||||||
|
for (int i=0; i < threads.length; i++){
|
||||||
|
threads[i] = new JobThread(i, i + 3, 2 * i +3);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < threads.length; i++){
|
||||||
|
threads[i].start();
|
||||||
|
}
|
||||||
|
Thread.sleep(100);
|
||||||
|
for (int i = 0; i < threads.length; i++){
|
||||||
|
threads[i].join();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!exceptions.isEmpty()){
|
||||||
|
throw(exceptions.get(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2020 Konduit K.K.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Apache License, Version 2.0 which is available at
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
import org.eclipse.python4j.PythonException;
|
||||||
|
import org.eclipse.python4j.PythonObject;
|
||||||
|
import org.eclipse.python4j.PythonTypes;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class PythonPrimitiveTypesTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInt() throws PythonException {
|
||||||
|
long j = 3;
|
||||||
|
PythonObject p = PythonTypes.INT.toPython(j);
|
||||||
|
long j2 = PythonTypes.INT.toJava(p);
|
||||||
|
|
||||||
|
Assert.assertEquals(j, j2);
|
||||||
|
|
||||||
|
PythonObject p2 = PythonTypes.convert(j);
|
||||||
|
long j3 = PythonTypes.INT.toJava(p2);
|
||||||
|
|
||||||
|
Assert.assertEquals(j, j3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStr() throws PythonException{
|
||||||
|
String s = "abcd";
|
||||||
|
PythonObject p = PythonTypes.STR.toPython(s);
|
||||||
|
String s2 = PythonTypes.STR.toJava(p);
|
||||||
|
|
||||||
|
Assert.assertEquals(s, s2);
|
||||||
|
|
||||||
|
PythonObject p2 = PythonTypes.convert(s);
|
||||||
|
String s3 = PythonTypes.STR.toJava(p2);
|
||||||
|
|
||||||
|
Assert.assertEquals(s, s3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFloat() throws PythonException{
|
||||||
|
double f = 7;
|
||||||
|
PythonObject p = PythonTypes.FLOAT.toPython(f);
|
||||||
|
double f2 = PythonTypes.FLOAT.toJava(p);
|
||||||
|
|
||||||
|
Assert.assertEquals(f, f2, 1e-5);
|
||||||
|
|
||||||
|
PythonObject p2 = PythonTypes.convert(f);
|
||||||
|
double f3 = PythonTypes.FLOAT.toJava(p2);
|
||||||
|
|
||||||
|
Assert.assertEquals(f, f3, 1e-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBool() throws PythonException{
|
||||||
|
boolean b = true;
|
||||||
|
PythonObject p = PythonTypes.BOOL.toPython(b);
|
||||||
|
boolean b2 = PythonTypes.BOOL.toJava(p);
|
||||||
|
|
||||||
|
Assert.assertEquals(b, b2);
|
||||||
|
|
||||||
|
PythonObject p2 = PythonTypes.convert(b);
|
||||||
|
boolean b3 = PythonTypes.BOOL.toJava(p2);
|
||||||
|
|
||||||
|
Assert.assertEquals(b, b3);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>python4j-parent</artifactId>
|
||||||
|
<groupId>org.eclipse</groupId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>python4j-numpy</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.bytedeco</groupId>
|
||||||
|
<artifactId>numpy-platform</artifactId>
|
||||||
|
<version>${numpy.javacpp.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.nd4j</groupId>
|
||||||
|
<artifactId>nd4j-native-api</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.nd4j</groupId>
|
||||||
|
<artifactId>nd4j-common-tests</artifactId>
|
||||||
|
<version>${nd4j.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>test-nd4j-native</id>
|
||||||
|
</profile>
|
||||||
|
<profile>
|
||||||
|
<id>test-nd4j-cuda-10.2</id>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
|
|
||||||
|
</project>
|
Loading…
Reference in New Issue