pyjnius icon indicating copy to clipboard operation
pyjnius copied to clipboard

Improvement: autoclass() when class not in boot classpath

Open cmacdonald opened this issue 5 years ago • 5 comments

I have a slight problem: I'm trying to instantiate a class that is have a java.lang.Class reference to. This was provided by a ClassLoader accessing a classpath different from "boot time". Traceback attached below.

However, JNI's FindClass() only provides jclass for classes in the boot classpath, i.e. using ClassLoader.getSystemClassLoader().

I dont think we need FindClass(), as we already have access to the Class object. We just need to instantiate it from that Class object.

  1. A possible workaround is adding an optional parameter to autoclass() providing the Class object directly, i.e.
other_class = bla._class.getClassLoader().loadClass("my.other.Class")
other_classInstance = autoclass(None, cls=other_class)()
  1. Or being able to pass a ClassLoader to autoclass? Dont think this would work in the generic case.

  2. Or detecting when a Class object is being instantiated into Python, add that to jclass_register

Traceback.

Traceback (most recent call last):
  File "tests/test_rewrite.py", line 97, in test_rm3
    qe = pt.rewrite.RM3(indexref)
  File "/Users/craigm/git/Pyterrier/pyterrier/rewrite.py", line 139, in __init__
    rm = pt.ApplicationSetup.getClass("org.terrier.querying.RM3").newInstance()
  File "jnius/jnius_export_class.pxi", line 857, in jnius.JavaMethod.__call__
  File "jnius/jnius_export_class.pxi", line 939, in jnius.JavaMethod.call_method
  File "jnius/jnius_conversion.pxi", line 212, in jnius.convert_jobject_to_python
  File "/Users/craigm/anaconda3/envs/pyterrier/lib/python3.6/site-packages/jnius/reflect.py", line 229, in autoclass
    c = find_javaclass(clsname)
  File "jnius/jnius_export_func.pxi", line 26, in jnius.find_javaclass
  File "jnius/jnius_jvm_dlopen.pxi", line 91, in jnius.create_jnienv
jnius.JavaException: JVM exception occurred: org/terrier/querying/RM3 java.lang.NoClassDefFoundError

cmacdonald avatar May 06 '20 08:05 cmacdonald

I think you mean "a class that all i have is have a java.lang.Class reference" (emphasis on missing words).

So, you'd want to do everything autoclass does, but using a class reference instead of the fqn of the class. Maybe autoclass could be refactored to do the name lookup part (https://github.com/kivy/pyjnius/blob/master/jnius/reflect.py#L220-L233), and delegate the reflection part to another function, that you could call with your class object directly if you wish so?

tshirtman avatar May 06 '20 15:05 tshirtman

"I'm trying to instantiate a class that I have a java.lang.Class reference to."

Yes, your proposal may work - a variant of (1). Will give a try in due course. Thanks.

cmacdonald avatar May 06 '20 16:05 cmacdonald

Cool, autoclass is quite big, so splitting it a bit won't hurt.

tshirtman avatar May 06 '20 16:05 tshirtman

I have committed my WIP to https://github.com/cmacdonald/pyjnius/tree/reflect_class_issue534

This:

  • splits autoclass() into autoclass() and reflect_class(). The former is called with a string, the latter using a Class instance.
  • convert_jobject_to_python() uses reflect_class() instead of autoclass(). This doesnt work, exhibiting a RecursionError. I cant see why, feedback appreciated. It may have something to do with the addition of getClass = JavaMethod('()Ljava/lang/Class;') to the Object impl.

For cases where the returned object is not part of the boot classpath, convert_jobject_to_python() will have to be altered to get the actual Class object, rather than JNI's FindClass. I /think/ that this could be obtained from the jobject itself - i.e. instantiate it as a Object, call getClass() then reinstantiate it using that object.

cmacdonald avatar May 24 '20 12:05 cmacdonald

took the liberty to open #539 from it to review more easily.

tshirtman avatar May 24 '20 18:05 tshirtman