From 176dc7091978b6b16783e73d55b50679c411fc22 Mon Sep 17 00:00:00 2001 From: Christopher Neugebauer Date: Tue, 16 Sep 2014 20:17:13 +1000 Subject: [PATCH 1/7] First pass at a nicer signatures API with tests --- jnius/signatures.py | 72 +++++++++++++++++++++++++ tests/test_signature.py | 117 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+) create mode 100644 jnius/signatures.py create mode 100644 tests/test_signature.py diff --git a/jnius/signatures.py b/jnius/signatures.py new file mode 100644 index 0000000..4b69387 --- /dev/null +++ b/jnius/signatures.py @@ -0,0 +1,72 @@ +''' +signatures.py +============= + +A handy API for writing JNI signatures easily + +Author: chrisjrn + +''' + +__version__ = '0.0.1' + +from . import JavaClass +from . import java_method + + +''' Type specifiers for primitives ''' + +class _SignaturePrimitive(object): + _spec = "" + +def _MakeSignaturePrimitive(name, spec): + class __Primitive(_SignaturePrimitive): + ''' PyJnius signature for Java %s type ''' % name + _name = name + _spec = spec + __name__ = "j" + name + return __Primitive + +jboolean = _MakeSignaturePrimitive("boolean", "Z") +jbyte = _MakeSignaturePrimitive("byte", "B") +jchar = _MakeSignaturePrimitive("char", "C") +jdouble = _MakeSignaturePrimitive("double", "D") +jfloat = _MakeSignaturePrimitive("float", "F") +jint = _MakeSignaturePrimitive("int", "I") +jlong = _MakeSignaturePrimitive("long", "J") +jshort = _MakeSignaturePrimitive("short", "S") +jvoid = _MakeSignaturePrimitive("void", "V") + +def JArray(of_type): + ''' Marks that this is an array of the given Primitive of JavaClass + type specified. ''' + + spec = "[" + _jni_type_spec(of_type) + return _MakeSignaturePrimitive("array", spec) + +def java_signature(returns, takes): + ''' Alternative version of @java_method that takes JavaClass + objects to produce the method signature. ''' + + sig = _produce_sig(returns, takes) + return java_method(sig) + +def _produce_sig(returns, takes): + ''' Produces a JNI method signature. ''' + out_takes = [] + for arg in takes: + out_takes.append(_jni_type_spec(arg)) + + return "(" + "".join(out_takes) + ")" + _jni_type_spec(returns) + +def _jni_type_spec(jclass): + ''' Produces a JNI type specification string for the given argument. + If the argument is a jnius.JavaClass, it produces the JNI type spec + for the class. Signature primitives return their stored type spec. + ''' + + if issubclass(jclass, JavaClass): + return "L" + jclass.__javaclass__ + ";" + elif issubclass(jclass, _SignaturePrimitive): + return jclass._spec + \ No newline at end of file diff --git a/tests/test_signature.py b/tests/test_signature.py new file mode 100644 index 0000000..a0166e0 --- /dev/null +++ b/tests/test_signature.py @@ -0,0 +1,117 @@ +import unittest + +from jnius import autoclass, java_method, PythonJavaClass, cast + +from jnius.signatures import * + +JObject = autoclass('java/lang/Object') +JString = autoclass('java/lang/String') + +class TestImplemIterator(PythonJavaClass): + __javainterfaces__ = [ + 'java/util/ListIterator', ] + + def __init__(self, collection, index=0): + super(TestImplemIterator, self).__init__() + self.collection = collection + self.index = index + + @java_signature(jboolean, ()) + def hasNext(self): + return self.index < len(self.collection.data) - 1 + + @java_signature(JObject, ()) + def next(self): + obj = self.collection.data[self.index] + self.index += 1 + return obj + + @java_signature(jboolean, ()) + def hasPrevious(self): + return self.index >= 0 + + @java_signature(JObject, ()) + def previous(self): + self.index -= 1 + obj = self.collection.data[self.index] + return obj + + @java_signature(jint, ()) + def previousIndex(self): + return self.index - 1 + + @java_signature(JString, ()) + def toString(self): + return repr(self) + + @java_signature(JObject, (jint, )) + def get(self, index): + return self.collection.data[index - 1] + + @java_signature(jvoid, (JObject, )) + def set(self, obj): + self.collection.data[self.index - 1] = obj + + +class TestImplem(PythonJavaClass): + __javainterfaces__ = ['java/util/List'] + + def __init__(self, *args): + super(TestImplem, self).__init__(*args) + self.data = list(args) + + @java_signature(autoclass("java.util.Iterator"), ()) + def iterator(self): + it = TestImplemIterator(self) + return it + + @java_signature(JString, ()) + def toString(self): + return repr(self) + + @java_signature(jint, ()) + def size(self): + return len(self.data) + + @java_signature(JObject, (jint,)) + def get(self, index): + return self.data[index] + + @java_signature(JObject, (jint, JObject)) + def set(self, index, obj): + old_object = self.data[index] + self.data[index] = obj + return old_object + + @java_signature(JArray(JObject), ()) + def toArray(self): + return self.data + + @java_signature(autoclass("java.util.ListIterator"), ()) + def listIterator(self): + it = TestImplemIterator(self) + return it + + # TODO cover this case of listIterator. + @java_method('(I)Ljava/util/ListIterator;', + name='ListIterator') + def listIteratorI(self, index): + it = TestImplemIterator(self, index) + return it + + +from jnius.reflect import autoclass + +class SignaturesTest(unittest.TestCase): + + def test_construct_stack_from_testimplem(self): + Stack = autoclass("java.util.Stack") + pyjlist = TestImplem(1, 2, 3, 4, 5, 6, 7) + stack = Stack() + stack.addAll(pyjlist) + self.assertEquals(7, pyjlist.size()) + self.assertEquals(stack.size(), pyjlist.size()) + array = pyjlist.toArray + + + From 7a17a9cc3df82372eb3809c4b011f12548cb945b Mon Sep 17 00:00:00 2001 From: Christopher Neugebauer Date: Wed, 17 Sep 2014 17:30:37 +1000 Subject: [PATCH 2/7] =?UTF-8?q?Renames=20=E2=80=9Cjava=5Fsignature?= =?UTF-8?q?=E2=80=9D=20to=20=E2=80=9Cwith=5Fsignature=E2=80=9D;=20adds=20?= =?UTF-8?q?=E2=80=9Csignature=E2=80=9D=20method=20to=20public=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jnius/signatures.py | 10 +++++----- tests/test_signature.py | 30 +++++++++++++++--------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/jnius/signatures.py b/jnius/signatures.py index 4b69387..c7333bf 100644 --- a/jnius/signatures.py +++ b/jnius/signatures.py @@ -40,19 +40,19 @@ jvoid = _MakeSignaturePrimitive("void", "V") def JArray(of_type): ''' Marks that this is an array of the given Primitive of JavaClass type specified. ''' - + spec = "[" + _jni_type_spec(of_type) return _MakeSignaturePrimitive("array", spec) -def java_signature(returns, takes): +def with_signature(returns, takes): ''' Alternative version of @java_method that takes JavaClass objects to produce the method signature. ''' - sig = _produce_sig(returns, takes) + sig = signature(returns, takes) return java_method(sig) -def _produce_sig(returns, takes): - ''' Produces a JNI method signature. ''' +def signature(returns, takes): + ''' Produces a JNI method signature, taking the provided arguments and returning the given return type. ''' out_takes = [] for arg in takes: out_takes.append(_jni_type_spec(arg)) diff --git a/tests/test_signature.py b/tests/test_signature.py index a0166e0..c849693 100644 --- a/tests/test_signature.py +++ b/tests/test_signature.py @@ -16,39 +16,39 @@ class TestImplemIterator(PythonJavaClass): self.collection = collection self.index = index - @java_signature(jboolean, ()) + @with_signature(jboolean, ()) def hasNext(self): return self.index < len(self.collection.data) - 1 - @java_signature(JObject, ()) + @with_signature(JObject, ()) def next(self): obj = self.collection.data[self.index] self.index += 1 return obj - @java_signature(jboolean, ()) + @with_signature(jboolean, ()) def hasPrevious(self): return self.index >= 0 - @java_signature(JObject, ()) + @with_signature(JObject, ()) def previous(self): self.index -= 1 obj = self.collection.data[self.index] return obj - @java_signature(jint, ()) + @with_signature(jint, ()) def previousIndex(self): return self.index - 1 - @java_signature(JString, ()) + @with_signature(JString, ()) def toString(self): return repr(self) - @java_signature(JObject, (jint, )) + @with_signature(JObject, (jint, )) def get(self, index): return self.collection.data[index - 1] - @java_signature(jvoid, (JObject, )) + @with_signature(jvoid, (JObject, )) def set(self, obj): self.collection.data[self.index - 1] = obj @@ -60,34 +60,34 @@ class TestImplem(PythonJavaClass): super(TestImplem, self).__init__(*args) self.data = list(args) - @java_signature(autoclass("java.util.Iterator"), ()) + @with_signature(autoclass("java.util.Iterator"), ()) def iterator(self): it = TestImplemIterator(self) return it - @java_signature(JString, ()) + @with_signature(JString, ()) def toString(self): return repr(self) - @java_signature(jint, ()) + @with_signature(jint, ()) def size(self): return len(self.data) - @java_signature(JObject, (jint,)) + @with_signature(JObject, (jint,)) def get(self, index): return self.data[index] - @java_signature(JObject, (jint, JObject)) + @with_signature(JObject, (jint, JObject)) def set(self, index, obj): old_object = self.data[index] self.data[index] = obj return old_object - @java_signature(JArray(JObject), ()) + @with_signature(JArray(JObject), ()) def toArray(self): return self.data - @java_signature(autoclass("java.util.ListIterator"), ()) + @with_signature(autoclass("java.util.ListIterator"), ()) def listIterator(self): it = TestImplemIterator(self) return it From c222e479fa89afeb27d6ba7b989650723a146cf4 Mon Sep 17 00:00:00 2001 From: Christopher Neugebauer Date: Wed, 17 Sep 2014 17:45:07 +1000 Subject: [PATCH 3/7] Documents signatures.py better --- jnius/signatures.py | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/jnius/signatures.py b/jnius/signatures.py index c7333bf..89e6bff 100644 --- a/jnius/signatures.py +++ b/jnius/signatures.py @@ -6,6 +6,30 @@ A handy API for writing JNI signatures easily Author: chrisjrn +This module aims to provide a more human-friendly API for +wiring up Java proxy methods in PyJnius. + +You can use the signature function to produce JNI method +signautures for methods; passing PyJnius JavaClass classes +as return or argument types; provided here are annotations +representing Java's primitive and array times. + +Methods can return just a standard primitive type: + +>>> signature(jint, ()) +'()I' + +>>> s.signature(jvoid, [jint]) +'(I)V' + +Or you can use autoclass proxies to specify Java classes +for return types. + +>>> from jnius import autoclass +>>> String = autoclass("java.lang.String") +>>> signature(String, ()) +'()Ljava/lang/String;' + ''' __version__ = '0.0.1' @@ -38,8 +62,8 @@ jshort = _MakeSignaturePrimitive("short", "S") jvoid = _MakeSignaturePrimitive("void", "V") def JArray(of_type): - ''' Marks that this is an array of the given Primitive of JavaClass - type specified. ''' + ''' Signature helper for identifying arrays of a given object or + primitive type. ''' spec = "[" + _jni_type_spec(of_type) return _MakeSignaturePrimitive("array", spec) @@ -52,7 +76,9 @@ def with_signature(returns, takes): return java_method(sig) def signature(returns, takes): - ''' Produces a JNI method signature, taking the provided arguments and returning the given return type. ''' + ''' Produces a JNI method signature, taking the provided arguments + and returning the given return type. ''' + out_takes = [] for arg in takes: out_takes.append(_jni_type_spec(arg)) From 422b5b122c92b2ff26a376ba89dea58ff79d84c9 Mon Sep 17 00:00:00 2001 From: Christopher Neugebauer Date: Wed, 17 Sep 2014 17:50:31 +1000 Subject: [PATCH 4/7] Improves the help rendering for signatures.py --- jnius/signatures.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/jnius/signatures.py b/jnius/signatures.py index 89e6bff..785897d 100644 --- a/jnius/signatures.py +++ b/jnius/signatures.py @@ -40,15 +40,16 @@ from . import java_method ''' Type specifiers for primitives ''' -class _SignaturePrimitive(object): +class _JavaSignaturePrimitive(object): _spec = "" def _MakeSignaturePrimitive(name, spec): - class __Primitive(_SignaturePrimitive): + class __Primitive(_JavaSignaturePrimitive): ''' PyJnius signature for Java %s type ''' % name _name = name _spec = spec - __name__ = "j" + name + __Primitive.__name__ = "j" + name + return __Primitive jboolean = _MakeSignaturePrimitive("boolean", "Z") From 4948cc0e8eee1641418bed73eca0b681648dce5e Mon Sep 17 00:00:00 2001 From: Christopher Neugebauer Date: Wed, 17 Sep 2014 17:51:56 +1000 Subject: [PATCH 5/7] (oops) --- jnius/signatures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jnius/signatures.py b/jnius/signatures.py index 785897d..9bc1830 100644 --- a/jnius/signatures.py +++ b/jnius/signatures.py @@ -94,6 +94,6 @@ def _jni_type_spec(jclass): if issubclass(jclass, JavaClass): return "L" + jclass.__javaclass__ + ";" - elif issubclass(jclass, _SignaturePrimitive): + elif issubclass(jclass, _JavaSignaturePrimitive): return jclass._spec \ No newline at end of file From 2f838e2facb9b8133ffe2513eca6ad6cd7f4a771 Mon Sep 17 00:00:00 2001 From: Christopher Neugebauer Date: Wed, 17 Sep 2014 17:56:30 +1000 Subject: [PATCH 6/7] Adds tests for raw signature creation --- tests/test_signature.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/test_signature.py b/tests/test_signature.py index c849693..e985c95 100644 --- a/tests/test_signature.py +++ b/tests/test_signature.py @@ -113,5 +113,34 @@ class SignaturesTest(unittest.TestCase): self.assertEquals(stack.size(), pyjlist.size()) array = pyjlist.toArray + def test_basic_signatures(self): + + # Void + sig = signature(jvoid, []) + self.assertEquals(sig, "()V") + + # Int method + sig = signature(jint, []) + self.assertEquals(sig, "()I") + + # Object return method + String = autoclass("java.lang.String") + sig = signature(String, []) + self.assertEquals(sig, "()Ljava/lang/String;") + + # Return void, takes objects as parameters + sig = signature(jvoid, [String, String]) + self.assertEquals(sig, "(Ljava/lang/String;Ljava/lang/String;)V") + + # Array return + sig = signature(JArray(jint), []) + self.assertEquals(sig, "()[I") + + # Multiple array parameter types + sig = signature(jvoid, [JArray(jint), JArray(jboolean)]) + self.assertEquals(sig, "([I[Z)V") + + + From 696924d54bb884408c64d18c6e79dc9fff14399d Mon Sep 17 00:00:00 2001 From: Christopher Neugebauer Date: Wed, 17 Sep 2014 18:07:41 +1000 Subject: [PATCH 7/7] More tests for signatures! --- tests/test_signature.py | 82 +++++++++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 27 deletions(-) diff --git a/tests/test_signature.py b/tests/test_signature.py index e985c95..07c9422 100644 --- a/tests/test_signature.py +++ b/tests/test_signature.py @@ -6,6 +6,7 @@ from jnius.signatures import * JObject = autoclass('java/lang/Object') JString = autoclass('java/lang/String') +JListIterator = autoclass("java.util.ListIterator") class TestImplemIterator(PythonJavaClass): __javainterfaces__ = [ @@ -16,39 +17,39 @@ class TestImplemIterator(PythonJavaClass): self.collection = collection self.index = index - @with_signature(jboolean, ()) + @with_signature(jboolean, []) def hasNext(self): return self.index < len(self.collection.data) - 1 - @with_signature(JObject, ()) + @with_signature(JObject, []) def next(self): obj = self.collection.data[self.index] self.index += 1 return obj - @with_signature(jboolean, ()) + @with_signature(jboolean, []) def hasPrevious(self): return self.index >= 0 - @with_signature(JObject, ()) + @with_signature(JObject, []) def previous(self): self.index -= 1 obj = self.collection.data[self.index] return obj - @with_signature(jint, ()) + @with_signature(jint, []) def previousIndex(self): return self.index - 1 - @with_signature(JString, ()) + @with_signature(JString, []) def toString(self): return repr(self) - @with_signature(JObject, (jint, )) + @with_signature(JObject, [jint]) def get(self, index): return self.collection.data[index - 1] - @with_signature(jvoid, (JObject, )) + @with_signature(jvoid, [JObject]) def set(self, obj): self.collection.data[self.index - 1] = obj @@ -60,40 +61,40 @@ class TestImplem(PythonJavaClass): super(TestImplem, self).__init__(*args) self.data = list(args) - @with_signature(autoclass("java.util.Iterator"), ()) + @with_signature(autoclass("java.util.Iterator"), []) def iterator(self): it = TestImplemIterator(self) return it - @with_signature(JString, ()) + @with_signature(JString, []) def toString(self): return repr(self) - @with_signature(jint, ()) + @with_signature(jint, []) def size(self): return len(self.data) - @with_signature(JObject, (jint,)) + @with_signature(JObject, [jint]) def get(self, index): return self.data[index] - @with_signature(JObject, (jint, JObject)) + @with_signature(JObject, [jint, JObject]) def set(self, index, obj): old_object = self.data[index] self.data[index] = obj return old_object - @with_signature(JArray(JObject), ()) + @with_signature(JArray(JObject), []) def toArray(self): return self.data - @with_signature(autoclass("java.util.ListIterator"), ()) + @with_signature(JListIterator, []) def listIterator(self): it = TestImplemIterator(self) return it # TODO cover this case of listIterator. - @java_method('(I)Ljava/util/ListIterator;', + @java_method(signature(JListIterator, [jint]), name='ListIterator') def listIteratorI(self, index): it = TestImplemIterator(self, index) @@ -111,36 +112,63 @@ class SignaturesTest(unittest.TestCase): stack.addAll(pyjlist) self.assertEquals(7, pyjlist.size()) self.assertEquals(stack.size(), pyjlist.size()) - array = pyjlist.toArray + array = pyjlist.toArray() - def test_basic_signatures(self): + def test_return_types(self): # Void sig = signature(jvoid, []) self.assertEquals(sig, "()V") - # Int method + # Boolean + sig = signature(jboolean, []) + self.assertEquals(sig, "()Z") + + # Byte + sig = signature(jbyte, []) + self.assertEquals(sig, "()B") + + # Char + sig = signature(jchar, []) + self.assertEquals(sig, "()C") + + # Double + sig = signature(jdouble, []) + self.assertEquals(sig, "()D") + + # Float + sig = signature(jfloat, []) + self.assertEquals(sig, "()F") + + # Int sig = signature(jint, []) self.assertEquals(sig, "()I") + # Long + sig = signature(jlong, []) + self.assertEquals(sig, "()J") + + # Short + sig = signature(jshort, []) + self.assertEquals(sig, "()S") + # Object return method String = autoclass("java.lang.String") sig = signature(String, []) self.assertEquals(sig, "()Ljava/lang/String;") - # Return void, takes objects as parameters - sig = signature(jvoid, [String, String]) - self.assertEquals(sig, "(Ljava/lang/String;Ljava/lang/String;)V") - # Array return sig = signature(JArray(jint), []) self.assertEquals(sig, "()[I") + def test_params(self): + String = autoclass("java.lang.String") + + # Return void, takes objects as parameters + sig = signature(jvoid, [String, String]) + self.assertEquals(sig, "(Ljava/lang/String;Ljava/lang/String;)V") + # Multiple array parameter types sig = signature(jvoid, [JArray(jint), JArray(jboolean)]) self.assertEquals(sig, "([I[Z)V") - - - -