Merge pull request #299 from drmoose/python-implementing-interface-fixes

Fixes for Implementing a Java Interface (DONE BUT CANT REMOVE wip FLAG)
This commit is contained in:
Gabriel Pettier 2018-04-30 14:59:46 +02:00 committed by GitHub
commit 61149f2043
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 184 additions and 7 deletions

View File

@ -11,6 +11,7 @@ __version__ = '1.1.2-dev'
from .jnius import * # noqa
from .reflect import * # noqa
from six import with_metaclass
# XXX monkey patch methods that cannot be in cython.
# Cython doesn't allow to set new attribute on methods it compiled
@ -18,7 +19,7 @@ from .reflect import * # noqa
HASHCODE_MAX = 2 ** 31 - 1
class PythonJavaClass_(PythonJavaClass):
class PythonJavaClass_(with_metaclass(MetaJavaBase, PythonJavaClass)):
@java_method('()I', name='hashCode')
def hashCode(self):

View File

@ -87,8 +87,8 @@ Python::
__all__ = ('JavaObject', 'JavaClass', 'JavaMethod', 'JavaField',
'JavaStaticMethod', 'JavaStaticField', 'JavaMultipleMethod',
'MetaJavaClass', 'JavaException', 'cast', 'find_javaclass',
'PythonJavaClass', 'java_method', 'detach')
'MetaJavaBase', 'MetaJavaClass', 'JavaException', 'cast',
'find_javaclass', 'PythonJavaClass', 'java_method', 'detach')
from libc.stdlib cimport malloc, free
from functools import partial

View File

@ -73,6 +73,9 @@ cdef void populate_args(JNIEnv *j_env, tuple definition_args, jvalue *j_args, ar
pc = py_arg
# get the java class
jc = pc.j_self
if jc is None:
pc._init_j_self_ptr()
jc = pc.j_self
# get the localref
j_args[index].l = jc.j_self.obj
elif isinstance(py_arg, type):
@ -332,7 +335,7 @@ cdef convert_jarray_to_python(JNIEnv *j_env, definition, jobject j_object):
cdef jobject convert_python_to_jobject(JNIEnv *j_env, definition, obj) except *:
cdef jobject retobject, retsubobject
cdef jclass retclass
cdef jmethodID redmidinit
cdef jmethodID redmidinit = NULL
cdef jvalue j_ret[1]
cdef JavaClass jc
cdef JavaObject jo
@ -376,6 +379,9 @@ cdef jobject convert_python_to_jobject(JNIEnv *j_env, definition, obj) except *:
pc = obj
# get the java class
jc = pc.j_self
if jc is None:
pc._init_j_self_ptr()
jc = pc.j_self
# get the localref
return jc.j_self.obj
elif isinstance(obj, (tuple, list)):

View File

@ -1,3 +1,6 @@
from cpython cimport PyObject
from warnings import warn
class JavaException(Exception):
'''Can be a real java exception, or just an exception from the wrapper.
'''
@ -36,15 +39,108 @@ cdef class JavaClassStorage:
self.j_cls = NULL
class MetaJavaBase(type):
def __instancecheck__(cls, value):
cdef JNIEnv *j_env = get_jnienv()
cdef JavaClassStorage meta = getattr(cls, '__cls_storage', None)
cdef JavaObject jo
cdef JavaClass jc
cdef PythonJavaClass pc
cdef jobject obj = NULL
cdef jclass proxy = j_env[0].FindClass(j_env, <char *>'java/lang/reflect/Proxy')
cdef jclass nih
cdef jmethodID meth
cdef object wrapped_python
if isinstance(value, basestring):
obj = j_env[0].NewStringUTF(j_env, <char *>"")
elif isinstance(value, JavaClass):
jc = value
obj = jc.j_self.obj
elif isinstance(value, JavaObject):
jo = value
obj = jo.obj
elif isinstance(value, PythonJavaClass):
pc = value
jc = pc.j_self
if jc is None:
pc._init_j_self_ptr()
jc = pc.j_self
obj = jc.j_self.obj
if NULL != obj:
if meta is not None and 0 != j_env[0].IsInstanceOf(j_env, obj, meta.j_cls):
return True
if NULL != proxy and 0 != j_env[0].IsInstanceOf(j_env, obj, proxy):
# value is a proxy object. check whether it's one of ours
meth = j_env[0].GetStaticMethodID(j_env, proxy, <char *>'getInvocationHandler',
<char *>'(Ljava/lang/Object;)Ljava/lang/reflect/InvocationHandler;'
)
obj = j_env[0].CallStaticObjectMethod(j_env, proxy, meth, obj)
nih = j_env[0].FindClass(j_env, <char *>'org/jnius/NativeInvocationHandler')
if NULL == nih:
# nih is not reliably in the classpath. don't crash if it's
# not there, because it's impossible to get this far with
# a PythonJavaClass without it, so we can safely assume this
# is just a POJO from elsewhere.
j_env[0].ExceptionClear(j_env)
else:
meth = j_env[0].GetMethodID(j_env, nih, <char *>'getPythonObjectPointer',
<char *>'()J')
if NULL == meth:
# Perhaps we have an old nih
j_env[0].ExceptionClear(j_env)
warn("The org.jnius.NativeInvocationHandler on your classpath"
" is out of date. isinstance will be unreliable.")
else:
wrapped_python = <object><PyObject *>j_env[0].CallLongMethod(j_env, obj, meth)
if wrapped_python is not value and wrapped_python is not None:
if isinstance(wrapped_python, cls):
return True
# All else fails, defer to python.
return super(MetaJavaBase, cls).__instancecheck__(value)
cdef dict jclass_register = {}
class MetaJavaClass(type):
class MetaJavaClass(MetaJavaBase):
def __new__(meta, classname, bases, classDict):
meta.resolve_class(classDict)
tp = type.__new__(meta, str(classname), bases, classDict)
jclass_register[classDict['__javaclass__']] = tp
return tp
def __subclasscheck__(cls, value):
cdef JNIEnv *j_env = get_jnienv()
cdef JavaClassStorage me = getattr(cls, '__cls_storage')
cdef JavaClassStorage jcs
cdef JavaClass jc
cdef jclass obj = NULL
if isinstance(value, JavaClass):
jc = value
obj = jc.j_self.obj
else:
jcs = getattr(value, '__cls_storage', None)
if jcs is not None:
obj = jcs.j_cls
if NULL == obj:
for interface in getattr(value, '__javainterfaces__', []):
obj = j_env[0].FindClass(j_env, str_for_c(interface))
if obj == NULL:
j_env[0].ExceptionClear(j_env)
elif 0 != j_env[0].IsAssignableFrom(j_env, obj, me.j_cls):
return True
else:
if 0 != j_env[0].IsAssignableFrom(j_env, obj, me.j_cls):
return True
return super(MetaJavaClass, cls).__subclasscheck__(value)
@staticmethod
def get_javaclass(name):
return jclass_register.get(name)

View File

@ -25,6 +25,9 @@ cdef class PythonJavaClass(object):
self.j_self = None
def __init__(self, *args, **kwargs):
self._init_j_self_ptr()
def _init_j_self_ptr(self):
javacontext = 'system'
if hasattr(self, '__javacontext__'):
javacontext = self.__javacontext__

View File

@ -315,7 +315,7 @@ cdef int calculate_score(sign_args, args, is_varargs=False) except *:
# if it's a generic object, accept python string, or any java
# class/object
if r == 'java/lang/Object':
if isinstance(arg, JavaClass) or isinstance(arg, JavaObject):
if isinstance(arg, (PythonJavaClass, JavaClass, JavaObject)):
score += 10
continue
elif isinstance(arg, basestring):

View File

@ -33,5 +33,9 @@ public class NativeInvocationHandler implements InvocationHandler {
return ret;
}
public long getPythonObjectPointer() {
return ptr;
}
native Object invoke0(Object proxy, Method method, Object[] args);
}

