davsclaus commented on code in PR #28391:
URL: https://github.com/apache/camel/pull/28391#discussion_r4367781833
##########
components/camel-python/src/main/java/org/apache/camel/language/python/PythonExpression.java:
##########
@@ -85,18 +92,53 @@ private <T> T doEvaluate(Exchange exchange, Class<T> type) {
compiler.set("properties", properties);
PyObject out = compiler.eval(compiledExpression);
- if (out != null) {
- String value = out.toString();
+ Object value = toJava(out);
+ if (value != null) {
return
exchange.getContext().getTypeConverter().convertTo(type, value);
}
} catch (Exception e) {
- throw new ExpressionIllegalSyntaxException(expressionString, e);
+ // the script compiled in the constructor, so this is an error
while it runs
+ throw new ExpressionEvaluationException(this, exchange, e);
} finally {
compiler.cleanup();
}
return null;
}
+ /**
+ * The Java value of what a script returns: None is null, a Python string,
number or boolean is the Java String,
+ * number or Boolean, and a Java object bound to the script (such as the
body) is that object. A dict, list, tuple
+ * or set is copied into a LinkedHashMap, ArrayList or LinkedHashSet of
Java values, as the Jython collections are
+ * live views that give their Python text from toString() and keep nested
Python collections.
+ */
+ static Object toJava(Object value) {
+ if (!(value instanceof PyObject py)) {
+ return value;
+ }
+ Object answer = py.__tojava__(Object.class);
+ if (!(answer instanceof PyObject)) {
+ // None, a Python string, number or boolean, or a Java object
+ return answer;
+ }
+ if (answer instanceof Map<?, ?> map) {
+ Map<Object, Object> copy = new LinkedHashMap<>();
+ map.forEach((k, v) -> copy.put(toJava(k), toJava(v)));
Review Comment:
Right, thanks: a dict that holds itself overflowed the stack. 1984d34ecbc2
keeps the copies in an `IdentityHashMap` and records each copy before
recursing, as `Python3Language.materialize` does. A collection that holds
itself now gives one copy that holds itself.
`collectionsThatHoldThemselvesAreCopiedOnce` covers a dict and a list; it
failed with `StackOverflowError` before the fix.
_Claude Code on behalf of davsclaus_
##########
components/camel-python/src/main/java/org/apache/camel/language/python/PythonExpression.java:
##########
@@ -85,18 +92,53 @@ private <T> T doEvaluate(Exchange exchange, Class<T> type) {
compiler.set("properties", properties);
PyObject out = compiler.eval(compiledExpression);
- if (out != null) {
- String value = out.toString();
+ Object value = toJava(out);
+ if (value != null) {
return
exchange.getContext().getTypeConverter().convertTo(type, value);
}
} catch (Exception e) {
- throw new ExpressionIllegalSyntaxException(expressionString, e);
+ // the script compiled in the constructor, so this is an error
while it runs
+ throw new ExpressionEvaluationException(this, exchange, e);
} finally {
compiler.cleanup();
}
return null;
}
+ /**
+ * The Java value of what a script returns: None is null, a Python string,
number or boolean is the Java String,
+ * number or Boolean, and a Java object bound to the script (such as the
body) is that object. A dict, list, tuple
+ * or set is copied into a LinkedHashMap, ArrayList or LinkedHashSet of
Java values, as the Jython collections are
+ * live views that give their Python text from toString() and keep nested
Python collections.
+ */
+ static Object toJava(Object value) {
+ if (!(value instanceof PyObject py)) {
+ return value;
+ }
+ Object answer = py.__tojava__(Object.class);
+ if (!(answer instanceof PyObject)) {
+ // None, a Python string, number or boolean, or a Java object
+ return answer;
+ }
+ if (answer instanceof Map<?, ?> map) {
+ Map<Object, Object> copy = new LinkedHashMap<>();
+ map.forEach((k, v) -> copy.put(toJava(k), toJava(v)));
+ return copy;
+ } else if (answer instanceof List<?> list) {
+ return copyInto(list, new ArrayList<>(list.size()));
+ } else if (answer instanceof Set<?> set) {
+ return copyInto(set, new LinkedHashSet<>());
+ }
+ return answer;
+ }
+
+ private static <C extends Collection<Object>> C copyInto(Collection<?>
source, C target) {
+ for (Object element : source) {
+ target.add(toJava(element));
+ }
+ return target;
+ }
Review Comment:
Right, thanks: a dict that holds itself overflowed the stack. 1984d34ecbc2
keeps the copies in an `IdentityHashMap` and records each copy before
recursing, as `Python3Language.materialize` does. A collection that holds
itself now gives one copy that holds itself.
`collectionsThatHoldThemselvesAreCopiedOnce` covers a dict and a list; it
failed with `StackOverflowError` before the fix.
_Claude Code on behalf of davsclaus_
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]