diff --git a/nodel-jyhost/build.gradle b/nodel-jyhost/build.gradle index 9cb02055..eba33a55 100644 --- a/nodel-jyhost/build.gradle +++ b/nodel-jyhost/build.gradle @@ -62,7 +62,7 @@ dependencies { compile project(':nodel-framework') compile 'commons-daemon:commons-daemon:1.0.15' compile 'org.slf4j:slf4j-api:1.7.10' - compile 'org.python:jython-standalone:2.5.4-rc1' + compile 'org.python:jython-standalone:2.7.3' // for the Nodel HTTP client: compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.5' diff --git a/nodel-jyhost/src/main/java/org/nodel/jyhost/PyNode.java b/nodel-jyhost/src/main/java/org/nodel/jyhost/PyNode.java index de16a9f9..0a20776f 100644 --- a/nodel-jyhost/src/main/java/org/nodel/jyhost/PyNode.java +++ b/nodel-jyhost/src/main/java/org/nodel/jyhost/PyNode.java @@ -22,6 +22,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.ReentrantLock; +import java.lang.reflect.Method; import org.joda.time.DateTime; import org.nodel.DateTimes; @@ -71,6 +72,7 @@ import org.python.core.PyDictionary; import org.python.core.PyException; import org.python.core.PyFunction; +import org.python.core.PyInteger; import org.python.core.PyObject; import org.python.core.PyString; import org.python.core.PySystemState; @@ -549,16 +551,6 @@ private void applyConfig0(NodeConfig config) throws Exception { _logger.info("Initialising new Python interpreter..."); - PySystemState pySystemState = new PySystemState(); - - // set the current working directory - pySystemState.setCurrentWorkingDir(_root.getAbsolutePath()); - - // append the Node's root directory to the path - pySystemState.path.append(new PyString(_root.getAbsolutePath())); - pySystemState.path.append(new PyString(_metaRoot.getAbsolutePath())); - Py.setSystemState(pySystemState); - _globals = new PyDictionary(); ReentrantLock lock = null; @@ -568,7 +560,6 @@ private void applyConfig0(NodeConfig config) throws Exception { trackFunction("(instance creation)"); - // _python = new PythonInterpreter(globals, pySystemState); _python = PythonInterpreter.threadLocalStateInterpreter(_globals); } finally { @@ -625,10 +616,10 @@ private void applyConfig0(NodeConfig config) throws Exception { lock = getAReentrantLock(); trackFunction("(toolkit injection)"); - + // use this import to provide a toolkit directly into the script _python.exec("from nodetoolkit import *"); - + } finally { untrackFunction("(toolkit injection)"); @@ -742,28 +733,34 @@ private void applyConfig0(NodeConfig config) throws Exception { List commentary = new ArrayList<>(3); trackFunction("mains"); - + // handle @before_main functions (if present) - PyFunction processBeforeMainFunctions = (PyFunction) _globals.get(Py.java2py("processBeforeMainFunctions")); - long beforeFnCount = processBeforeMainFunctions.__call__().asLong(); - - if (beforeFnCount > 0) + if (_globals.get(Py.java2py("processBeforeMainFunctions")) instanceof PyFunction) { + PyFunction processBeforeMainFunctions = (PyFunction) _globals.get(Py.java2py("processBeforeMainFunctions")); + long beforeFnCount = processBeforeMainFunctions.__call__().asLong(); + + if (beforeFnCount > 0) commentary.add("'@before_main' function" + (beforeFnCount == 1 ? "" : "s")); - - // call 'main' if it exists - PyFunction mainFunction = (PyFunction) _python.get("main"); - if (mainFunction != null) { + } + + // handle main function (if present) + if (_python.get("main") instanceof PyFunction) { + PyFunction mainFunction = (PyFunction) _python.get("main"); + if (mainFunction != null) { mainFunction.__call__(); commentary.add("'main'"); + } } - + // handle @after_main functions (if present) - PyFunction processAfterMainFunctions = (PyFunction) _globals.get(Py.java2py("processAfterMainFunctions")); - long afterFnCount = processAfterMainFunctions.__call__().asLong(); - if (afterFnCount > 0) + if (_globals.get(Py.java2py("processAfterMainFunctions")) instanceof PyFunction) { + PyFunction processAfterMainFunctions = (PyFunction) _globals.get(Py.java2py("processAfterMainFunctions")); + long afterFnCount = processAfterMainFunctions.__call__().asLong(); + if (afterFnCount > 0) commentary.add("'@after_main' function" + (afterFnCount == 1 ? "" : "s")); - + } + // nothing went wrong, kick off toolkit _toolkit.enable(); @@ -889,8 +886,13 @@ public void error(Object obj) { // inject into 'sys' _pySystemState.__setattr__("nodetoolkit", Py.java2py(_toolkit)); + + // set the current working directory + _pySystemState.setCurrentWorkingDir(_root.getAbsolutePath()); - + // append the Node's root directory to the path + _pySystemState.path.append(new PyString(_root.getAbsolutePath())); + _pySystemState.path.append(new PyString(_metaRoot.getAbsolutePath())); } /** @@ -904,21 +906,23 @@ private void cleanupInterpreter() { _logger.info(message); _outReader.inject(message); - + try { - PyFunction processCleanupFunctions = (PyFunction) _globals.get(Py.java2py("processCleanupFunctions")); - long cleanupFnCount = processCleanupFunctions.__call__().asLong(); - - if (cleanupFnCount > 0) { - message = "('@at_cleanup' function" + (cleanupFnCount == 1 ? "" : "s") + " completed.)"; - _logger.info(message); - _outReader.inject(message); + if (_globals.get(Py.java2py("processCleanupFunctions")) instanceof PyFunction) { + PyFunction processCleanupFunctions = (PyFunction) _globals.get(Py.java2py("processCleanupFunctions")); + long cleanupFnCount = processCleanupFunctions.__call__().asLong(); + + if (cleanupFnCount > 0) { + message = "('@at_cleanup' function" + (cleanupFnCount == 1 ? "" : "s") + " completed.)"; + _logger.info(message); + _outReader.inject(message); + } } } catch (Exception exc) { // upstream exception handling should mean we never get here, but just in case _logger.warn("Unexpected exception during cleaning up; should be safe to ignore", exc); } - + _python.cleanup(); message = "(clean up complete)"; @@ -1062,9 +1066,11 @@ protected Object handleActionRequest(SimpleName action, Object arg) { throw new IllegalStateException("Action call failure (internal server error) - '" + functionName + "'"); } - + PyFunction pyFunction = (PyFunction) pyObject; - PyBaseCode code = (PyBaseCode) pyFunction.func_code; + PyBaseCode code = null; + Class objectClass = pyFunction.getClass(); + code = (PyBaseCode) objectClass.getField("__code__").get(pyFunction); // only support either 0 or 1 args PyObject pyResult; @@ -1332,7 +1338,9 @@ private void handleRemoteEventArrival(SimpleName alias, NodelClientEvent nodelCl } PyFunction pyFunction = (PyFunction) pyObject; - PyBaseCode code = (PyBaseCode) pyFunction.func_code; + PyBaseCode code = null; + Class objectClass = pyFunction.getClass(); + code = (PyBaseCode) objectClass.getField("__code__").get(pyFunction); // only support either 0 or 1 args if (code.co_argcount == 0)