Pyjnius: Failed to find constructor when there are multiple(overloaded) constructors

Created on 22 Oct 2020  ·  8Comments  ·  Source: kivy/pyjnius

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()

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.

All 8 comments

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

https://github.com/kivy/pyjnius/blob/307659b13c1e5583fcb25603b7d3732265ffd4a0/tests/test_constructor.py#L54

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.

Was this page helpful?
0 / 5 - 0 ratings