bcdev / jpy

A bi-directional Python-Java bridge used to embed Java in CPython or the other way round.
Apache License 2.0
187 stars 37 forks source link

Cannot instantiate a "simple" class #155

Open ShaheedHaque opened 5 years ago

ShaheedHaque commented 5 years ago

I'm new to jpy, and have been trying out both 0.9.0 as well as the top of the tree from github. I'm having some difficulties getting what (I think is a simple) class to be constructed:

$ python3
Python 3.6.6 (default, Sep 12 2018, 18:26:19) 
...
>>> import jpyutil
>>> jpyutil.init_jvm(jvm_maxmem='512M', jvm_classpath=['/main/...<elided>.../jasperstarter.jar'])
(<CDLL '/usr/lib/jvm/java-11-openjdk-amd64/lib/server/libjvm.so', handle f47490 at 0x7fade7dc6470>, ['-Xmx512M', '-Djava.class.path=/main/...<elided>.../jasperstarter.jar', '-Djpy.jpyLib=/usr/local/lib/python3.6/dist-packages/jpy.cpython-36m-x86_64-linux-gnu.so', '-Djpy.jdlLib=/usr/local/lib/python3.6/dist-packages/jdl.cpython-36m-x86_64-linux-gnu.so', '-Djpy.pythonLib=/usr/lib/python3.6/config-3.6m-x86_64-linux-gnu/libpython3.6.so', '-Djpy.pythonPrefix=/usr', '-Djpy.pythonExecutable=/usr/bin/python3'])
>>> 
>>> import jpy
>>> jpy.get_type('java.lang.String')
<class 'java.lang.String'>
>>> Config = jpy.get_type('de.cenote.jasperstarter.Config')
>>> foo = Config()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: no constructor found (missing JType attribute '__jinit__')

Now, what is odd is that help(Config) says that there is a __jini__ as follows:

...
Help on class Config:

class Config(java.lang.Object)
 |  Java Meta Type
 |  
...
 |  ----------------------------------------------------------------------
 |  Data and other attributes inherited from java.lang.Object:
 |  
 |  __jinit__ = jpy.JOverloadedMethod(class='java.lang.Object', name='__ji...
 |      Java Overloaded Method

I'm aware of the lack of support for non-final static, but that should not be an issue here. FWIW, the Java code in question is available for inspection. Any pointers appreciated.

ShaheedHaque commented 5 years ago

I hacked JObj_init() to dump the type dictionary, and __jinit__ really does seem to be missing:

    PyListObject *keys = PyDict_Keys(type->tp_dict);
    Py_ssize_t i;
    for (i = 0; i < PyList_Size(keys); i++) {
         PyObject *key = PyList_GetItem(keys, i);
         PyObject_Print(key, stdout, 0);
         printf("-> %ld\n", i);
    }

Here are the dictionary keys I found:

'__repr__'-> 0
'__hash__'-> 1
'__str__'-> 2
'__getattribute__'-> 3
'__setattr__'-> 4
'__delattr__'-> 5
'__lt__'-> 6
'__le__'-> 7
'__eq__'-> 8
'__ne__'-> 9
'__gt__'-> 10
'__ge__'-> 11
'__init__'-> 12
'__new__'-> 13
'__doc__'-> 14
'jclass'-> 15
'hasOutput'-> 16
'getOutput'-> 17
'getOutputFormats'-> 18
'isWriteJasper'-> 19
'hasAskFilter'-> 20
'getDbType'-> 21
...more non-dunder stuff...
'hashCode'-> 64
'getClass'-> 65
'notify'-> 66
'notifyAll'-> 67

Is the presence of jclass above significant? Is there some problem with the Java type that makes it unusable?

ShaheedHaque commented 5 years ago

I dusted off enough of my rusty Java to realise that the constructor of the class is package-private, and not public. Hence, JType_ProcessClassConstructors() does not expose it.

ShaheedHaque commented 5 years ago

I am reopening this to address the point that help(Config) listed a __jinit__ which was not usable.