mirror of https://github.com/kivy/pyjnius.git
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:
commit
61149f2043
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue