diff --git a/nodel-jyhost/build.gradle b/nodel-jyhost/build.gradle index 9cb02055..c3fae6e0 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.2' // 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..5881f3fb 100644 --- a/nodel-jyhost/src/main/java/org/nodel/jyhost/PyNode.java +++ b/nodel-jyhost/src/main/java/org/nodel/jyhost/PyNode.java @@ -71,6 +71,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; @@ -625,10 +626,16 @@ 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 *"); - + if (((PyInteger) _python.eval("'nodetoolkit' in dir(__import__('sys'))")).asInt() > 0) { + // Jython 2.7 exposes system state through sys + _python.exec("from sys.nodetoolkit import *"); + } else { + // Jython 2.5 exposes system state at top-level + _python.exec("from nodetoolkit import *"); + } + } finally { untrackFunction("(toolkit injection)"); @@ -742,28 +749,33 @@ 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) { + } + + 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(); @@ -904,21 +916,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 +1076,17 @@ 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(); + try { + // Jython 2.5 + code = (PyBaseCode) objectClass.getField("func_code").get(pyFunction); + } catch (NoSuchFieldException e) { + // Jython 2.7 + code = (PyBaseCode) objectClass.getField("__code__").get(pyFunction); + } // only support either 0 or 1 args PyObject pyResult; @@ -1332,7 +1354,15 @@ private void handleRemoteEventArrival(SimpleName alias, NodelClientEvent nodelCl } PyFunction pyFunction = (PyFunction) pyObject; - PyBaseCode code = (PyBaseCode) pyFunction.func_code; + PyBaseCode code = null; + Class objectClass = pyFunction.getClass(); + try { + // Jython 2.5 + code = (PyBaseCode) objectClass.getField("func_code").get(pyFunction); + } catch (NoSuchFieldException e) { + // Jython 2.7 + code = (PyBaseCode) objectClass.getField("__code__").get(pyFunction); + } // only support either 0 or 1 args if (code.co_argcount == 0)