Found a bug with pyjnius when dealing with a Java class with multiple constructors.
When I executed the below Java code, the main method will invoke the 2nd constructor and finish gracefully.
When I execute the python code below, it will report the constructor is not found.
(jnius.JavaException: No constructor matching your arguments, available: ['(ILjava/lang/String;)V', '(ILjava/lang/String;Ljava/lang/Object;[I)V'])
But when I removed the 1st constructor in java code, my python code with pyjnius can correctly find the only constructor and finish gracefully.
Java Class
public class SampleJavaClass {
public SampleJavaClass( int arg1, String arg2, int arg3, Object arg4, int... arg5 ) {
System.out.println("arg1: " + Integer.toString(arg1));
System.out.println("arg2: " + arg2);
System.out.println("arg3: " + arg3);
System.out.println("arg3: " + arg4);
System.out.println("arg4: " + arg5.toString());
}
// the constructor plans to use
public SampleJavaClass (int arg1, String arg2, Object arg3, int... arg4) {
System.out.println("arg1: " + Integer.toString(arg1));
System.out.println("arg2: " + arg2);
System.out.println("arg3: " + arg3);
System.out.println("arg4: " + arg4.toString());
}
public static void main(String[] args) {
SampleJavaClass test = new SampleJavaClass(1, "var2", null, 4);
}
}
Python code with pyjnius
import os
currentPath = os.getcwd()
classpath = currentPath + "/SampleJavaClass.class"
import jnius_config
jnius_config.set_classpath('.', classpath)
from jnius import autoclass
def main():
SampleJavaClass = autoclass("SampleJavaClass")
SampleJavaClass(1, "var2", None, 4)
print("Execution finished!")
if __name__ == "__main__":
main()
have you tried using a constructor signature hint?
See https://github.com/kivy/pyjnius/blob/307659b13c1e5583fcb25603b7d3732265ffd4a0/tests/test_constructor.py#L54 for an example.
When pyjnius calls a method or constructor it first checks if there are 0, 1, or multiple constructors or methods with the given name. If there is only 1 it always makes the call, hoping for the best. If there are multiple, it does this scoring process to try to decide if each is acceptable or not. Hopefully one and only one is acceptable; if not, it throws an error.
In this case, it is rejecting both as constructors are unacceptable. Using a signature hint gets around the problem by causing it to skip going through the scoring process. The signature hint is a good workaround.
For this issue, I took a closer look at the code and found there is in fact a bug that is causing this to fail. And happily it's an easy fix. I'll make a PR with a unit test in a moment.
have you tried using a constructor signature hint?
See
for an example.
I tried adding a signature as shown by the code below just now, but it emitted an Assertion Error. Where did I mess up?
Error
File "jnius/jnius_export_class.pxi", line 270, in jnius.JavaClass.__init__
File "jnius/jnius_export_class.pxi", line 319, in jnius.JavaClass.call_constructor
AssertionError
Python code with pyjnius
import os
currentPath = os.getcwd()
classpath = currentPath + "/SampleJavaClass.class"
import jnius_config
jnius_config.set_classpath('.', classpath)
from jnius import autoclass
def main():
SampleJavaClass = autoclass("SampleJavaClass")
SampleJavaClass(1, "var2", None, 4, signature="(ILjava/lang/String;Ljava/lang/Object;[I)V")
print("Execution finished!")
if __name__ == "__main__":
main()
Here is the relevant code:
requestedDefn = kwargs.pop('signature', None)
for definition, is_varargs in definitions:
found_definitions.append(definition)
d_ret, d_args = parse_definition(definition)
if requestedDefn == definition:
assert not is_varargs
scores=[]
score=1
scores.append((score, definition, d_ret, d_args, args))
break
Why can't signature hints be used with variable arguments?
Never mind, I figured it out and have a fix for this. I'll add it to the PR.
@enjoybeta I fixed this in the PR, but bottom line, the signature hint workaround doesn't work in the current release.
@cmacdonald , is there another workaround they can use here?
Nothing of the top of my head - add a quick Java factory class that calls the varargs constructor?
Anyway, I propose we can close the issue as the next Jnius release will fix the bug.
Good idea! A factory method can take care of it if altering that source code is an option. If not, they can create a separate helper-utility class that does it for them.
Agreed, we can close this issue now.
Most helpful comment
When pyjnius calls a method or constructor it first checks if there are 0, 1, or multiple constructors or methods with the given name. If there is only 1 it always makes the call, hoping for the best. If there are multiple, it does this scoring process to try to decide if each is acceptable or not. Hopefully one and only one is acceptable; if not, it throws an error.
In this case, it is rejecting both as constructors are unacceptable. Using a signature hint gets around the problem by causing it to skip going through the scoring process. The signature hint is a good workaround.
For this issue, I took a closer look at the code and found there is in fact a bug that is causing this to fail. And happily it's an easy fix. I'll make a PR with a unit test in a moment.