View File

@ -0,0 +1,68 @@
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
import unittest
from jnius import autoclass, java_method, PythonJavaClass
Iterable = autoclass('java.lang.Iterable')
ArrayList = autoclass('java.util.ArrayList')
Runnable = autoclass('java.lang.Runnable')
Thread = autoclass('java.lang.Thread')
Object = autoclass('java.lang.Object')
class SampleIterable(PythonJavaClass):
__javainterfaces__ = ['java/lang/Iterable']
@java_method('()Ljava/lang/Iterator;')
def iterator(self):
sample = ArrayList()
sample.add(1)
sample.add(2)
return sample.iterator()
class ExportClassTest(unittest.TestCase):
def test_is_instance(self):
array_list = ArrayList()
thread = Thread()
sample_iterable = SampleIterable()
self.assertIsInstance(sample_iterable, Iterable)
self.assertIsInstance(sample_iterable, Object)
self.assertIsInstance(sample_iterable, SampleIterable)
self.assertNotIsInstance(sample_iterable, Runnable)
self.assertNotIsInstance(sample_iterable, Thread)
self.assertIsInstance(array_list, Iterable)
self.assertIsInstance(array_list, ArrayList)
self.assertIsInstance(array_list, Object)
self.assertNotIsInstance(thread, Iterable)
self.assertIsInstance(thread, Thread)
self.assertIsInstance(thread, Runnable)
def test_subclasses_work_for_arg_matching(self):
array_list = ArrayList()
array_list.add(SampleIterable())
self.assertIsInstance(array_list.get(0), Iterable)
self.assertIsInstance(array_list.get(0), SampleIterable)
def assertIsSubclass(self, cls, parent):
if not issubclass(cls, parent):
self.fail("%s is not a subclass of %s" %
(cls.__name__, parent.__name__))
def assertNotIsSubclass(self, cls, parent):
if issubclass(cls, parent):
self.fail("%s is a subclass of %s" %
(cls.__name__, parent.__name__))
def test_is_subclass(self):
self.assertIsSubclass(Thread, Runnable)
self.assertIsSubclass(ArrayList, Iterable)
self.assertIsSubclass(ArrayList, Object)
self.assertIsSubclass(SampleIterable, Iterable)
self.assertNotIsSubclass(Thread, Iterable)
self.assertNotIsSubclass(ArrayList, Runnable)
self.assertNotIsSubclass(Runnable, Thread)
self.assertNotIsSubclass(Iterable, SampleIterable)

View File

@ -15,7 +15,6 @@ class TestImplemIterator(PythonJavaClass):
'java/util/ListIterator', ]
def __init__(self, collection, index=0):
super(TestImplemIterator, self).__init__()
self.collection = collection
self.index = index