From 6044dc10f13aed8e793ea71d7f689816462b8f28 Mon Sep 17 00:00:00 2001 From: Tommy Gilligan Date: Thu, 30 Jun 2022 11:33:07 +1000 Subject: [PATCH 1/7] 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/7] 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/7] 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/7] 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' From 25d6926c6e2251b8dd733209200546774b4f748e Mon Sep 17 00:00:00 2001 From: scroix Date: Thu, 8 Dec 2022 14:07:58 +1100 Subject: [PATCH 5/7] Update to Jython-2.7.3 --- 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 c3fae6e0..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.7.2' + compile 'org.python:jython-standalone:2.7.3' // for the Nodel HTTP client: compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.5' From 05b2eb4e44b515336fe2d1767caed06e6e9cc422 Mon Sep 17 00:00:00 2001 From: scroix Date: Thu, 8 Dec 2022 14:10:31 +1100 Subject: [PATCH 6/7] Remove Jython-2.5 scenario --- .../main/java/org/nodel/jyhost/PyNode.java | 25 +++---------------- 1 file changed, 3 insertions(+), 22 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 5881f3fb..a7a3dee9 100644 --- a/nodel-jyhost/src/main/java/org/nodel/jyhost/PyNode.java +++ b/nodel-jyhost/src/main/java/org/nodel/jyhost/PyNode.java @@ -71,7 +71,6 @@ 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; @@ -628,13 +627,7 @@ private void applyConfig0(NodeConfig config) throws Exception { trackFunction("(toolkit injection)"); // use this import to provide a toolkit directly into the script - 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 *"); - } + _python.exec("from nodetoolkit import *"); } finally { untrackFunction("(toolkit injection)"); @@ -1080,13 +1073,7 @@ protected Object handleActionRequest(SimpleName action, Object arg) { PyFunction pyFunction = (PyFunction) pyObject; 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); - } + code = (PyBaseCode) objectClass.getField("__code__").get(pyFunction); // only support either 0 or 1 args PyObject pyResult; @@ -1356,13 +1343,7 @@ private void handleRemoteEventArrival(SimpleName alias, NodelClientEvent nodelCl PyFunction pyFunction = (PyFunction) pyObject; 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); - } + code = (PyBaseCode) objectClass.getField("__code__").get(pyFunction); // only support either 0 or 1 args if (code.co_argcount == 0) From c5876c5b6123d4f579e6823d70dc1f656b9915c5 Mon Sep 17 00:00:00 2001 From: scroix Date: Thu, 8 Dec 2022 14:12:09 +1100 Subject: [PATCH 7/7] Update sys.path with nodetoolkit whilst injecting --- .../main/java/org/nodel/jyhost/PyNode.java | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 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 a7a3dee9..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 { @@ -752,6 +743,7 @@ private void applyConfig0(NodeConfig config) throws Exception { commentary.add("'@before_main' function" + (beforeFnCount == 1 ? "" : "s")); } + // handle main function (if present) if (_python.get("main") instanceof PyFunction) { PyFunction mainFunction = (PyFunction) _python.get("main"); if (mainFunction != null) { @@ -894,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())); } /**