-
Notifications
You must be signed in to change notification settings - Fork 80
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Replace some wrapper funcs in the deephaven.time module with Java wrapped method calls when used in formulas #4095
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -92,9 +92,12 @@ | |||||
import io.deephaven.engine.context.QueryScope; | ||||||
import io.deephaven.engine.table.impl.MatchPair; | ||||||
import io.deephaven.engine.table.impl.ShiftedColumnsFactory; | ||||||
import io.deephaven.engine.util.AbstractScriptSession; | ||||||
import io.deephaven.engine.util.PyCallableWrapper.ColumnChunkArgument; | ||||||
import io.deephaven.engine.util.PyCallableWrapper.ConstantChunkArgument; | ||||||
import io.deephaven.engine.util.PyCallableWrapper; | ||||||
import io.deephaven.engine.util.PythonDeephavenSession; | ||||||
import io.deephaven.engine.util.PythonScopeJpyImpl; | ||||||
import io.deephaven.internal.log.LoggerFactory; | ||||||
import io.deephaven.io.logger.Logger; | ||||||
import io.deephaven.util.type.TypeUtils; | ||||||
|
@@ -109,6 +112,8 @@ | |||||
import io.deephaven.vector.Vector; | ||||||
import org.apache.commons.lang3.ClassUtils; | ||||||
import org.apache.commons.lang3.exception.ExceptionUtils; | ||||||
import org.jpy.PyInputMode; | ||||||
import org.jpy.PyListWrapper; | ||||||
import org.jpy.PyObject; | ||||||
|
||||||
import java.lang.reflect.Array; | ||||||
|
@@ -465,6 +470,66 @@ private Class<?> findNestedClass(Class<?> enclosingClass, String nestedClassName | |||||
return m.get(nestedClassName); | ||||||
} | ||||||
|
||||||
private boolean pyToJavaReplaced(final Class<?> scope, final MethodCallExpr n) { | ||||||
final String methodName = n.getNameAsString(); | ||||||
final QueryScope queryScope = ExecutionContext.getContext().getQueryScope(); | ||||||
|
||||||
if (scope == null) { | ||||||
final Class<?> methodClass = variables.get(methodName); | ||||||
if (methodClass == PyCallableWrapper.class) { | ||||||
final Object paramValueRaw = queryScope.readParamValue(methodName, null); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can't do this 100% correctly from the information you have available. That is, you don't know that the name is coming from the query scope, since column names take precedence. Your argument seems to hinge on "how could there be a column of PyCallableWrappers?" Some user will find a way... Maybe we should add more typing information from the caller, to tell us what's a column ref, etc? |
||||||
final PyCallableWrapper pyCallableWrapper = (PyCallableWrapper) paramValueRaw; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe a |
||||||
try { | ||||||
String javaMethodName = pyCallableWrapper.getAttribute("_j_simple_name", String.class); | ||||||
n.setName(javaMethodName); | ||||||
int nargs = pyCallableWrapper.getAttribute("_nargs", int.class); | ||||||
PyListWrapper defArgList = (PyListWrapper) pyCallableWrapper.getAttribute("_def_args").asList(); | ||||||
int defArgsToAdd = nargs - n.getArguments().size(); | ||||||
if (defArgsToAdd > defArgList.size()) { | ||||||
throw new IllegalArgumentException("Missing args for " + methodName); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This might need more detail. Maybe try to give a string for the method call itself, with the args? |
||||||
} | ||||||
if (defArgsToAdd < 0) { | ||||||
// do nothing, could be valid if the java method has a vararg | ||||||
} | ||||||
// Since the func call follows the Java syntax, we don't do name matching for the keyword args | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think a more detailed comment would be helpful (to people me like me):
|
||||||
for (int i = defArgList.size() - (nargs - n.getArguments().size()); i < defArgList.size(); i++) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
Object v = PythonScopeJpyImpl.convert(defArgList.get(i)); | ||||||
n.addArgument(v.getClass() == String.class ? "\"" + v.toString() + "\"" : v.toString()); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any chance you unwrap |
||||||
} | ||||||
return true; | ||||||
} catch (IllegalArgumentException iae) { | ||||||
throw iae; | ||||||
} catch (RuntimeException e) { | ||||||
// Not a Java replaceable callable, can safely ignore | ||||||
Comment on lines
+502
to
+503
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we use |
||||||
} | ||||||
} | ||||||
} else if (scope == org.jpy.PyObject.class || scope == PyCallableWrapper.class) { | ||||||
String pythonExpr = n.getScope().get().toString() + "." + methodName; | ||||||
PythonDeephavenSession pds = | ||||||
(PythonDeephavenSession) ((AbstractScriptSession.UnsynchronizedScriptSessionQueryScope) queryScope) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
.scriptSession(); | ||||||
Map<String, PyObject> scopeVars = pds.getVariablesRaw(); | ||||||
PyObject pyobj; | ||||||
try { | ||||||
pyobj = PyObject.executeCode(pythonExpr, PyInputMode.EXPRESSION, scopeVars, null); | ||||||
} catch (RuntimeException e) { | ||||||
throw new RuntimeException("Cannot find Python callable: " + pythonExpr); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Throw something else. |
||||||
} | ||||||
try { | ||||||
Object obj = PythonScopeJpyImpl.convert(pyobj); | ||||||
if (obj.getClass() == (PyCallableWrapper.class)) { | ||||||
String javaMethodName = ((PyCallableWrapper) obj).getAttribute("_j_simple_name", String.class); | ||||||
n.setScope(null); | ||||||
n.setName(javaMethodName); | ||||||
return true; | ||||||
Comment on lines
+520
to
+524
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't we want the same default handling? That's missing... |
||||||
} | ||||||
} catch (Exception e) { | ||||||
// Not a Java replaceable callable, can safely ignore | ||||||
} | ||||||
} | ||||||
return false; | ||||||
} | ||||||
|
||||||
private Method getMethod(final Class<?> scope, final String methodName, final Class<?>[] paramTypes, | ||||||
final Class<?>[][] parameterizedTypes) { | ||||||
final ArrayList<CandidateExecutable<Method>> acceptableMethods = new ArrayList<>(); | ||||||
|
@@ -1140,6 +1205,16 @@ public Class<?> visit(NameExpr n, VisitArgs printer) { | |||||
* throw them to 'findClass()'. Many details are not relevant here. For example, field access is handled by a | ||||||
* different method: visit(FieldAccessExpr, StringBuilder). | ||||||
*/ | ||||||
Map<String, String> pyConstantsMap = Map.of( | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should probably be very late in the method, so that we don't try to provide a type for an absent variable in a non-python language. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should be conditional on the expression coming from Python, I think. |
||||||
"True", "true", | ||||||
"False", "false", | ||||||
"None", "null"); | ||||||
final String name = n.getNameAsString(); | ||||||
String jConstant = pyConstantsMap.get(name); | ||||||
if (jConstant != null) { | ||||||
printer.append(jConstant); | ||||||
return name.equals("None") ? NULL_CLASS : boolean.class; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is terribly hacky. Why not keep pairs in the map's values, so the type is associated with the constant? |
||||||
} | ||||||
printer.append(n.getNameAsString()); | ||||||
|
||||||
Class<?> ret = variables.get(n.getNameAsString()); | ||||||
|
@@ -1865,6 +1940,13 @@ public Class<?> visit(MethodCallExpr n, VisitArgs printer) { | |||||
return result; | ||||||
}).orElse(null); | ||||||
|
||||||
if (pyToJavaReplaced(scope, n)) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe rename method, |
||||||
if (scope != null) { | ||||||
scope = null; | ||||||
innerPrinter.reset(); | ||||||
} | ||||||
} | ||||||
Comment on lines
+1943
to
+1948
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, it looks to me like this means we can only replace things that are statically imported. Should change the scope in |
||||||
|
||||||
Expression[] expressions = n.getArguments() == null ? new Expression[0] | ||||||
: n.getArguments().toArray(new Expression[0]); | ||||||
|
||||||
|
@@ -1873,7 +1955,6 @@ public Class<?> visit(MethodCallExpr n, VisitArgs printer) { | |||||
Class<?>[][] parameterizedTypes = getParameterizedTypes(expressions); | ||||||
|
||||||
Method method = getMethod(scope, n.getNameAsString(), expressionTypes, parameterizedTypes); | ||||||
|
||||||
Class<?>[] argumentTypes = method.getParameterTypes(); | ||||||
|
||||||
// now do some parameter conversions... | ||||||
|
@@ -1913,6 +1994,7 @@ public Class<?> visit(MethodCallExpr n, VisitArgs printer) { | |||||
} | ||||||
} else { // Groovy or Java method call | ||||||
printer.append(innerPrinter); | ||||||
n.setName(method.getName()); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why aren't we doing all of our |
||||||
printer.append(n.getNameAsString()); | ||||||
} | ||||||
|
||||||
|
@@ -2538,6 +2620,12 @@ public boolean hasStringBuilder() { | |||||
return builder != null; | ||||||
} | ||||||
|
||||||
public void reset() { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hopefully my proposed change enables us to delete this. |
||||||
if (hasStringBuilder()) { | ||||||
builder.setLength(0); | ||||||
} | ||||||
} | ||||||
|
||||||
/** | ||||||
* Convenience method: forwards argument to 'builder' if 'builder' is not null | ||||||
*/ | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should find a way to not move this. How about we have an interface in
Then, just have the |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This class didn't need to move. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should have a JavaDoc, and clarify that it is mutating
n
.