From 6044dc10f13aed8e793ea71d7f689816462b8f28 Mon Sep 17 00:00:00 2001 From: Tommy Gilligan Date: Thu, 30 Jun 2022 11:33:07 +1000 Subject: [PATCH 1/4] Fix invalid cast (PyNone -> PyFunction) in Jython 2.7 --- .../main/java/org/nodel/jyhost/PyNode.java | 53 +++++++++++-------- 1 file changed, 30 insertions(+), 23 deletions(-) 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..fef83d0f 100644 --- a/nodel-jyhost/src/main/java/org/nodel/jyhost/PyNode.java +++ b/nodel-jyhost/src/main/java/org/nodel/jyhost/PyNode.java @@ -742,28 +742,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 +909,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)"; From d2d219b071306ea6693cab3a8ecb8f8f9bd09f5e Mon Sep 17 00:00:00 2001 From: Tommy Gilligan Date: Thu, 30 Jun 2022 11:33:14 +1000 Subject: [PATCH 2/4] Import nodetoolkit from sys when it is there --- .../src/main/java/org/nodel/jyhost/PyNode.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) 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 fef83d0f..74ad71d6 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)"); From e4ec91ec36a81190427e15ba1d6fd596ee996256 Mon Sep 17 00:00:00 2001 From: Tommy Gilligan Date: Thu, 30 Jun 2022 11:33:19 +1000 Subject: [PATCH 3/4] Use __code__ with Jython 2.7 and func_code with Jython 2.5 --- .../main/java/org/nodel/jyhost/PyNode.java | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) 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 74ad71d6..5881f3fb 100644 --- a/nodel-jyhost/src/main/java/org/nodel/jyhost/PyNode.java +++ b/nodel-jyhost/src/main/java/org/nodel/jyhost/PyNode.java @@ -1076,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; @@ -1346,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) From 08abd22b70805a4871c17aa29e9a81aeff5ff4a6 Mon Sep 17 00:00:00 2001 From: Tommy Gilligan Date: Wed, 6 Jul 2022 11:57:28 +1000 Subject: [PATCH 4/4] Default to Jython 2.7.2 --- nodel-jyhost/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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'