From cf93f7afc8cf013bb2bef87cda009c69fcdb9378 Mon Sep 17 00:00:00 2001 From: Len Boyette Date: Sun, 24 Nov 2013 19:37:06 -0500 Subject: [PATCH 01/74] Removed hard-coded linux directory in the include_dirs --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 1715e82..b8f7aee 100644 --- a/setup.py +++ b/setup.py @@ -70,7 +70,7 @@ else: cpu = 'i386' if sys.maxint == 2147483647 else 'amd64' include_dirs = [ join(jdk_home, 'include'), - join(jdk_home, 'include', 'linux')] + join(jdk_home, 'include', platform)] library_dirs = [join(jre_home, 'lib', cpu, 'server')] extra_link_args = ['-Wl,-rpath', library_dirs[0]] libraries = ['jvm'] From f88f3ed170ee2fd2f79c1ec0ab9aa8671c151664 Mon Sep 17 00:00:00 2001 From: Len Boyette Date: Sun, 24 Nov 2013 21:54:07 -0500 Subject: [PATCH 02/74] Compile in Windows successfully Fixed jvm.lib reference --- jnius/jnius_jvm_desktop.pxi | 2 +- setup.py | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/jnius/jnius_jvm_desktop.pxi b/jnius/jnius_jvm_desktop.pxi index 114647a..c20ed09 100644 --- a/jnius/jnius_jvm_desktop.pxi +++ b/jnius/jnius_jvm_desktop.pxi @@ -1,7 +1,7 @@ # on desktop, we need to create an env :) # example taken from http://www.inonit.com/cygwin/jni/invocationApi/c.html -cdef extern jint JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args) +cdef extern jint __stdcall JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args) cdef extern from "jni.h": int JNI_VERSION_1_4 jboolean JNI_FALSE diff --git a/setup.py b/setup.py index b8f7aee..a513b50 100644 --- a/setup.py +++ b/setup.py @@ -68,10 +68,16 @@ else: if not jre_home: raise Exception('Unable to determine JRE_HOME') cpu = 'i386' if sys.maxint == 2147483647 else 'amd64' + include_dirs = [ join(jdk_home, 'include'), join(jdk_home, 'include', platform)] - library_dirs = [join(jre_home, 'lib', cpu, 'server')] + if platform == 'win32': + library_dirs = [ + join(jdk_home, 'lib'), + join(jre_home, 'bin', 'server')] + else: + library_dirs = [join(jre_home, 'lib', cpu, 'server')] extra_link_args = ['-Wl,-rpath', library_dirs[0]] libraries = ['jvm'] From d7bf5aa8a183452f257c50942696a27f1bbb4286 Mon Sep 17 00:00:00 2001 From: Len Boyette Date: Mon, 25 Nov 2013 18:19:54 -0500 Subject: [PATCH 03/74] Improved JDK_HOME identification in Windows --- setup.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index a513b50..aa56387 100644 --- a/setup.py +++ b/setup.py @@ -54,9 +54,18 @@ else: # otherwise, we need to search the JDK_HOME jdk_home = environ.get('JDK_HOME') if not jdk_home: - jdk_home = subprocess.Popen('readlink -f `which javac` | sed "s:bin/javac::"', - shell=True, stdout=subprocess.PIPE).communicate()[0].strip() - if not jdk_home: + if platform == 'win32': + env_var = environ.get('JAVA_HOME') + if env_var and 'jdk' in env_var: + jdk_home = env_var + + # Remove /bin if it's appended to JAVA_HOME + if jdk_home[-3:] == 'bin': + jdk_home = jdk_home[:-4] + else: + jdk_home = subprocess.Popen('readlink -f `which javac` | sed "s:bin/javac::"', + shell=True, stdout=subprocess.PIPE).communicate()[0].strip() + if not jdk_home or not exists(jdk_home): raise Exception('Unable to determine JDK_HOME') jre_home = environ.get('JRE_HOME') From 445ee20643f0545ab97d1ea263728cdb092d7575 Mon Sep 17 00:00:00 2001 From: kevlened Date: Mon, 25 Nov 2013 23:44:46 -0500 Subject: [PATCH 04/74] Fixed platform selection error --- setup.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index aa56387..d992005 100644 --- a/setup.py +++ b/setup.py @@ -78,9 +78,14 @@ else: raise Exception('Unable to determine JRE_HOME') cpu = 'i386' if sys.maxint == 2147483647 else 'amd64' + if platform == 'win32': + incl_dir = join(jdk_home, 'include', 'win32') + else: + incl_dir = join(jdk_home, 'include', 'linux') + include_dirs = [ join(jdk_home, 'include'), - join(jdk_home, 'include', platform)] + incl_dir] if platform == 'win32': library_dirs = [ join(jdk_home, 'lib'), From 33cddb2d533f976149932eec14f241391b61f115 Mon Sep 17 00:00:00 2001 From: Remi Rampin Date: Fri, 10 Jan 2014 13:37:25 -0500 Subject: [PATCH 05/74] Removes LGPL license in 'COPYING' There is already the MIT license in 'LICENSE'. --- COPYING | 165 -------------------------------------------------------- 1 file changed, 165 deletions(-) delete mode 100644 COPYING diff --git a/COPYING b/COPYING deleted file mode 100644 index cca7fc2..0000000 --- a/COPYING +++ /dev/null @@ -1,165 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. From d5d2926190e5998c82006deb429ab9373612a4d6 Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Tue, 21 Jan 2014 09:48:48 +0100 Subject: [PATCH 06/74] raise an exception if the signature have invalid character + fix pyjnius documentation about array declaration in signature --- docs/source/api.rst | 8 ++++++-- jnius/jnius_utils.pxi | 4 ++++ tests/test_proxy.py | 11 +++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/docs/source/api.rst b/docs/source/api.rst index 959be7c..d819e02 100644 --- a/docs/source/api.rst +++ b/docs/source/api.rst @@ -293,7 +293,7 @@ All the types for any part of the signature can be one of: * D = represent a java/lang/Double; * V = represent void, available only for the return type -All the types can have the `[]` suffix to design an array. The return type can be `V` or empty. +All the types can have the `[` prefix to design an array. The return type can be `V` or empty. A signature like:: @@ -302,10 +302,14 @@ A signature like:: -> argument 2 is a java.util.List object -> the method doesn't return anything. - (java.util.Collection, java.lang.Object[]); + (java.util.Collection;[java.lang.Object;)V -> argument 1 is a Collection -> argument 2 is an array of Object -> nothing is returned + + ([B)Z + -> argument 1 is a Byte [] + -> a boolean is returned When you implement Java in Python, the signature of the Java method must match. diff --git a/jnius/jnius_utils.pxi b/jnius/jnius_utils.pxi index 4318a96..c251528 100644 --- a/jnius/jnius_utils.pxi +++ b/jnius/jnius_utils.pxi @@ -27,6 +27,10 @@ cdef parse_definition(definition): if c == 'L': c, argdef = argdef.split(';', 1) args.append(prefix + c + ';') + continue + + raise Exception('Invalid "{}" character in definition "{}"'.format( + c, definition[1:])) return ret, tuple(args) diff --git a/tests/test_proxy.py b/tests/test_proxy.py index f89f80f..7a5d7be 100644 --- a/tests/test_proxy.py +++ b/tests/test_proxy.py @@ -96,6 +96,14 @@ class TestImplem(PythonJavaClass): return it +class TestBadSignature(PythonJavaClass): + __javainterfaces__ = ['java/util/List'] + + @java_method('(Landroid/bluetooth/BluetoothDevice;IB[])V') + def bad_signature(self, *args): + pass + + print '2: instanciate the class, with some data' a = TestImplem(*range(10)) print a @@ -140,3 +148,6 @@ print '-> Collections.max(a)' print Collections.max(a2) #print '-> Collections.shuffle(a)' #print Collections.shuffle(a2) + +# test bad signature +TestBadSignature() From 2521fbd9c92ff9a9229e6f7ee87ebdf675dc952c Mon Sep 17 00:00:00 2001 From: Richard Larkin Date: Fri, 7 Mar 2014 14:57:30 +0200 Subject: [PATCH 07/74] doc: revisions to README.md --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 7a7acdf..e27cc67 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ PyJNIus ======= -Python module to access Java class as Python class, using JNI. +A Python module to access Java classes as Python classes using JNI. -(Work in progress.) +PyJNIus is a "Work In Progress". Quick overview -------------- @@ -26,22 +26,22 @@ hello Usage on desktop ---------------- -You need a java JDK installed (openjdk will do), cython, and make to build it +You need a java JDK installed (OpenJDK will do), Cython and make to build it. make -That's it! you can run the tests with +That's it! You can run the tests using make tests -To make sure everything is running right. +to ensure everything is running correctly. Usage with python-for-android ----------------------------- * Get http://github.com/kivy/python-for-android * Compile a distribution with `-m "pyjnius kivy"` -* Then, you can do this kind of things: +* Then, you can do this kind of thing: ```python from time import sleep @@ -87,9 +87,9 @@ I/python ( 5983): [0.13407529890537262, 9.4235782623291016, 2.2026655673980713] Advanced example ---------------- -When you use autoclass, it will discover all the methods and fields within the object, and resolve it. -For now, it can be better to declare and use only what you need. -The previous example can be done manually: +When you use autoclass, it will discover all the methods and fields of the +object and resolve them. For now, it is better to declare and use only what you +need. The previous example can be done manually as follows: ```python from time import sleep From 10b36fdf007b915cafa8b3b352449f5e5e12ca7c Mon Sep 17 00:00:00 2001 From: Richard Larkin Date: Fri, 7 Mar 2014 15:18:35 +0200 Subject: [PATCH 08/74] doc: revisions to docs/sources/android.rst --- docs/source/android.rst | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/docs/source/android.rst b/docs/source/android.rst index 2c069d1..90f4c2b 100644 --- a/docs/source/android.rst +++ b/docs/source/android.rst @@ -3,16 +3,18 @@ Android ======= -Android have a great and extensive API to control the device, your application -etc. Some part of the Android API is accessible directly with Pyjnius, but some -of them requires you to code in Java. +Android has a great and extensive API to control devices, your application +etc. Some parts of the Android API are directly accessible with Pyjnius but +some of them require you to code in Java. Get the DPI ----------- The `DisplayMetrics -`_ contains multiple fields that can return a lot of information about the device's screen:: +`_ +contains multiple fields that can return a lot of information about the device's +screen:: from jnius import autoclass DisplayMetrics = autoclass('android.util.DisplayMetrics') @@ -27,13 +29,13 @@ Recording an audio file By looking at the `Audio Capture `_ guide -from Android, you can see the simple step to do for recording an audio file. -Let's do in with Pyjnius:: +for Android, you can see the simple steps for recording an audio file. +Let's do it with Pyjnius:: from jnius import autoclass from time import sleep - # get the needed Java class + # get the needed Java classes MediaRecorder = autoclass('android.media.MediaRecorder') AudioSource = autoclass('android.media.MediaRecorder$AudioSource') OutputFormat = autoclass('android.media.MediaRecorder$OutputFormat') @@ -83,16 +85,17 @@ using the Android Media Player too:: mPlayer.release() -Accessing to the Activity -------------------------- +Accessing the Activity +---------------------- -This example will show how to start a new Intent. Be careful, some Intent -require you to setup some parts in the `AndroidManifest.xml`, and have some -actions done within your own Activity. This is out of the scope of Pyjnius, but -we'll show you what is the best approach for playing with it. +This example will show how to start a new Intent. Be careful: some Intents +require you to setup parts in the `AndroidManifest.xml` and have some +actions performed within your Activity. This is out of the scope of Pyjnius but +we'll show you what the best approach is for playing with it. -On Python-for-android project, you can access to the default `PythonActivity`. -Let's see an example that demonstrate the `Intent.ACTION_VIEW`:: +Using the Python-for-android project, you can access the default +`PythonActivity`. Let's look at an example that demonstrates the +`Intent.ACTION_VIEW`:: from jnius import cast from jnius import autoclass @@ -108,9 +111,9 @@ Let's see an example that demonstrate the `Intent.ACTION_VIEW`:: intent.setData(Uri.parse('http://kivy.org')) # PythonActivity.mActivity is the instance of the current Activity - # BUT, startActivity is a method from the Activity class, not our + # BUT, startActivity is a method from the Activity class, not from our # PythonActivity. - # We need to cast our class into an activity, and use it + # We need to cast our class into an activity and use it currentActivity = cast('android.app.Activity', PythonActivity.mActivity) currentActivity.startActivity(intent) @@ -120,12 +123,12 @@ Let's see an example that demonstrate the `Intent.ACTION_VIEW`:: Accelerometer access -------------------- -The accelerometer is a good example that show how you need to wrote a little +The accelerometer is a good example that shows how to write a little Java code that you can access later with Pyjnius. The `SensorManager `_ -lets you access to the device's sensors. To use it, you need to register a +lets you access the device's sensors. To use it, you need to register a `SensorEventListener `_ and overload 2 abstract methods: `onAccuracyChanged` and `onSensorChanged`. From 19d4ddc45daabdf0301941516187dcf848994389 Mon Sep 17 00:00:00 2001 From: Richard Larkin Date: Fri, 7 Mar 2014 15:24:10 +0200 Subject: [PATCH 09/74] doc: revisions to docs/source/android.rst --- docs/source/android.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/source/android.rst b/docs/source/android.rst index 90f4c2b..aeff0c1 100644 --- a/docs/source/android.rst +++ b/docs/source/android.rst @@ -21,8 +21,9 @@ screen:: metrics = DisplayMetrics() print 'DPI', metrics.getDeviceDensity() -Note: To access nested classes, use `$` like: -`autoclass('android.provider.MediaStore$Images$Media')`. +.. Note :: + To access nested classes, use `$` like: + `autoclass('android.provider.MediaStore$Images$Media')`. Recording an audio file ----------------------- From 0279d2e870edc8fc51d811de4f7810fea3e2f4bd Mon Sep 17 00:00:00 2001 From: James Laird Date: Mon, 5 May 2014 17:29:33 +1000 Subject: [PATCH 10/74] Add support for multidimensional arrays --- Makefile | 1 + jnius/jnius_conversion.pxi | 25 +++++++++++++++++++++++++ jnius/jnius_utils.pxi | 6 +++--- tests/org/jnius/MultipleDimensions.java | 16 ++++++++++++++++ tests/test_multidimension.py | 12 ++++++++++++ 5 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 tests/org/jnius/MultipleDimensions.java create mode 100644 tests/test_multidimension.py diff --git a/Makefile b/Makefile index 5f452dc..e815409 100644 --- a/Makefile +++ b/Makefile @@ -14,4 +14,5 @@ tests: build_ext cd tests && javac org/jnius/SimpleEnum.java cd tests && javac org/jnius/InterfaceWithPublicEnum.java cd tests && javac org/jnius/ClassArgument.java + cd tests && javac org/jnius/MultipleDimensions.java cd tests && env PYTHONPATH=..:$(PYTHONPATH) nosetests-2.7 -v diff --git a/jnius/jnius_conversion.pxi b/jnius/jnius_conversion.pxi index 35f3807..12a0f1a 100644 --- a/jnius/jnius_conversion.pxi +++ b/jnius/jnius_conversion.pxi @@ -274,6 +274,20 @@ cdef convert_jarray_to_python(JNIEnv *j_env, definition, jobject j_object): obj = convert_jobject_to_python(j_env, definition, j_object_item) ret.append(obj) j_env[0].DeleteLocalRef(j_env, j_object_item) + + elif r == '[': + r = definition[1:] + ret = [] + for i in range(array_size): + j_object_item = j_env[0].GetObjectArrayElement( + j_env, j_object, i) + if j_object_item == NULL: + ret.append(None) + continue + obj = convert_jarray_to_python(j_env, r, j_object_item) + ret.append(obj) + j_env[0].DeleteLocalRef(j_env, j_object_item) + else: raise JavaException('Invalid return definition for array') @@ -526,6 +540,17 @@ cdef jobject convert_pyarray_to_java(JNIEnv *j_env, definition, pyarray) except else: raise JavaException('Invalid variable used for L array', definition, pyarray) + elif definition[0] == '[': + subdef = definition[1:] + eproto = convert_pyarray_to_java(j_env, subdef, pyarray[0]) + ret = j_env[0].NewObjectArray( + j_env, array_size, j_env[0].GetObjectClass(j_env, eproto), NULL) + j_env[0].SetObjectArrayElement( + j_env, ret, 0, eproto) + for i in range(1, array_size): + j_env[0].SetObjectArrayElement( + j_env, ret, i, convert_pyarray_to_java(j_env, subdef, pyarray[i])) + else: raise JavaException('Invalid array definition', definition, pyarray) diff --git a/jnius/jnius_utils.pxi b/jnius/jnius_utils.pxi index c251528..4f77f46 100644 --- a/jnius/jnius_utils.pxi +++ b/jnius/jnius_utils.pxi @@ -10,10 +10,10 @@ cdef parse_definition(definition): while len(argdef): c = argdef[0] - # read the array char + # read the array char(s) prefix = '' - if c == '[': - prefix = c + while c == '[': + prefix += c argdef = argdef[1:] c = argdef[0] diff --git a/tests/org/jnius/MultipleDimensions.java b/tests/org/jnius/MultipleDimensions.java new file mode 100644 index 0000000..9deeec5 --- /dev/null +++ b/tests/org/jnius/MultipleDimensions.java @@ -0,0 +1,16 @@ +package org.jnius; + +public class MultipleDimensions { + public static boolean methodParamsMatrixI(int[][] x) { + if (x.length != 3 || x[0].length != 3) + return false; + return (x[0][0] == 1 && x[0][1] == 2 && x[1][2] == 6); + } + public static int[][] methodReturnMatrixI() { + int[][] matrix = {{1,2,3}, + {4,5,6}, + {7,8,9}}; + return matrix; + } + +} diff --git a/tests/test_multidimension.py b/tests/test_multidimension.py new file mode 100644 index 0000000..87e16b1 --- /dev/null +++ b/tests/test_multidimension.py @@ -0,0 +1,12 @@ +import unittest +from jnius.reflect import autoclass + +class MultipleDimensionsTest(unittest.TestCase): + + def test_multiple_dimensions(self): + MultipleDims = autoclass('org.jnius.MultipleDimensions') + matrix = [[1, 2, 3], + [4, 5, 6], + [7, 8, 9]] + self.assertEquals(MultipleDims.methodParamsMatrixI(matrix), True) + self.assertEquals(MultipleDims.methodReturnMatrixI(), matrix) From 2910e2fddcd4f4c4669c14d0bbd81e7b213462bb Mon Sep 17 00:00:00 2001 From: James Laird Date: Fri, 9 May 2014 17:22:56 +1000 Subject: [PATCH 11/74] Add control of JVM startup options --- docs/source/api.rst | 16 ++++++++ jnius/jnius_env.pxi | 4 +- jnius/jnius_jvm_desktop.pxi | 57 ++++++++++---------------- jnius_config.py | 79 +++++++++++++++++++++++++++++++++++++ setup.py | 1 + 5 files changed, 120 insertions(+), 37 deletions(-) create mode 100644 jnius_config.py diff --git a/docs/source/api.rst b/docs/source/api.rst index d819e02..dde5f7e 100644 --- a/docs/source/api.rst +++ b/docs/source/api.rst @@ -327,3 +327,19 @@ example:: Signature: ()V } + +JVM options and the class path +------------------------------ + +JVM options need to be set before `import jnius` is called, as they cannot be changed after the VM starts up. +To this end, you can:: + + import jnius_config + jnius_config.add_options('-Xrs', '-Xmx4096') + jnius_config.set_classpath('.', '/usr/local/fem/plugins/*') + import jnius + +If a classpath is set with these functions, it overrides any CLASSPATH environment variable. +Multiple options or path entries should be supplied as multiple arguments to the `add_` and `set_` functions. +If no classpath is provided and CLASSPATH is not set, the path defaults to `'.'`. +This functionality is not available on Android. diff --git a/jnius/jnius_env.pxi b/jnius/jnius_env.pxi index 40da529..84b58ff 100644 --- a/jnius/jnius_env.pxi +++ b/jnius/jnius_env.pxi @@ -4,11 +4,13 @@ cdef JNIEnv *default_env = NULL cdef extern int gettid() cdef JavaVM *jvm = NULL -cdef JNIEnv *get_jnienv(): +cdef JNIEnv *get_jnienv() except NULL: global default_env # first call, init. if default_env == NULL: default_env = get_platform_jnienv() + if default_env == NULL: + return NULL default_env[0].GetJavaVM(default_env, &jvm) # return the current env attached to the thread diff --git a/jnius/jnius_jvm_desktop.pxi b/jnius/jnius_jvm_desktop.pxi index c20ed09..9a24b81 100644 --- a/jnius/jnius_jvm_desktop.pxi +++ b/jnius/jnius_jvm_desktop.pxi @@ -4,6 +4,7 @@ cdef extern jint __stdcall JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args) cdef extern from "jni.h": int JNI_VERSION_1_4 + int JNI_OK jboolean JNI_FALSE ctypedef struct JavaVMInitArgs: jint version @@ -16,52 +17,36 @@ cdef extern from "jni.h": cdef JNIEnv *_platform_default_env = NULL -def classpath(): - import platform - from glob import glob - from os import environ - from os.path import realpath, dirname, join - - if platform.system() == 'Windows': - split_char = ';' - else: - split_char = ':' - - paths = [realpath('.'), join(dirname(__file__), 'src'), ] - if 'CLASSPATH' not in environ: - return split_char.join(paths) - - cp = environ.get('CLASSPATH') - pre_paths = paths + cp.split(split_char) - # deal with wildcards - for path in pre_paths: - if not path.endswith('*'): - paths.append(path) - else: - paths.extend(glob(path + '.jar')) - paths.extend(glob(path + '.JAR')) - result = split_char.join(paths) - return result - -cdef void create_jnienv(): +cdef void create_jnienv() except *: cdef JavaVM* jvm cdef JavaVMInitArgs args - cdef JavaVMOption options[1] + cdef JavaVMOption *options + cdef int ret cdef bytes py_bytes + import jnius_config - cp = classpath() - py_bytes = ('-Djava.class.path={0}'.format(cp)) - options[0].optionString = py_bytes - options[0].extraInfo = NULL + optarr = jnius_config.options + optarr.append("-Djava.class.path=" + jnius_config.expand_classpath()) + + options = malloc(sizeof(JavaVMOption) * len(optarr)) + for i, opt in enumerate(optarr): + options[i].optionString = (opt) + options[i].extraInfo = NULL args.version = JNI_VERSION_1_4 args.options = options - args.nOptions = 1 + args.nOptions = len(optarr) args.ignoreUnrecognized = JNI_FALSE - JNI_CreateJavaVM(&jvm, &_platform_default_env, &args) + ret = JNI_CreateJavaVM(&jvm, &_platform_default_env, &args) + free(options) -cdef JNIEnv *get_platform_jnienv(): + if ret != JNI_OK: + raise SystemError("JVM failed to start") + + jnius_config.vm_running = True + +cdef JNIEnv *get_platform_jnienv() except NULL: if _platform_default_env == NULL: create_jnienv() return _platform_default_env diff --git a/jnius_config.py b/jnius_config.py new file mode 100644 index 0000000..bb5ba95 --- /dev/null +++ b/jnius_config.py @@ -0,0 +1,79 @@ +__all__ = ('set_options', 'add_options', 'get_options', + 'set_classpath', 'add_classpath', 'get_classpath', + 'expand_classpath') + +import platform +if platform.system() == 'Windows': + split_char = ';' +else: + split_char = ':' + +vm_running = False +options = [] +classpath = None + +def set_options(*opts): + "Sets the list of options to the JVM. Removes any previously set options." + if vm_running: + raise ValueError("VM is already running, can't set options") + globals()['options'] = opts + +def add_options(*opts): + "Appends options to the list of VM options." + if vm_running: + raise ValueError("VM is already running, can't set options") + global options + options.extend(opts) + +def get_options(): + "Retrieves the current list of VM options." + global options + return list(options) + + +def set_classpath(*path): + """ + Sets the classpath for the JVM to use. Replaces any existing classpath, overriding the CLASSPATH environment variable. + """ + if vm_running: + raise ValueError("VM is already running, can't set classpath") + global classpath + classpath = path + +def add_classpath(*path): + """ + Appends items to the classpath for the JVM to use. + Replaces any existing classpath, overriding the CLASSPATH environment variable. + """ + if vm_running: + raise ValueError("VM is already running, can't set classpath") + global classpath + if classpath is None: + classpath = list(path) + else: + classpath.extend(path) + +def get_classpath(): + "Retrieves the classpath the JVM will use." + from os import environ + from os.path import realpath + global classpath + + if classpath is not None: + return list(classpath) + + if 'CLASSPATH' in environ: + return environ['CLASSPATH'].split(split_char) + + return [realpath('.')] + +def expand_classpath(): + from glob import glob + paths = [] + # deal with wildcards + for path in get_classpath(): + if not path.endswith('*'): + paths.append(path) + else: + paths.extend(glob(path + '.[Jj][Aa][Rr]')) + return split_char.join(paths) diff --git a/setup.py b/setup.py index d992005..ce68313 100644 --- a/setup.py +++ b/setup.py @@ -108,6 +108,7 @@ setup(name='jnius', version=version, cmdclass={'build_ext': build_ext}, packages=['jnius'], + py_modules=['jnius_config'], url='http://pyjnius.readthedocs.org/', author='Mathieu Virbel and Gabriel Pettier', author_email='mat@kivy.org,gabriel@kivy.org', From 6328de8142ae504c8a6a04a481a2316c463fc7ef Mon Sep 17 00:00:00 2001 From: Len Trigg Date: Fri, 16 May 2014 12:12:12 +1200 Subject: [PATCH 12/74] Incorporate patch from #96 and a unit test --- jnius/jnius_utils.pxi | 11 ++++++++++- tests/test_bad_declaration.py | 9 +++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/jnius/jnius_utils.pxi b/jnius/jnius_utils.pxi index c251528..7f473dc 100644 --- a/jnius/jnius_utils.pxi +++ b/jnius/jnius_utils.pxi @@ -36,11 +36,20 @@ cdef parse_definition(definition): cdef void check_exception(JNIEnv *j_env) except *: + cdef jmethodID toString = NULL + cdef jstring e_string + cdef jboolean isCopy cdef jthrowable exc = j_env[0].ExceptionOccurred(j_env) if exc: j_env[0].ExceptionDescribe(j_env) j_env[0].ExceptionClear(j_env) - raise JavaException('JVM exception occured') + + toString = j_env[0].GetMethodID(j_env, j_env[0].FindClass(j_env, "java/lang/Object"), "toString", "()Ljava/lang/String;"); + e_string = j_env[0].CallObjectMethod(j_env, exc, toString); + + pystr = convert_jobject_to_python(j_env, 'Ljava/lang/String;', e_string) + + raise JavaException('JVM exception occurred: ' + pystr) cdef dict assignable_from = {} diff --git a/tests/test_bad_declaration.py b/tests/test_bad_declaration.py index a7b46a7..c75b786 100644 --- a/tests/test_bad_declaration.py +++ b/tests/test_bad_declaration.py @@ -21,3 +21,12 @@ class BadDeclarationTest(unittest.TestCase): Stack = autoclass('java.util.Stack') stack = Stack() self.assertRaises(JavaException, stack.push, 'hello', 'world', 123) + + def test_java_exception_handling(self): + Stack = autoclass('java.util.Stack') + stack = Stack() + try: + stack.pop() + self.fail("Expected exception to be thrown") + except JavaException as je: + self.assertIn("EmptyStackException", str(je)) From d93c911d9e89c9d82febb3b9d9e9f8d1486ba28d Mon Sep 17 00:00:00 2001 From: Lenbok Date: Mon, 19 May 2014 10:22:36 +1200 Subject: [PATCH 13/74] Add improved exception handling: - Disable calling ExceptionDescribe, as this pollutes stderr, making it harder to implement clean exception handling. JNI documentation describes it as being for debugging purposes. - Extract separate fields for the exception class name, exception message, and exception stack trace, and add these as attributes to JavaException. Calling code can now decide for itself whether and how to present this information. --- jnius/jnius_export_class.pxi | 10 ++++- jnius/jnius_utils.pxi | 70 ++++++++++++++++++++++++++++++--- tests/org/jnius/BasicsTest.java | 11 ++++++ tests/test_bad_declaration.py | 21 +++++++++- 4 files changed, 105 insertions(+), 7 deletions(-) diff --git a/jnius/jnius_export_class.pxi b/jnius/jnius_export_class.pxi index 0e321d9..e6fe76b 100644 --- a/jnius/jnius_export_class.pxi +++ b/jnius/jnius_export_class.pxi @@ -2,7 +2,15 @@ class JavaException(Exception): '''Can be a real java exception, or just an exception from the wrapper. ''' - pass + classname = None # The classname of the exception + innermessage = None # The message of the inner exception + stacktrace = None # The stack trace of the inner exception + + def __init__(self, message, classname=None, innermessage=None, stacktrace=None): + self.classname = classname + self.innermessage = innermessage + self.stacktrace = stacktrace + Exception.__init__(self, message) cdef class JavaObject(object): diff --git a/jnius/jnius_utils.pxi b/jnius/jnius_utils.pxi index 7f473dc..edbbd8c 100644 --- a/jnius/jnius_utils.pxi +++ b/jnius/jnius_utils.pxi @@ -37,19 +37,79 @@ cdef parse_definition(definition): cdef void check_exception(JNIEnv *j_env) except *: cdef jmethodID toString = NULL - cdef jstring e_string + cdef jmethodID getCause = NULL + cdef jmethodID getStackTrace = NULL + cdef jmethodID getMessage = NULL + cdef jstring e_msg cdef jboolean isCopy cdef jthrowable exc = j_env[0].ExceptionOccurred(j_env) if exc: - j_env[0].ExceptionDescribe(j_env) + # ExceptionDescribe always writes to stderr, preventing tidy exception + # handling, so should only be for debugging + # j_env[0].ExceptionDescribe(j_env) j_env[0].ExceptionClear(j_env) toString = j_env[0].GetMethodID(j_env, j_env[0].FindClass(j_env, "java/lang/Object"), "toString", "()Ljava/lang/String;"); - e_string = j_env[0].CallObjectMethod(j_env, exc, toString); + getMessage = j_env[0].GetMethodID(j_env, j_env[0].FindClass(j_env, "java/lang/Throwable"), "getMessage", "()Ljava/lang/String;"); + getCause = j_env[0].GetMethodID(j_env, j_env[0].FindClass(j_env, "java/lang/Throwable"), "getCause", "()Ljava/lang/Throwable;"); + getStackTrace = j_env[0].GetMethodID(j_env, j_env[0].FindClass(j_env, "java/lang/Throwable"), "getStackTrace", "()[Ljava/lang/StackTraceElement;"); - pystr = convert_jobject_to_python(j_env, 'Ljava/lang/String;', e_string) + e_msg = j_env[0].CallObjectMethod(j_env, exc, getMessage); + pymsg = None if e_msg == NULL else convert_jobject_to_python(j_env, 'Ljava/lang/String;', e_msg) - raise JavaException('JVM exception occurred: ' + pystr) + pystack = [] + _append_exception_trace_messages(j_env, pystack, exc, getCause, getStackTrace, toString) + + pyexcclass = lookup_java_object_name(j_env, exc).replace('/', '.') + + raise JavaException('JVM exception occurred: %s' % (pymsg if pymsg is not None else pyexcclass), pyexcclass, pymsg, pystack) + + +cdef void _append_exception_trace_messages( + JNIEnv* j_env, + list pystack, + jthrowable exc, + jmethodID mid_getCause, + jmethodID mid_getStackTrace, + jmethodID mid_toString): + + # Get the array of StackTraceElements. + cdef jobjectArray frames = j_env[0].CallObjectMethod(j_env, exc, mid_getStackTrace) + cdef jsize frames_length = j_env[0].GetArrayLength(j_env, frames) + cdef jstring msg_obj + cdef jobject frame + cdef jthrowable cause + + # Add Throwable.toString() before descending stack trace messages. + if frames != NULL: + msg_obj = j_env[0].CallObjectMethod(j_env, exc, mid_toString) + pystr = None if msg_obj == NULL else convert_jobject_to_python(j_env, 'Ljava/lang/String;', msg_obj) + # If this is not the top-of-the-trace then this is a cause. + if len(pystack) > 0: + pystack.append("Caused by:") + pystack.append(pystr) + j_env[0].DeleteLocalRef(j_env, msg_obj) + + # Append stack trace messages if there are any. + if frames_length > 0: + for i in range(frames_length): + # Get the string returned from the 'toString()' method of the next frame and append it to the error message. + frame = j_env[0].GetObjectArrayElement(j_env, frames, i) + msg_obj = j_env[0].CallObjectMethod(j_env, frame, mid_toString) + pystr = None if msg_obj == NULL else convert_jobject_to_python(j_env, 'Ljava/lang/String;', msg_obj) + pystack.append(pystr) + j_env[0].DeleteLocalRef(j_env, msg_obj) + j_env[0].DeleteLocalRef(j_env, frame) + + # If 'exc' has a cause then append the stack trace messages from the cause. + if frames != NULL: + cause = j_env[0].CallObjectMethod(j_env, exc, mid_getCause) + if cause != NULL: + _append_exception_trace_messages(j_env, pystack, cause, + mid_getCause, mid_getStackTrace, mid_toString) + j_env[0].DeleteLocalRef(j_env, cause) + + j_env[0].DeleteLocalRef(j_env, frames) cdef dict assignable_from = {} diff --git a/tests/org/jnius/BasicsTest.java b/tests/org/jnius/BasicsTest.java index c257822..feeceb0 100644 --- a/tests/org/jnius/BasicsTest.java +++ b/tests/org/jnius/BasicsTest.java @@ -22,6 +22,17 @@ public class BasicsTest { public float methodF() { return 1.23456789f; }; public double methodD() { return 1.23456789; }; public String methodString() { return new String("helloworld"); } + public void methodException(int depth) throws IllegalArgumentException { + if (depth == 0) throw new IllegalArgumentException("helloworld"); + else methodException(depth -1); + } + public void methodExceptionChained() throws IllegalArgumentException { + try { + methodException(5); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("helloworld2", e); + } + } static public boolean fieldStaticZ = true; static public byte fieldStaticB = 127; diff --git a/tests/test_bad_declaration.py b/tests/test_bad_declaration.py index c75b786..a1f66f4 100644 --- a/tests/test_bad_declaration.py +++ b/tests/test_bad_declaration.py @@ -29,4 +29,23 @@ class BadDeclarationTest(unittest.TestCase): stack.pop() self.fail("Expected exception to be thrown") except JavaException as je: - self.assertIn("EmptyStackException", str(je)) + # print "Got JavaException: " + str(je) + # print "Got Exception Class: " + je.classname + # print "Got stacktrace: \n" + '\n'.join(je.stacktrace) + self.assertEquals("java.util.EmptyStackException", je.classname) + + def test_java_exception_chaining(self): + BasicsTest = autoclass('org.jnius.BasicsTest') + basics = BasicsTest() + try: + basics.methodExceptionChained() + self.fail("Expected exception to be thrown") + except JavaException as je: + # print "Got JavaException: " + str(je) + # print "Got Exception Class: " + je.classname + # print "Got Exception Message: " + je.innermessage + # print "Got stacktrace: \n" + '\n'.join(je.stacktrace) + self.assertEquals("java.lang.IllegalArgumentException", je.classname) + self.assertEquals("helloworld2", je.innermessage) + self.assertIn("Caused by:", je.stacktrace) + self.assertEquals(11, len(je.stacktrace)) \ No newline at end of file From a45ba1737edc9822f3b33892459925227e148782 Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Thu, 22 May 2014 17:54:51 +0200 Subject: [PATCH 14/74] fix issue with "cannot allocate an array of constant size 0" on Windows / MSVC compiler --- jnius/jnius_export_class.pxi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jnius/jnius_export_class.pxi b/jnius/jnius_export_class.pxi index 0e321d9..7638ab5 100644 --- a/jnius/jnius_export_class.pxi +++ b/jnius/jnius_export_class.pxi @@ -73,7 +73,7 @@ class MetaJavaClass(type): with nogil: classLoader = j_env[0].CallStaticObjectMethodA( - j_env, baseclass, getClassLoader, []) + j_env, baseclass, getClassLoader, NULL) jargs = malloc(sizeof(jobject) * 2) jargs[0] = classLoader jargs[1] = interfaces From 0635452378d2747d3ce0806ef25a1a1f59114991 Mon Sep 17 00:00:00 2001 From: JustinCappos Date: Tue, 29 Jul 2014 17:57:25 -0400 Subject: [PATCH 15/74] Update api.rst grammar fix --- docs/source/api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/api.rst b/docs/source/api.rst index d819e02..322f581 100644 --- a/docs/source/api.rst +++ b/docs/source/api.rst @@ -313,7 +313,7 @@ A signature like:: When you implement Java in Python, the signature of the Java method must match. -Java provide a tool named `javap` to get the signature of any java class. For +Java provides a tool named `javap` to get the signature of any java class. For example:: $ javap -s java.util.Iterator From f5b555d3d2deb0d8b79ce1937cc7ae17b72adaad Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Thu, 31 Jul 2014 13:49:25 +0200 Subject: [PATCH 16/74] Add specific case for Android/libART: it crash if we check NativeInvocationHandler/InvocationHandler with isAssignableFrom. Plus, include some defaults methods implementation of java.lang.Object (hashCode, toString, equals), which is used by ART. Closes #92. Closes #113. --- jnius/__init__.py | 23 +++++++++++++++++++++++ jnius/jnius_proxy.pxi | 1 + jnius/jnius_utils.pxi | 9 +++++++++ 3 files changed, 33 insertions(+) diff --git a/jnius/__init__.py b/jnius/__init__.py index 06435a0..8ac24eb 100644 --- a/jnius/__init__.py +++ b/jnius/__init__.py @@ -11,3 +11,26 @@ __version__ = '1.1-dev' from .jnius import * from .reflect import * + +# XXX monkey patch methods that cannot be in cython. +# Cython doesn't allow to set new attribute on methods it compiled + +class PythonJavaClass_(PythonJavaClass): + + @java_method('()I', name='hashCode') + def hashCode(self): + return id(self) + + @java_method('()Ljava/lang/String;', name='hashCode') + def hashCode_(self): + return '{}'.format(id(self)) + + @java_method('()Ljava/lang/String;', name='toString') + def toString(self): + return repr(self) + + @java_method('(Ljava/lang/Object;)Z', name='equals') + def equals(self, other): + return self.hashCode() == other.hashCode() + +PythonJavaClass = PythonJavaClass_ diff --git a/jnius/jnius_proxy.pxi b/jnius/jnius_proxy.pxi index 9e604cd..f7ee437 100644 --- a/jnius/jnius_proxy.pxi +++ b/jnius/jnius_proxy.pxi @@ -72,6 +72,7 @@ cdef class PythonJavaClass(object): return py_method(*args) + cdef jobject py_invoke0(JNIEnv *j_env, jobject j_this, jobject j_proxy, jobject j_method, jobjectArray args) except * with gil: diff --git a/jnius/jnius_utils.pxi b/jnius/jnius_utils.pxi index c251528..2ebd188 100644 --- a/jnius/jnius_utils.pxi +++ b/jnius/jnius_utils.pxi @@ -51,6 +51,14 @@ cdef void check_assignable_from(JNIEnv *env, JavaClass jc, bytes signature) exce if signature == 'java/lang/Object': return + # FIXME Android/libART specific check + # check_jni.cc crash when calling the IsAssignableFrom with + # org/jnius/NativeInvocationHandler java/lang/reflect/InvocationHandler + # Because we know it's ok, just return here. + if signature == 'java/lang/reflect/InvocationHandler' and \ + jc.__javaclass__ == 'org/jnius/NativeInvocationHandler': + return + # if the signature is a direct match, it's ok too :) if jc.__javaclass__ == signature: return @@ -67,6 +75,7 @@ cdef void check_assignable_from(JNIEnv *env, JavaClass jc, bytes signature) exce signature)) result = bool(env[0].IsAssignableFrom(env, jc.j_cls, cls)) + env[0].ExceptionDescribe(env) env[0].ExceptionClear(env) assignable_from[(jc.__javaclass__, signature)] = bool(result) From 9c483237a4494862a051db5931b7e75faaeb7713 Mon Sep 17 00:00:00 2001 From: dessant Date: Tue, 19 Aug 2014 15:35:20 +0300 Subject: [PATCH 17/74] fix print statement --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d992005..dbbbc28 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ try: except ImportError: from distutils.command.build_ext import build_ext if platform != 'android': - print '\n\nYou need Cython to compile Pyjnius.\n\n' + print('\n\nYou need Cython to compile Pyjnius.\n\n') raise files = [fn[:-3] + 'c' for fn in files if fn.endswith('pyx')] From 7826ca53d70dab85835a8a9bd4097c3ddecefb9b Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Tue, 19 Aug 2014 20:06:44 +0200 Subject: [PATCH 18/74] fix architecture detection for python2 and python3 --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index dbbbc28..b1cefb7 100644 --- a/setup.py +++ b/setup.py @@ -2,6 +2,7 @@ from distutils.core import setup, Extension from os import environ from os.path import dirname, join, exists import sys +from platform import architecture files = [ 'jni.pxi', @@ -76,7 +77,7 @@ else: shell=True, stdout=subprocess.PIPE).communicate()[0].strip() if not jre_home: raise Exception('Unable to determine JRE_HOME') - cpu = 'i386' if sys.maxint == 2147483647 else 'amd64' + cpu = 'amd64' if architecture()[0] == '64bit' else 'i386' if platform == 'win32': incl_dir = join(jdk_home, 'include', 'win32') From f9e0a87e970a7b4850bfe184b021ae2d18ecf84b Mon Sep 17 00:00:00 2001 From: Corey Prophitt Date: Sat, 30 Aug 2014 00:53:12 -0700 Subject: [PATCH 19/74] Fixed sound recorder example syntax error --- docs/source/android.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/android.rst b/docs/source/android.rst index aeff0c1..8da6f1b 100644 --- a/docs/source/android.rst +++ b/docs/source/android.rst @@ -45,9 +45,9 @@ Let's do it with Pyjnius:: # create out recorder mRecorder = MediaRecorder() mRecorder.setAudioSource(AudioSource.MIC) - mRecorder.setOutputFormat(OutputFormat.THREE_GPP) + mRecorder.setOutputFormat(OutputFormat.THREE_GPP) mRecorder.setOutputFile('/sdcard/testrecorder.3gp') - mRecorder.setAudioEncoder(AudioEncoder.ARM_NB) + mRecorder.setAudioEncoder(AudioEncoder.AMR_NB) mRecorder.prepare() # record 5 seconds @@ -151,7 +151,7 @@ everything needed for accessing the accelerometer:: // Contain the last event we got from the listener static public SensorEvent lastEvent = null; - + // Define a new listener static class AccelListener implements SensorEventListener { public void onSensorChanged(SensorEvent ev) { From 9f9d61618f12a51f228e7079ba206c5379dc669d Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Tue, 16 Sep 2014 10:08:32 +0200 Subject: [PATCH 20/74] fixes for Cython 0.21. Closes #131 --- jnius/jnius_conversion.pxi | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/jnius/jnius_conversion.pxi b/jnius/jnius_conversion.pxi index 12a0f1a..e41a8d2 100644 --- a/jnius/jnius_conversion.pxi +++ b/jnius/jnius_conversion.pxi @@ -27,6 +27,7 @@ cdef void populate_args(JNIEnv *j_env, tuple definition_args, jvalue *j_args, ar cdef JavaClass jc cdef PythonJavaClass pc cdef int index + cdef bytes py_str for index, argtype in enumerate(definition_args): py_arg = args[index] if argtype == 'Z': @@ -50,8 +51,8 @@ cdef void populate_args(JNIEnv *j_env, tuple definition_args, jvalue *j_args, ar j_args[index].l = NULL elif isinstance(py_arg, basestring) and \ argtype in ('Ljava/lang/String;', 'Ljava/lang/Object;'): - j_args[index].l = j_env[0].NewStringUTF( - j_env, py_arg.encode('utf-8')) + py_str = py_arg.encode('utf-8') + j_args[index].l = j_env[0].NewStringUTF(j_env, py_str) elif isinstance(py_arg, JavaClass): jc = py_arg check_assignable_from(j_env, jc, argtype[1:-1]) From 176dc7091978b6b16783e73d55b50679c411fc22 Mon Sep 17 00:00:00 2001 From: Christopher Neugebauer Date: Tue, 16 Sep 2014 20:17:13 +1000 Subject: [PATCH 21/74] 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 22/74] =?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 23/74] 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 24/74] 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 25/74] (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 26/74] 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 27/74] 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") - - - - From 5fb4667c18b79744940658171b64018c0e06953c Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Thu, 13 Nov 2014 09:43:11 +0100 Subject: [PATCH 28/74] detect the implementation issue on IsAssignableFrom, and go the other way around for ART. Also dont try to use ExceptionDescribe if there is no Exception throw, or we will get a bad bad native crash. Closes #92 --- jnius/jnius_utils.pxi | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/jnius/jnius_utils.pxi b/jnius/jnius_utils.pxi index ffc4721..33cd322 100644 --- a/jnius/jnius_utils.pxi +++ b/jnius/jnius_utils.pxi @@ -44,8 +44,25 @@ cdef void check_exception(JNIEnv *j_env) except *: cdef dict assignable_from = {} +cdef int assignable_from_order = 0 cdef void check_assignable_from(JNIEnv *env, JavaClass jc, bytes signature) except *: - cdef jclass cls + global assignable_from_order + cdef jclass cls, clsA, clsB + cdef jthrowable exc + + # first call, we need to get over the libart issue, which implemented + # IsAssignableFrom the wrong way. + # Ref: https://github.com/kivy/pyjnius/issues/92 + # Google Bug: https://android.googlesource.com/platform/art/+/1268b74%5E!/ + if assignable_from_order == 0: + clsA = env[0].FindClass(env, "java/lang/String") + clsB = env[0].FindClass(env, "java/lang/Object") + if env[0].IsAssignableFrom(env, clsB, clsA): + # Bug triggered, IsAssignableFrom said we can do things like: + # String a = Object() + assignable_from_order = -1 + else: + assignable_from_order = 1 # if we have a JavaObject, it's always ok. if signature == 'java/lang/Object': @@ -74,9 +91,16 @@ cdef void check_assignable_from(JNIEnv *env, JavaClass jc, bytes signature) exce raise JavaException('Unable to found the class for {0!r}'.format( signature)) - result = bool(env[0].IsAssignableFrom(env, jc.j_cls, cls)) - env[0].ExceptionDescribe(env) - env[0].ExceptionClear(env) + if assignable_from_order == 1: + result = bool(env[0].IsAssignableFrom(env, jc.j_cls, cls)) + else: + result = bool(env[0].IsAssignableFrom(env, cls, jc.j_cls)) + + exc = env[0].ExceptionOccurred(env) + if exc: + env[0].ExceptionDescribe(env) + env[0].ExceptionClear(env) + assignable_from[(jc.__javaclass__, signature)] = bool(result) if result is False: From f0b8ce3856c87730e1eccdd1091205ff85423ca1 Mon Sep 17 00:00:00 2001 From: Tony Finn Date: Sat, 3 Jan 2015 18:56:50 +0000 Subject: [PATCH 29/74] Fix JVM signatures for the java.lang.Class method. The methods defined in the Class class contain semicolons at the end of the signatures for methods which return primitives, while these are only required after class names. As a result, a JVM error occurs when attempting to call any of the affected methods. --- jnius/reflect.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/jnius/reflect.py b/jnius/reflect.py index b3b1ea7..8e260df 100644 --- a/jnius/reflect.py +++ b/jnius/reflect.py @@ -10,7 +10,7 @@ class Class(JavaClass): __metaclass__ = MetaJavaClass __javaclass__ = 'java/lang/Class' - desiredAssertionStatus = JavaMethod('()Z;') + desiredAssertionStatus = JavaMethod('()Z') forName = JavaMultipleMethod([ ('(Ljava/lang/String,Z,Ljava/lang/ClassLoader;)Ljava/langClass;', True, False), ('(Ljava/lang/String;)Ljava/lang/Class;', True, False), ]) @@ -32,7 +32,7 @@ class Class(JavaClass): getInterfaces = JavaMethod('()[Ljava/lang/Class;') getMethod = JavaMethod('(Ljava/lang/String,[Ljava/lang/Class;)Ljava/lang/reflect/Method;') getMethods = JavaMethod('()[Ljava/lang/reflect/Method;') - getModifiers = JavaMethod('()[I;') + getModifiers = JavaMethod('()[I') getName = JavaMethod('()Ljava/lang/String;') getPackage = JavaMethod('()Ljava/lang/Package;') getProtectionDomain = JavaMethod('()Ljava/security/ProtectionDomain;') @@ -40,11 +40,11 @@ class Class(JavaClass): getResourceAsStream = JavaMethod('(Ljava/lang/String;)Ljava/io/InputStream;') getSigners = JavaMethod('()[Ljava/lang/Object;') getSuperclass = JavaMethod('()Ljava/lang/reflect/Class;') - isArray = JavaMethod('()Z;') - isAssignableFrom = JavaMethod('(Ljava/lang/reflect/Class;)Z;') - isInstance = JavaMethod('(Ljava/lang/Object;)Z;') - isInterface = JavaMethod('()Z;') - isPrimitive = JavaMethod('()Z;') + isArray = JavaMethod('()Z') + isAssignableFrom = JavaMethod('(Ljava/lang/reflect/Class;)Z') + isInstance = JavaMethod('(Ljava/lang/Object;)Z') + isInterface = JavaMethod('()Z') + isPrimitive = JavaMethod('()Z') newInstance = JavaMethod('()Ljava/lang/Object;') toString = JavaMethod('()Ljava/lang/String;') From 4839674fdd16da4daa8fcb1bfe0ace7565ccc89f Mon Sep 17 00:00:00 2001 From: Ryan Pessa Date: Tue, 27 Jan 2015 10:28:14 -0600 Subject: [PATCH 30/74] use temporary var to handle unsigned char -> jbyte (signed char) --- jnius/jnius_conversion.pxi | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/jnius/jnius_conversion.pxi b/jnius/jnius_conversion.pxi index e41a8d2..00daf96 100644 --- a/jnius/jnius_conversion.pxi +++ b/jnius/jnius_conversion.pxi @@ -414,6 +414,7 @@ cdef jobject convert_pyarray_to_java(JNIEnv *j_env, definition, pyarray) except cdef jobject ret = NULL cdef int array_size = len(pyarray) cdef int i + cdef unsigned char c_tmp cdef jboolean j_boolean cdef jbyte j_byte cdef jchar j_char @@ -460,7 +461,8 @@ cdef jobject convert_pyarray_to_java(JNIEnv *j_env, definition, pyarray) except ret, 0, array_size, a_bytes._buf) else: for i in range(array_size): - j_byte = pyarray[i] + c_tmp = pyarray[i] + j_byte = c_tmp j_env[0].SetByteArrayRegion(j_env, ret, i, 1, &j_byte) From 15664625acd4e984a613b3c26276fe646e5224a9 Mon Sep 17 00:00:00 2001 From: retsyo Date: Thu, 29 Jan 2015 19:47:26 +0800 Subject: [PATCH 31/74] Update jnius_conversion.pxi change chr(j_chars[i]) to unichr(j_chars[i]), now it can deal with Chinese --- jnius/jnius_conversion.pxi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jnius/jnius_conversion.pxi b/jnius/jnius_conversion.pxi index 00daf96..39d603a 100644 --- a/jnius/jnius_conversion.pxi +++ b/jnius/jnius_conversion.pxi @@ -224,7 +224,7 @@ cdef convert_jarray_to_python(JNIEnv *j_env, definition, jobject j_object): elif r == 'C': j_chars = j_env[0].GetCharArrayElements( j_env, j_object, &iscopy) - ret = [chr(j_chars[i]) for i in range(array_size)] + ret = [unichr(j_chars[i]) for i in range(array_size)] j_env[0].ReleaseCharArrayElements( j_env, j_object, j_chars, 0) From d9cdbb7288372eb58a72b86d901e8965cf0cc952 Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Fri, 6 Feb 2015 18:34:50 +0100 Subject: [PATCH 32/74] add a test with signed char to ensure it still working --- tests/test_bytearray.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_bytearray.py b/tests/test_bytearray.py index 46a7296..56d7f14 100644 --- a/tests/test_bytearray.py +++ b/tests/test_bytearray.py @@ -7,3 +7,7 @@ class StringArgumentForByteArrayTest(unittest.TestCase): # the ByteBuffer.wrap() accept only byte[]. ByteBuffer = autoclass('java.nio.ByteBuffer') self.assertIsNotNone(ByteBuffer.wrap('hello world')) + + def test_string_arg_with_signed_char(self): + ByteBuffer = autoclass('java.nio.ByteBuffer') + self.assertIsNotNone(ByteBuffer.wrap('\x00\xffHello World\x7f')) From 614e43626a253816d20033a48b7f9b44b37eb650 Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Sat, 7 Feb 2015 19:31:04 +0100 Subject: [PATCH 33/74] add few tests about bytearray filling. --- tests/org/jnius/BasicsTest.java | 8 ++++++++ tests/test_bytearray.py | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/tests/org/jnius/BasicsTest.java b/tests/org/jnius/BasicsTest.java index c257822..6a4e47d 100644 --- a/tests/org/jnius/BasicsTest.java +++ b/tests/org/jnius/BasicsTest.java @@ -141,4 +141,12 @@ public class BasicsTest { return false; return (x[0] == 127 && x[1] == 127 && x[2] == 127); } + + public void fillByteArray(byte[] x) { + if (x.length != 3) + return; + x[0] = 127; + x[1] = 1; + x[2] = -127; + } } diff --git a/tests/test_bytearray.py b/tests/test_bytearray.py index 56d7f14..226289d 100644 --- a/tests/test_bytearray.py +++ b/tests/test_bytearray.py @@ -11,3 +11,11 @@ class StringArgumentForByteArrayTest(unittest.TestCase): def test_string_arg_with_signed_char(self): ByteBuffer = autoclass('java.nio.ByteBuffer') self.assertIsNotNone(ByteBuffer.wrap('\x00\xffHello World\x7f')) + + def test_fill_byte_array(self): + arr = [0, 0, 0] + Test = autoclass('org.jnius.BasicsTest')() + Test.fillByteArray(arr) + self.assertEquals( + arr, + [127, 1, -127]) From 857e4c762a2c6f6c7bbf2e0d09037a2690b32c69 Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Sat, 7 Feb 2015 19:33:19 +0100 Subject: [PATCH 34/74] add a jnius.detach() method to detach a thread that used some jni. This cannot be automatic as jnius do not control the thread creation, so it cannot add a callabck to autocall this method. It must be added by the user before the thread leave, if it used pyjnius inside. Ref #107 Ref #97 --- jnius/jni.pxi | 1 + jnius/jnius.pyx | 2 +- jnius/jnius_env.pxi | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/jnius/jni.pxi b/jnius/jni.pxi index afcd661..4e6c546 100644 --- a/jnius/jni.pxi +++ b/jnius/jni.pxi @@ -401,4 +401,5 @@ cdef extern from "jni.h": ctypedef struct JNIInvokeInterface: jint (*AttachCurrentThread)(JavaVM *, JNIEnv **, void *) + jint (*DetachCurrentThread)(JavaVM *) diff --git a/jnius/jnius.pyx b/jnius/jnius.pyx index 08f619a..dc074ba 100644 --- a/jnius/jnius.pyx +++ b/jnius/jnius.pyx @@ -87,7 +87,7 @@ Python:: __all__ = ('JavaObject', 'JavaClass', 'JavaMethod', 'JavaField', 'MetaJavaClass', 'JavaException', 'cast', 'find_javaclass', - 'PythonJavaClass', 'java_method') + 'PythonJavaClass', 'java_method', 'detach') from libc.stdlib cimport malloc, free from functools import partial diff --git a/jnius/jnius_env.pxi b/jnius/jnius_env.pxi index 84b58ff..336ab0e 100644 --- a/jnius/jnius_env.pxi +++ b/jnius/jnius_env.pxi @@ -19,3 +19,7 @@ cdef JNIEnv *get_jnienv() except NULL: jvm[0].AttachCurrentThread(jvm, &env, NULL) return env + +def detach(): + jvm[0].DetachCurrentThread(jvm) + From 9e60152dc5172cfa0c2c90dbcc9e25d5c4cb2493 Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Sat, 7 Feb 2015 20:07:56 +0100 Subject: [PATCH 35/74] pyjnius: document detach() function. Closes #97. Closes #107. --- docs/source/api.rst | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/docs/source/api.rst b/docs/source/api.rst index 3349d6b..9f09a54 100644 --- a/docs/source/api.rst +++ b/docs/source/api.rst @@ -343,3 +343,43 @@ If a classpath is set with these functions, it overrides any CLASSPATH environme Multiple options or path entries should be supplied as multiple arguments to the `add_` and `set_` functions. If no classpath is provided and CLASSPATH is not set, the path defaults to `'.'`. This functionality is not available on Android. + + +Pyjnius and threads +------------------- + +.. function:: detach() + + Each time you create a native thread in Python and uses Pyjnius, any call to + Pyjnius methods will force attachment of the native thread to the current JVM. + But you must detach it before leaving the thread, and Pyjnius cannot do it for + you. + +Example:: + + import threading + import jnius + + def run(...): + try: + # use pyjnius here + finally: + jnius.detach() + +If you don't, it will crash on dalvik and ART / Android:: + + D/dalvikvm(16696): threadid=12: thread exiting, not yet detached (count=0) + D/dalvikvm(16696): threadid=12: thread exiting, not yet detached (count=1) + E/dalvikvm(16696): threadid=12: native thread exited without detaching + E/dalvikvm(16696): VM aborting + +Or:: + + W/art (21168): Native thread exiting without having called DetachCurrentThread (maybe it's going to use a pthread_key_create destructor?): Thread[16,tid=21293,Native,Thread*=0x4c25c040,peer=0x677eaa70,"Thread-16219"] + F/art (21168): art/runtime/thread.cc:903] Native thread exited without calling DetachCurrentThread: Thread[16,tid=21293,Native,Thread*=0x4c25c040,peer=0x677eaa70,"Thread-16219"] + F/art (21168): art/runtime/runtime.cc:203] Runtime aborting... + F/art (21168): art/runtime/runtime.cc:203] (Aborting thread was not attached to runtime!) + F/art (21168): art/runtime/runtime.cc:203] Dumping all threads without appropriate locks held: thread list lock mutator lock + F/art (21168): art/runtime/runtime.cc:203] All threads: + F/art (21168): art/runtime/runtime.cc:203] DALVIK THREADS (16): + ... From 6fa4980b1b9feec9857ab07ae7cd7639782590fe Mon Sep 17 00:00:00 2001 From: Max Smolens Date: Wed, 25 Feb 2015 22:32:12 -0500 Subject: [PATCH 36/74] OS X: use JavaVM framework from current Mac OS SDK --- setup.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index f56e91e..f8a20c9 100644 --- a/setup.py +++ b/setup.py @@ -44,12 +44,13 @@ if platform == 'android': libraries = ['sdl', 'log'] library_dirs = ['libs/' + environ['ARCH']] elif platform == 'darwin': - import objc - framework = objc.pathForFramework('JavaVM.framework') + import subprocess + framework = subprocess.Popen('xcrun --sdk macosx --show-sdk-path', + shell=True, stdout=subprocess.PIPE).communicate()[0].strip() if not framework: raise Exception('You must install Java on your Mac OS X distro') extra_link_args = ['-framework', 'JavaVM'] - include_dirs = [join(framework, 'Versions/A/Headers')] + include_dirs = [join(framework, 'System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers')] else: import subprocess # otherwise, we need to search the JDK_HOME From 18d506911ea0a3f65b3714d6b3675ee6d1a9b183 Mon Sep 17 00:00:00 2001 From: Max Smolens Date: Wed, 25 Feb 2015 22:59:28 -0500 Subject: [PATCH 37/74] Enable setting primitive data type fields on Java classes --- jnius/jnius_export_class.pxi | 55 +++++++++++++++++++++++++++++++++ tests/org/jnius/BasicsTest.java | 50 ++++++++++++++++++++++++++++-- tests/test_basics.py | 20 ++++++++++++ 3 files changed, 123 insertions(+), 2 deletions(-) diff --git a/jnius/jnius_export_class.pxi b/jnius/jnius_export_class.pxi index 7638ab5..63100b0 100644 --- a/jnius/jnius_export_class.pxi +++ b/jnius/jnius_export_class.pxi @@ -321,6 +321,61 @@ cdef class JavaField(object): j_self = (obj).j_self.obj return self.read_field(j_self) + def __set__(self, obj, value): + cdef jobject j_self + + self.ensure_field() + if obj is None: + # set not implemented for static fields + raise NotImplementedError() + + j_self = (obj).j_self.obj + self.write_field(j_self, value) + + cdef write_field(self, jobject j_self, value): + cdef jboolean j_boolean + cdef jbyte j_byte + cdef jchar j_char + cdef jshort j_short + cdef jint j_int + cdef jlong j_long + cdef jfloat j_float + cdef jdouble j_double + cdef JNIEnv *j_env = get_jnienv() + + # type of the java field + r = self.definition[0] + + # set the java field; implemented only for primitive types + if r == 'Z': + j_boolean = value + j_env[0].SetBooleanField(j_env, j_self, self.j_field, j_boolean) + elif r == 'B': + j_byte = value + j_env[0].SetByteField(j_env, j_self, self.j_field, j_byte) + elif r == 'C': + j_char = value + j_env[0].SetCharField(j_env, j_self, self.j_field, j_char) + elif r == 'S': + j_short = value + j_env[0].SetShortField(j_env, j_self, self.j_field, j_short) + elif r == 'I': + j_int = value + j_env[0].SetIntField(j_env, j_self, self.j_field, j_int) + elif r == 'J': + j_long = value + j_env[0].SetLongField(j_env, j_self, self.j_field, j_long) + elif r == 'F': + j_float = value + j_env[0].SetFloatField(j_env, j_self, self.j_field, j_float) + elif r == 'D': + j_double = value + j_env[0].SetDoubleField(j_env, j_self, self.j_field, j_double) + else: + raise Exception('Invalid field definition') + + check_exception(j_env) + cdef read_field(self, jobject j_self): cdef jboolean j_boolean cdef jbyte j_byte diff --git a/tests/org/jnius/BasicsTest.java b/tests/org/jnius/BasicsTest.java index 6a4e47d..cdfa4e8 100644 --- a/tests/org/jnius/BasicsTest.java +++ b/tests/org/jnius/BasicsTest.java @@ -43,6 +43,19 @@ public class BasicsTest { public double fieldD = 1.23456789; public String fieldString = new String("helloworld"); + public boolean fieldSetZ; + public byte fieldSetB; + public char fieldSetC; + public short fieldSetS; + public int fieldSetI; + public long fieldSetJ; + public float fieldSetF; + public double fieldSetD; + public String fieldSetString; + + // Floating-point comparison epsilon + private final static double EPSILON = 1E-6; + public BasicsTest() {} public BasicsTest(byte fieldBVal) { fieldB = fieldBVal; @@ -97,9 +110,10 @@ public class BasicsTest { public boolean methodParamsZBCSIJFD(boolean x1, byte x2, char x3, short x4, int x5, long x6, float x7, double x8) { - // ADD float / double, but dunno how to do with approx return (x1 == true && x2 == 127 && x3 == 'k' && x4 == 32767 && - x5 == 2147483467 && x6 == 2147483467); + x5 == 2147483467 && x6 == 2147483467 && + (Math.abs(x7 - 1.23456789f) < EPSILON) && + (Math.abs(x8 - 1.23456789) < EPSILON)); } public boolean methodParamsString(String s) { @@ -149,4 +163,36 @@ public class BasicsTest { x[1] = 1; x[2] = -127; } + + public boolean testFieldSetZ() { + return (fieldSetZ == true); + } + + public boolean testFieldSetB() { + return (fieldSetB == 127); + } + + public boolean testFieldSetC() { + return (fieldSetC == 'k'); + } + + public boolean testFieldSetS() { + return (fieldSetS == 32767); + } + + public boolean testFieldSetI() { + return (fieldSetI == 2147483467); + } + + public boolean testFieldSetJ() { + return (fieldSetJ == 2147483467); + } + + public boolean testFieldSetF() { + return (Math.abs(fieldSetF - 1.23456789f) < EPSILON); + } + + public boolean testFieldSetD() { + return (Math.abs(fieldSetD - 1.23456789) < EPSILON); + } } diff --git a/tests/test_basics.py b/tests/test_basics.py index 3964561..a28b620 100644 --- a/tests/test_basics.py +++ b/tests/test_basics.py @@ -55,6 +55,26 @@ class BasicsTest(unittest.TestCase): self.assertEquals(test.fieldB, 127) self.assertEquals(test2.fieldB, 10) + def test_instance_set_fields(self): + test = autoclass('org.jnius.BasicsTest')() + test.fieldSetZ = True + test.fieldSetB = 127 + test.fieldSetC = ord('k') + test.fieldSetS = 32767 + test.fieldSetI = 2147483467 + test.fieldSetJ = 2147483467 + test.fieldSetF = 1.23456789 + test.fieldSetD = 1.23456789 + + self.assertTrue(test.testFieldSetZ()) + self.assertTrue(test.testFieldSetB()) + self.assertTrue(test.testFieldSetC()) + self.assertTrue(test.testFieldSetS()) + self.assertTrue(test.testFieldSetI()) + self.assertTrue(test.testFieldSetJ()) + self.assertTrue(test.testFieldSetF()) + self.assertTrue(test.testFieldSetD()) + def test_instances_methods_array(self): test = autoclass('org.jnius.BasicsTest')() self.assertEquals(test.methodArrayZ(), [True] * 3) From a0c9467d4245b780d8b61563b9cae500baeae402 Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Mon, 2 Mar 2015 10:59:34 +0100 Subject: [PATCH 38/74] add native support for CharSequence. We supported String, but not the baseclass CharSequence. Both returns a python string, and a python string can be assigned to both. --- jnius/jnius_conversion.pxi | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/jnius/jnius_conversion.pxi b/jnius/jnius_conversion.pxi index 00daf96..6064778 100644 --- a/jnius/jnius_conversion.pxi +++ b/jnius/jnius_conversion.pxi @@ -9,7 +9,10 @@ cdef void release_args(JNIEnv *j_env, tuple definition_args, jvalue *j_args, arg if py_arg is None: j_args[index].l = NULL if isinstance(py_arg, basestring) and \ - argtype in ('Ljava/lang/String;', 'Ljava/lang/Object;'): + argtype in ( + 'Ljava/lang/String;', + 'Ljava/lang/CharSequence;', + 'Ljava/lang/Object;'): j_env[0].DeleteLocalRef(j_env, j_args[index].l) elif argtype[0] == '[': ret = convert_jarray_to_python(j_env, argtype[1:], j_args[index].l) @@ -50,7 +53,10 @@ cdef void populate_args(JNIEnv *j_env, tuple definition_args, jvalue *j_args, ar if py_arg is None: j_args[index].l = NULL elif isinstance(py_arg, basestring) and \ - argtype in ('Ljava/lang/String;', 'Ljava/lang/Object;'): + argtype in ( + 'Ljava/lang/String;', + 'Ljava/lang/CharSequence;', + 'Ljava/lang/Object;'): py_str = py_arg.encode('utf-8') j_args[index].l = j_env[0].NewStringUTF(j_env, py_str) elif isinstance(py_arg, JavaClass): @@ -123,7 +129,7 @@ cdef convert_jobject_to_python(JNIEnv *j_env, bytes definition, jobject j_object # Ie, B would be passed as Ljava/lang/Character; # if we got a string, just convert back to Python str. - if r == 'java/lang/String': + if r in ('java/lang/String', 'java/lang/CharSequence'): c_str = j_env[0].GetStringUTFChars(j_env, j_object, NULL) py_str = c_str j_env[0].ReleaseStringUTFChars(j_env, j_object, c_str) @@ -311,7 +317,10 @@ cdef jobject convert_python_to_jobject(JNIEnv *j_env, definition, obj) except *: if obj is None: return NULL elif isinstance(obj, basestring) and \ - definition in ('Ljava/lang/String;', 'Ljava/lang/Object;'): + definition in ( + 'Ljava/lang/String;', + 'Ljava/lang/CharSequence;', + 'Ljava/lang/Object;'): return j_env[0].NewStringUTF(j_env, obj) elif isinstance(obj, (int, long)) and \ definition in ( @@ -522,7 +531,10 @@ cdef jobject convert_pyarray_to_java(JNIEnv *j_env, definition, pyarray) except j_env[0].SetObjectArrayElement( j_env, ret, i, NULL) elif isinstance(arg, basestring) and \ - definition in ('Ljava/lang/String;', 'Ljava/lang/Object;'): + definition in ( + 'Ljava/lang/String;', + 'Ljava/lang/CharSequence;', + 'Ljava/lang/Object;'): j_string = j_env[0].NewStringUTF( j_env, arg) j_env[0].SetObjectArrayElement( From ce3572ba79bff1a91066f7899673f63c1c3ecf7c Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Mon, 2 Mar 2015 11:22:05 +0100 Subject: [PATCH 39/74] fixes hashCode overflow. (disclamer: i wont talk about collision probabiliity or whatever. In my point of view, it seems very low, as the id() is based on the address of the object, it except if you uses tons of memory, you wont get it. And whatever the changes are, this will never keep up safe from colliding with others java object.). Closes #146 --- jnius/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/jnius/__init__.py b/jnius/__init__.py index 8ac24eb..34c2599 100644 --- a/jnius/__init__.py +++ b/jnius/__init__.py @@ -15,15 +15,17 @@ from .reflect import * # XXX monkey patch methods that cannot be in cython. # Cython doesn't allow to set new attribute on methods it compiled +HASHCODE_MAX = 2 ** 31 - 1 + class PythonJavaClass_(PythonJavaClass): @java_method('()I', name='hashCode') def hashCode(self): - return id(self) + return id(self) % HASHCODE_MAX @java_method('()Ljava/lang/String;', name='hashCode') def hashCode_(self): - return '{}'.format(id(self)) + return '{}'.format(self.hashCode()) @java_method('()Ljava/lang/String;', name='toString') def toString(self): From e149451ed1c9b902288a965f8539f6eb332bf6d3 Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Mon, 2 Mar 2015 11:30:23 +0100 Subject: [PATCH 40/74] fix license. Closes #139 --- setup.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/setup.py b/setup.py index f56e91e..0341ebe 100644 --- a/setup.py +++ b/setup.py @@ -128,8 +128,7 @@ setup(name='jnius', classifiers=[ 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', - 'License :: OSI Approved :: GNU Library or Lesser ' - 'General Public License (LGPL)', + 'License :: OSI Approved :: MIT License', 'Natural Language :: English', 'Operating System :: MacOS :: MacOS X', 'Operating System :: Microsoft :: Windows', From 6d91a2fe6278feb2c98a90a0a967f16eef82d9e8 Mon Sep 17 00:00:00 2001 From: Benson Margulies Date: Thu, 30 Apr 2015 14:04:58 -0400 Subject: [PATCH 41/74] Fix 159: passing the result of autoclass to java.lang.Class parameter. --- jnius/jnius_utils.pxi | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/jnius/jnius_utils.pxi b/jnius/jnius_utils.pxi index 31bdc6d..2e2b0ae 100644 --- a/jnius/jnius_utils.pxi +++ b/jnius/jnius_utils.pxi @@ -278,6 +278,11 @@ cdef int calculate_score(sign_args, args, is_varargs=False) except *: continue return -1 + # accept an autoclass class for java/lang/Class. + if hasattr(arg, '__javaclass__') and r == 'java/lang/Class': + score += 10 + continue + # if we pass a JavaClass, ensure the definition is matching # XXX FIXME what if we use a subclass or something ? if isinstance(arg, JavaClass): From 0c86904ede1b72f8539bc757b6745c069ad01fb4 Mon Sep 17 00:00:00 2001 From: Benson Margulies Date: Thu, 30 Apr 2015 16:13:16 -0400 Subject: [PATCH 42/74] Support to map getters to properties. --- jnius/reflect.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/jnius/reflect.py b/jnius/reflect.py index 8e260df..ceaaa9d 100644 --- a/jnius/reflect.py +++ b/jnius/reflect.py @@ -137,6 +137,11 @@ def ensureclass(clsname): registers.append(clsname) autoclass(clsname) +def lower_name(s): + return s[:1].lower() + s[1:] if s else '' + +def bean_getter(s): + return (s.startswith('get') and len(s) > 3 and s[3].isupper()) or (s.startswith('is') and len(s) > 2 and s[2].isupper()) def autoclass(clsname): jniname = clsname.replace('.', '/') @@ -176,9 +181,12 @@ def autoclass(clsname): get_signature(method.getReturnType())) cls = JavaStaticMethod if static else JavaMethod classDict[name] = cls(sig, varargs=varargs) + if name != 'getClass' and bean_getter(name) and len(method.getParameterTypes()) == 0: + lowername = lower_name(name[3:]) + classDict[lowername] = (lambda n: property(lambda self: getattr(self, n)()))(name) continue - # multpile signatures + # multiple signatures signatures = [] for index, subname in enumerate(methods_name): if subname != name: From 2753c59573bf353e3729cf24c769d79b1705ae91 Mon Sep 17 00:00:00 2001 From: Benson Margulies Date: Thu, 30 Apr 2015 16:24:27 -0400 Subject: [PATCH 43/74] Make lists work like Python list containers. --- jnius/reflect.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/jnius/reflect.py b/jnius/reflect.py index ceaaa9d..2e0f1f5 100644 --- a/jnius/reflect.py +++ b/jnius/reflect.py @@ -215,6 +215,12 @@ def autoclass(clsname): classDict[name] = JavaMultipleMethod(signatures) + for iclass in c.getInterfaces(): + if iclass.getName() == 'java.util.List': + classDict['__getitem__'] = lambda self, index: self.get(index) + classDict['__len__'] = lambda self: self.size() + break + for field in c.getFields(): static = Modifier.isStatic(field.getModifiers()) sig = get_signature(field.getType()) From 0ab1468bc5d75f1488a001a951dc557120a78ba7 Mon Sep 17 00:00:00 2001 From: Benson Margulies Date: Thu, 30 Apr 2015 19:36:56 -0400 Subject: [PATCH 44/74] Fix issue #162 by making 'make tests' pass. --- Makefile | 24 ++++++++++++++++-------- tests/test_proxy.py | 12 ++++++++++-- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index e815409..ada7e90 100644 --- a/Makefile +++ b/Makefile @@ -1,18 +1,26 @@ .PHONY: build_ext tests +JAVAC_OPTS=-target 1.6 -source 1.6 +JAVAC=javac $(JAVAC_OPTS) + build_ext: - javac jnius/src/org/jnius/NativeInvocationHandler.java + $(JAVAC) jnius/src/org/jnius/NativeInvocationHandler.java python setup.py build_ext --inplace -f -g +clean: + find . -name "*.class" -exec rm {} \; + rm -rf build + html: $(MAKE) -C docs html tests: build_ext - cd tests && javac org/jnius/HelloWorld.java - cd tests && javac org/jnius/BasicsTest.java - cd tests && javac org/jnius/MultipleMethods.java - cd tests && javac org/jnius/SimpleEnum.java - cd tests && javac org/jnius/InterfaceWithPublicEnum.java - cd tests && javac org/jnius/ClassArgument.java - cd tests && javac org/jnius/MultipleDimensions.java + cd tests && $(JAVAC) org/jnius/HelloWorld.java + cd tests && $(JAVAC) org/jnius/BasicsTest.java + cd tests && $(JAVAC) org/jnius/MultipleMethods.java + cd tests && $(JAVAC) org/jnius/SimpleEnum.java + cd tests && $(JAVAC) org/jnius/InterfaceWithPublicEnum.java + cd tests && $(JAVAC) org/jnius/ClassArgument.java + cd tests && $(JAVAC) org/jnius/MultipleDimensions.java + cp jnius/src/org/jnius/NativeInvocationHandler.class tests/org/jnius cd tests && env PYTHONPATH=..:$(PYTHONPATH) nosetests-2.7 -v diff --git a/tests/test_proxy.py b/tests/test_proxy.py index 7a5d7be..c7630ce 100644 --- a/tests/test_proxy.py +++ b/tests/test_proxy.py @@ -1,4 +1,5 @@ from jnius import autoclass, java_method, PythonJavaClass, cast +from nose.tools import * print '1: declare a TestImplem that implement Collection' @@ -104,7 +105,7 @@ class TestBadSignature(PythonJavaClass): pass -print '2: instanciate the class, with some data' +print '2: instantiate the class, with some data' a = TestImplem(*range(10)) print a print dir(a) @@ -150,4 +151,11 @@ print Collections.max(a2) #print Collections.shuffle(a2) # test bad signature -TestBadSignature() +threw = False +try: + TestBadSignature() +except Exception: + threw = True + +if not threw: + raise Exception("Failed to throw for bad signature") From cca4da3cb0e14934a375a92e4bad24595a30a4fd Mon Sep 17 00:00:00 2001 From: Benson Margulies Date: Thu, 30 Apr 2015 16:13:16 -0400 Subject: [PATCH 45/74] Some sauce to map Java Beans to python properties and lists. --- jnius/reflect.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/jnius/reflect.py b/jnius/reflect.py index 8e260df..2e0f1f5 100644 --- a/jnius/reflect.py +++ b/jnius/reflect.py @@ -137,6 +137,11 @@ def ensureclass(clsname): registers.append(clsname) autoclass(clsname) +def lower_name(s): + return s[:1].lower() + s[1:] if s else '' + +def bean_getter(s): + return (s.startswith('get') and len(s) > 3 and s[3].isupper()) or (s.startswith('is') and len(s) > 2 and s[2].isupper()) def autoclass(clsname): jniname = clsname.replace('.', '/') @@ -176,9 +181,12 @@ def autoclass(clsname): get_signature(method.getReturnType())) cls = JavaStaticMethod if static else JavaMethod classDict[name] = cls(sig, varargs=varargs) + if name != 'getClass' and bean_getter(name) and len(method.getParameterTypes()) == 0: + lowername = lower_name(name[3:]) + classDict[lowername] = (lambda n: property(lambda self: getattr(self, n)()))(name) continue - # multpile signatures + # multiple signatures signatures = [] for index, subname in enumerate(methods_name): if subname != name: @@ -207,6 +215,12 @@ def autoclass(clsname): classDict[name] = JavaMultipleMethod(signatures) + for iclass in c.getInterfaces(): + if iclass.getName() == 'java.util.List': + classDict['__getitem__'] = lambda self, index: self.get(index) + classDict['__len__'] = lambda self: self.size() + break + for field in c.getFields(): static = Modifier.isStatic(field.getModifiers()) sig = get_signature(field.getType()) From 4f8c918ad401adea45b0e295d56652143a270d84 Mon Sep 17 00:00:00 2001 From: Benson Margulies Date: Thu, 30 Apr 2015 19:36:56 -0400 Subject: [PATCH 46/74] Fix issue #162 by making 'make tests' pass. --- Makefile | 24 ++++++++++++++++-------- tests/test_proxy.py | 12 ++++++++++-- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index e815409..ada7e90 100644 --- a/Makefile +++ b/Makefile @@ -1,18 +1,26 @@ .PHONY: build_ext tests +JAVAC_OPTS=-target 1.6 -source 1.6 +JAVAC=javac $(JAVAC_OPTS) + build_ext: - javac jnius/src/org/jnius/NativeInvocationHandler.java + $(JAVAC) jnius/src/org/jnius/NativeInvocationHandler.java python setup.py build_ext --inplace -f -g +clean: + find . -name "*.class" -exec rm {} \; + rm -rf build + html: $(MAKE) -C docs html tests: build_ext - cd tests && javac org/jnius/HelloWorld.java - cd tests && javac org/jnius/BasicsTest.java - cd tests && javac org/jnius/MultipleMethods.java - cd tests && javac org/jnius/SimpleEnum.java - cd tests && javac org/jnius/InterfaceWithPublicEnum.java - cd tests && javac org/jnius/ClassArgument.java - cd tests && javac org/jnius/MultipleDimensions.java + cd tests && $(JAVAC) org/jnius/HelloWorld.java + cd tests && $(JAVAC) org/jnius/BasicsTest.java + cd tests && $(JAVAC) org/jnius/MultipleMethods.java + cd tests && $(JAVAC) org/jnius/SimpleEnum.java + cd tests && $(JAVAC) org/jnius/InterfaceWithPublicEnum.java + cd tests && $(JAVAC) org/jnius/ClassArgument.java + cd tests && $(JAVAC) org/jnius/MultipleDimensions.java + cp jnius/src/org/jnius/NativeInvocationHandler.class tests/org/jnius cd tests && env PYTHONPATH=..:$(PYTHONPATH) nosetests-2.7 -v diff --git a/tests/test_proxy.py b/tests/test_proxy.py index 7a5d7be..c7630ce 100644 --- a/tests/test_proxy.py +++ b/tests/test_proxy.py @@ -1,4 +1,5 @@ from jnius import autoclass, java_method, PythonJavaClass, cast +from nose.tools import * print '1: declare a TestImplem that implement Collection' @@ -104,7 +105,7 @@ class TestBadSignature(PythonJavaClass): pass -print '2: instanciate the class, with some data' +print '2: instantiate the class, with some data' a = TestImplem(*range(10)) print a print dir(a) @@ -150,4 +151,11 @@ print Collections.max(a2) #print Collections.shuffle(a2) # test bad signature -TestBadSignature() +threw = False +try: + TestBadSignature() +except Exception: + threw = True + +if not threw: + raise Exception("Failed to throw for bad signature") From 734e54789bc1b0e5d41b763c3e64436c916a26d0 Mon Sep 17 00:00:00 2001 From: Benson Margulies Date: Thu, 30 Apr 2015 21:07:08 -0400 Subject: [PATCH 47/74] cleanup the build process. Use ant, put the java code in a proper java root. --- .gitignore | 1 + Makefile | 19 +++++--------- build.xml | 25 +++++++++++++++++++ setup.py | 1 + .../{ => java-src}/org/jnius/BasicsTest.java | 0 .../org/jnius/ClassArgument.java | 0 .../{ => java-src}/org/jnius/HelloWorld.java | 0 .../org/jnius/InterfaceWithPublicEnum.java | 0 .../org/jnius/MultipleDimensions.java | 0 .../org/jnius/MultipleMethods.java | 0 .../{ => java-src}/org/jnius/SimpleEnum.java | 0 11 files changed, 33 insertions(+), 13 deletions(-) create mode 100644 build.xml rename tests/{ => java-src}/org/jnius/BasicsTest.java (100%) rename tests/{ => java-src}/org/jnius/ClassArgument.java (100%) rename tests/{ => java-src}/org/jnius/HelloWorld.java (100%) rename tests/{ => java-src}/org/jnius/InterfaceWithPublicEnum.java (100%) rename tests/{ => java-src}/org/jnius/MultipleDimensions.java (100%) rename tests/{ => java-src}/org/jnius/MultipleMethods.java (100%) rename tests/{ => java-src}/org/jnius/SimpleEnum.java (100%) diff --git a/.gitignore b/.gitignore index d4f8b5a..f671175 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ build .*.swp *.class .idea +*~ \ No newline at end of file diff --git a/Makefile b/Makefile index ada7e90..e1a7734 100644 --- a/Makefile +++ b/Makefile @@ -1,26 +1,19 @@ +all: build_ext + .PHONY: build_ext tests -JAVAC_OPTS=-target 1.6 -source 1.6 -JAVAC=javac $(JAVAC_OPTS) +ANT=ant build_ext: - $(JAVAC) jnius/src/org/jnius/NativeInvocationHandler.java + $(ANT) all python setup.py build_ext --inplace -f -g clean: - find . -name "*.class" -exec rm {} \; + $(ANT) clean rm -rf build html: $(MAKE) -C docs html tests: build_ext - cd tests && $(JAVAC) org/jnius/HelloWorld.java - cd tests && $(JAVAC) org/jnius/BasicsTest.java - cd tests && $(JAVAC) org/jnius/MultipleMethods.java - cd tests && $(JAVAC) org/jnius/SimpleEnum.java - cd tests && $(JAVAC) org/jnius/InterfaceWithPublicEnum.java - cd tests && $(JAVAC) org/jnius/ClassArgument.java - cd tests && $(JAVAC) org/jnius/MultipleDimensions.java - cp jnius/src/org/jnius/NativeInvocationHandler.class tests/org/jnius - cd tests && env PYTHONPATH=..:$(PYTHONPATH) nosetests-2.7 -v + (cd tests; env CLASSPATH=../build/test-classes:../build/classes PYTHONPATH=..:$(PYTHONPATH) nosetests-2.7 -v) diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..5258b15 --- /dev/null +++ b/build.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/setup.py b/setup.py index c6beedd..cef3be1 100644 --- a/setup.py +++ b/setup.py @@ -44,6 +44,7 @@ if platform == 'android': libraries = ['sdl', 'log'] library_dirs = ['libs/' + environ['ARCH']] elif platform == 'darwin': + # TODO: this is the old Java 1.6 mechanism. Will need help for 1.7. import subprocess framework = subprocess.Popen('xcrun --sdk macosx --show-sdk-path', shell=True, stdout=subprocess.PIPE).communicate()[0].strip() diff --git a/tests/org/jnius/BasicsTest.java b/tests/java-src/org/jnius/BasicsTest.java similarity index 100% rename from tests/org/jnius/BasicsTest.java rename to tests/java-src/org/jnius/BasicsTest.java diff --git a/tests/org/jnius/ClassArgument.java b/tests/java-src/org/jnius/ClassArgument.java similarity index 100% rename from tests/org/jnius/ClassArgument.java rename to tests/java-src/org/jnius/ClassArgument.java diff --git a/tests/org/jnius/HelloWorld.java b/tests/java-src/org/jnius/HelloWorld.java similarity index 100% rename from tests/org/jnius/HelloWorld.java rename to tests/java-src/org/jnius/HelloWorld.java diff --git a/tests/org/jnius/InterfaceWithPublicEnum.java b/tests/java-src/org/jnius/InterfaceWithPublicEnum.java similarity index 100% rename from tests/org/jnius/InterfaceWithPublicEnum.java rename to tests/java-src/org/jnius/InterfaceWithPublicEnum.java diff --git a/tests/org/jnius/MultipleDimensions.java b/tests/java-src/org/jnius/MultipleDimensions.java similarity index 100% rename from tests/org/jnius/MultipleDimensions.java rename to tests/java-src/org/jnius/MultipleDimensions.java diff --git a/tests/org/jnius/MultipleMethods.java b/tests/java-src/org/jnius/MultipleMethods.java similarity index 100% rename from tests/org/jnius/MultipleMethods.java rename to tests/java-src/org/jnius/MultipleMethods.java diff --git a/tests/org/jnius/SimpleEnum.java b/tests/java-src/org/jnius/SimpleEnum.java similarity index 100% rename from tests/org/jnius/SimpleEnum.java rename to tests/java-src/org/jnius/SimpleEnum.java From 8c910485debc4c5b06880a5fdd627664fc1f8917 Mon Sep 17 00:00:00 2001 From: Benson Margulies Date: Thu, 30 Apr 2015 21:29:55 -0400 Subject: [PATCH 48/74] Use the java from java_home on OSX. --- setup.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index cef3be1..e696d3f 100644 --- a/setup.py +++ b/setup.py @@ -44,14 +44,21 @@ if platform == 'android': libraries = ['sdl', 'log'] library_dirs = ['libs/' + environ['ARCH']] elif platform == 'darwin': - # TODO: this is the old Java 1.6 mechanism. Will need help for 1.7. import subprocess - framework = subprocess.Popen('xcrun --sdk macosx --show-sdk-path', + framework = subprocess.Popen('/usr/libexec/java_home', shell=True, stdout=subprocess.PIPE).communicate()[0].strip() + print('java_home: {0}\n'.format(framework)); if not framework: raise Exception('You must install Java on your Mac OS X distro') - extra_link_args = ['-framework', 'JavaVM'] - include_dirs = [join(framework, 'System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers')] + if '1.6' in framework: + extra_link_args = ['-framework', 'JavaVM'] + include_dirs = [join(framework, 'System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers')] + else: + # TODO: This won't make a dylib that moves from one machine to another. + # TODO: it needs a switch to dlopen and a configuration of the path. + java_runtime_lib = '{0}/jre/lib/server'.format(framework) + extra_link_args = ['-L', java_runtime_lib, '-ljvm', '-rpath', java_runtime_lib] + include_dirs = ['{0}/include'.format(framework), '{0}/include/darwin'.format(framework)] else: import subprocess # otherwise, we need to search the JDK_HOME From e1070965cc534428d564937a680c9e6a86e082b9 Mon Sep 17 00:00:00 2001 From: Benson Margulies Date: Fri, 1 May 2015 17:57:22 -0400 Subject: [PATCH 49/74] Support Python 3.4 see if we can't make travis happy. --- .travis.yml | 8 +- Makefile | 16 +++- jnius/jnius.pyx | 5 +- jnius/jnius_conversion.pxi | 112 ++++++++++++++--------- jnius/jnius_export_class.pxi | 62 +++++++------ jnius/jnius_export_func.pxi | 12 ++- jnius/jnius_jvm_desktop.pxi | 4 + jnius/jnius_nativetypes.pxi | 24 ++++- jnius/jnius_nativetypes3.pxi | 78 ++++++++++++++++ jnius/jnius_utils.pxi | 42 +++++++-- jnius/reflect.py | 27 +++--- setup.py | 12 ++- tests/test_assignable.py | 5 + tests/test_bad_declaration.py | 7 +- tests/test_basics.py | 7 +- tests/test_bytearray.py | 9 +- tests/test_cast.py | 5 + tests/test_class_argument.py | 5 + tests/test_enum.py | 5 + tests/test_implementation.py | 5 + tests/test_interface.py | 5 + tests/test_jnitable_overflow.py | 5 + tests/test_method_multiple_signatures.py | 5 + tests/test_multidimension.py | 5 + tests/test_output_args.py | 5 + tests/test_proxy.py | 59 ++++++------ tests/test_reflect.py | 5 + tests/test_simple.py | 9 +- 28 files changed, 408 insertions(+), 140 deletions(-) create mode 100644 jnius/jnius_nativetypes3.pxi diff --git a/.travis.yml b/.travis.yml index 05d6869..3f9b48d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,19 @@ language: python python: - "2.7" + - "3.4" # command to install dependencies before_install: - sudo apt-get update - - sudo apt-get install python-pip openjdk-7-jdk + - sudo apt-get install python-pip openjdk-7-jdk ant install: - pip install --upgrade cython --use-mirrors + - pip install --upgrade future --use-mirrors + - pip install --upgrade setuptools --use-mirrors - make # command to run tests script: - - make tests + - make clean all tests + - make clean all tests PYTHON3=1 diff --git a/Makefile b/Makefile index e1a7734..38e21a6 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,20 @@ all: build_ext -.PHONY: build_ext tests +.PHONY: build_ext tests tests3 + +ifdef PYTHON3 +PYTHON=python3 +NOSETESTS=nosetests-3 +else +PYTHON=python +NOSETESTS=nosetests-2.7 +endif ANT=ant build_ext: $(ANT) all - python setup.py build_ext --inplace -f -g + $(PYTHON) setup.py build_ext --inplace -f -g clean: $(ANT) clean @@ -15,5 +23,5 @@ clean: html: $(MAKE) -C docs html -tests: build_ext - (cd tests; env CLASSPATH=../build/test-classes:../build/classes PYTHONPATH=..:$(PYTHONPATH) nosetests-2.7 -v) +tests: + (cd tests; env CLASSPATH=../build/test-classes:../build/classes PYTHONPATH=..:$(PYTHONPATH) $(NOSETESTS) -v) diff --git a/jnius/jnius.pyx b/jnius/jnius.pyx index dc074ba..678fd5e 100644 --- a/jnius/jnius.pyx +++ b/jnius/jnius.pyx @@ -106,7 +106,10 @@ include "jnius_env.pxi" include "jnius_utils.pxi" include "jnius_conversion.pxi" include "jnius_localref.pxi" -include "jnius_nativetypes.pxi" +IF JNIUS_PYTHON3: + include "jnius_nativetypes3.pxi" +ELSE: + include "jnius_nativetypes.pxi" include "jnius_export_func.pxi" include "jnius_export_class.pxi" diff --git a/jnius/jnius_conversion.pxi b/jnius/jnius_conversion.pxi index 6064778..b276b55 100644 --- a/jnius/jnius_conversion.pxi +++ b/jnius/jnius_conversion.pxi @@ -1,3 +1,10 @@ +from cpython.version cimport PY_MAJOR_VERSION + +cdef jstringy_arg(argtype): + return argtype in ('Ljava/lang/String;', + 'Ljava/lang/CharSequence;', + 'Ljava/lang/Object;') + cdef void release_args(JNIEnv *j_env, tuple definition_args, jvalue *j_args, args) except *: # do the conversion from a Python object to Java from a Java definition cdef JavaObject jo @@ -9,10 +16,7 @@ cdef void release_args(JNIEnv *j_env, tuple definition_args, jvalue *j_args, arg if py_arg is None: j_args[index].l = NULL if isinstance(py_arg, basestring) and \ - argtype in ( - 'Ljava/lang/String;', - 'Ljava/lang/CharSequence;', - 'Ljava/lang/Object;'): + jstringy_arg(argtype): j_env[0].DeleteLocalRef(j_env, j_args[index].l) elif argtype[0] == '[': ret = convert_jarray_to_python(j_env, argtype[1:], j_args[index].l) @@ -22,7 +26,6 @@ cdef void release_args(JNIEnv *j_env, tuple definition_args, jvalue *j_args, arg pass j_env[0].DeleteLocalRef(j_env, j_args[index].l) - cdef void populate_args(JNIEnv *j_env, tuple definition_args, jvalue *j_args, args) except *: # do the conversion from a Python object to Java from a Java definition cdef JavaClassStorage jcs @@ -52,11 +55,8 @@ cdef void populate_args(JNIEnv *j_env, tuple definition_args, jvalue *j_args, ar elif argtype[0] == 'L': if py_arg is None: j_args[index].l = NULL - elif isinstance(py_arg, basestring) and \ - argtype in ( - 'Ljava/lang/String;', - 'Ljava/lang/CharSequence;', - 'Ljava/lang/Object;'): + elif (isinstance(py_arg, basestring) or (PY_MAJOR_VERSION >=3 and isinstance(py_arg, str))) \ + and jstringy_arg(argtype): py_str = py_arg.encode('utf-8') j_args[index].l = j_env[0].NewStringUTF(j_env, py_str) elif isinstance(py_arg, JavaClass): @@ -89,28 +89,30 @@ cdef void populate_args(JNIEnv *j_env, tuple definition_args, jvalue *j_args, ar if py_arg is None: j_args[index].l = NULL continue - if isinstance(py_arg, basestring): + if isinstance(py_arg, basestring) and PY_MAJOR_VERSION < 3: if argtype == '[B': py_arg = map(ord, py_arg) elif argtype == '[C': py_arg = list(py_arg) + if isinstance(py_arg, str) and PY_MAJOR_VERSION >= 3 and argtype == '[C': + py_arg = list(py_arg) if isinstance(py_arg, ByteArray) and argtype != '[B': raise JavaException( 'Cannot use ByteArray for signature {}'.format(argtype)) - if not isinstance(py_arg, (list, tuple, ByteArray)): + if not isinstance(py_arg, (list, tuple, ByteArray, bytes)): raise JavaException('Expecting a python list/tuple, got ' '{0!r}'.format(py_arg)) j_args[index].l = convert_pyarray_to_java( j_env, argtype[1:], py_arg) -cdef convert_jobject_to_python(JNIEnv *j_env, bytes definition, jobject j_object): +cdef convert_jobject_to_python(JNIEnv *j_env, definition, jobject j_object): # Convert a Java Object to a Python object, according to the definition. # If the definition is a java/lang/Object, then try to determine what is it # exactly. cdef char *c_str cdef bytes py_str - cdef bytes r = definition[1:-1] + r = definition[1:-1] cdef JavaObject ret_jobject cdef JavaClass ret_jc cdef jclass retclass @@ -119,6 +121,7 @@ cdef convert_jobject_to_python(JNIEnv *j_env, bytes definition, jobject j_object # we got a generic object -> lookup for the real name instead. if r == 'java/lang/Object': r = definition = lookup_java_object_name(j_env, j_object) + print('cjtp:r {0} definition {1}'.format(r, definition)) if definition[0] == '[': return convert_jarray_to_python(j_env, definition[1:], j_object) @@ -133,7 +136,10 @@ cdef convert_jobject_to_python(JNIEnv *j_env, bytes definition, jobject j_object c_str = j_env[0].GetStringUTFChars(j_env, j_object, NULL) py_str = c_str j_env[0].ReleaseStringUTFChars(j_env, j_object, c_str) - return py_str + if PY_MAJOR_VERSION < 3: + return py_str + else: + return py_str.decode('utf-8') # XXX should be deactivable from configuration # ie, user might not want autoconvertion of lang classes. @@ -178,7 +184,7 @@ cdef convert_jobject_to_python(JNIEnv *j_env, bytes definition, jobject j_object from .reflect import Object ret_jc = Object(noinstance=True) else: - from reflect import autoclass + from .reflect import autoclass ret_jc = autoclass(r.replace('/', '.'))(noinstance=True) else: ret_jc = jclass_register[r](noinstance=True) @@ -316,12 +322,11 @@ cdef jobject convert_python_to_jobject(JNIEnv *j_env, definition, obj) except *: elif definition[0] == 'L': if obj is None: return NULL - elif isinstance(obj, basestring) and \ - definition in ( - 'Ljava/lang/String;', - 'Ljava/lang/CharSequence;', - 'Ljava/lang/Object;'): + elif isinstance(obj, basestring) and jstringy_arg(definition): return j_env[0].NewStringUTF(j_env, obj) + elif isinstance(obj, str) and PY_MAJOR_VERSION >= 3 and jstringy_arg(definition): + utf8 = obj.encode('utf-8') + return j_env[0].NewStringUTF(j_env, utf8) elif isinstance(obj, (int, long)) and \ definition in ( 'Ljava/lang/Integer;', @@ -361,13 +366,23 @@ cdef jobject convert_python_to_jobject(JNIEnv *j_env, definition, obj) except *: definition[1:-1], obj)) elif definition[0] == '[': - conversions = { - int: 'I', - bool: 'Z', - long: 'J', - float: 'F', - basestring: 'Ljava/lang/String;', - } + if PY_MAJOR_VERSION < 3: + conversions = { + int: 'I', + bool: 'Z', + long: 'J', + float: 'F', + basestring: 'Ljava/lang/String;', + } + else: + conversions = { + int: 'I', + bool: 'Z', + long: 'J', + float: 'F', + str: 'Ljava/lang/String;', + bytes: 'B' + } retclass = j_env[0].FindClass(j_env, 'java/lang/Object') retobject = j_env[0].NewObjectArray(j_env, len(obj), retclass, NULL) for index, item in enumerate(obj): @@ -443,13 +458,23 @@ cdef jobject convert_pyarray_to_java(JNIEnv *j_env, definition, pyarray) except if definition == 'Ljava/lang/Object;' and len(pyarray) > 0: # then the method will accept any array type as param # let's be as precise as we can - conversions = { - int: 'I', - bool: 'Z', - long: 'J', - float: 'F', - basestring: 'Ljava/lang/String;', - } + if PY_MAJOR_VERSION < 3: + conversions = { + int: 'I', + bool: 'Z', + long: 'J', + float: 'F', + basestring: 'Ljava/lang/String;', + } + else: + conversions = { + int: 'I', + bool: 'Z', + long: 'J', + float: 'F', + bytes: 'B', + str: 'Ljava/lang/String;', + } for _type, override in conversions.iteritems(): if isinstance(pyarray[0], _type): definition = override @@ -518,8 +543,9 @@ cdef jobject convert_pyarray_to_java(JNIEnv *j_env, definition, pyarray) except ret, i, 1, &j_double) elif definition[0] == 'L': + defstr = str_for_c(definition[1:-1]) j_class = j_env[0].FindClass( - j_env, definition[1:-1]) + j_env, defstr) if j_class == NULL: raise JavaException('Cannot create array with a class not ' 'found {0!r}'.format(definition[1:-1])) @@ -530,15 +556,19 @@ cdef jobject convert_pyarray_to_java(JNIEnv *j_env, definition, pyarray) except if arg is None: j_env[0].SetObjectArrayElement( j_env, ret, i, NULL) - elif isinstance(arg, basestring) and \ - definition in ( - 'Ljava/lang/String;', - 'Ljava/lang/CharSequence;', - 'Ljava/lang/Object;'): + elif isinstance(arg, basestring) and PY_MAJOR_VERSION < 3 and \ + jstringy_arg(definition): j_string = j_env[0].NewStringUTF( j_env, arg) j_env[0].SetObjectArrayElement( j_env, ret, i, j_string) + elif isinstance(arg, str) and PY_MAJOR_VERSION >= 3 and \ + jstringy_arg(definition): + utf8 = arg.encode('utf-8') + j_string = j_env[0].NewStringUTF( + j_env, utf8) + j_env[0].SetObjectArrayElement( + j_env, ret, i, j_string) elif isinstance(arg, JavaClass): jc = arg check_assignable_from(j_env, jc, definition[1:-1]) diff --git a/jnius/jnius_export_class.pxi b/jnius/jnius_export_class.pxi index abf075a..d562c62 100644 --- a/jnius/jnius_export_class.pxi +++ b/jnius/jnius_export_class.pxi @@ -1,4 +1,3 @@ - class JavaException(Exception): '''Can be a real java exception, or just an exception from the wrapper. ''' @@ -42,7 +41,7 @@ cdef dict jclass_register = {} class MetaJavaClass(type): def __new__(meta, classname, bases, classDict): meta.resolve_class(classDict) - tp = type.__new__(meta, classname, bases, classDict) + tp = type.__new__(meta, str(classname), bases, classDict) jclass_register[classDict['__javaclass__']] = tp return tp @@ -92,8 +91,9 @@ class MetaJavaClass(type): raise JavaException('Unable to create the class' ' {0}'.format(__javaclass__)) else: + class_name = str_for_c(__javaclass__) jcs.j_cls = j_env[0].FindClass(j_env, - __javaclass__) + class_name) if jcs.j_cls == NULL: raise JavaException('Unable to find the class' ' {0}'.format(__javaclass__)) @@ -108,29 +108,29 @@ class MetaJavaClass(type): # search all the static JavaMethod within our class, and resolve them cdef JavaMethod jm cdef JavaMultipleMethod jmm - for name, value in classDict.iteritems(): + for name, value in items_compat(classDict): if isinstance(value, JavaMethod): jm = value if not jm.is_static: continue jm.set_resolve_info(j_env, jcs.j_cls, None, - name, __javaclass__) + str_for_c(name), str_for_c(__javaclass__)) elif isinstance(value, JavaMultipleMethod): jmm = value jmm.set_resolve_info(j_env, jcs.j_cls, None, - name, __javaclass__) + str_for_c(name), str_for_c(__javaclass__)) # search all the static JavaField within our class, and resolve them cdef JavaField jf - for name, value in classDict.iteritems(): + for name, value in items_compat(classDict): if not isinstance(value, JavaField): continue jf = value if not jf.is_static: continue jf.set_resolve_info(j_env, jcs.j_cls, - name, __javaclass__) + str_for_c(name), str_for_c(__javaclass__)) cdef class JavaClass(object): @@ -217,8 +217,9 @@ cdef class JavaClass(object): populate_args(j_env, d_args, j_args, args_) # get the java constructor + defstr = str_for_c(definition) constructor = j_env[0].GetMethodID( - j_env, self.j_cls, '', definition) + j_env, self.j_cls, '', defstr) if constructor == NULL: raise JavaException('Unable to found the constructor' ' for {0}'.format(self.__javaclass__)) @@ -246,23 +247,23 @@ cdef class JavaClass(object): cdef JavaMethod jm cdef JavaMultipleMethod jmm cdef JNIEnv *j_env = get_jnienv() - for name, value in self.__class__.__dict__.iteritems(): + for name, value in items_compat(self.__class__.__dict__): if isinstance(value, JavaMethod): jm = value if jm.is_static: continue jm.set_resolve_info(j_env, self.j_cls, self.j_self, - name, self.__javaclass__) + str_for_c(name), str_for_c(self.__javaclass__)) elif isinstance(value, JavaMultipleMethod): jmm = value jmm.set_resolve_info(j_env, self.j_cls, self.j_self, - name, self.__javaclass__) + str_for_c(name), str_for_c(self.__javaclass__)) cdef void resolve_fields(self) except *: # search all the JavaField within our class, and resolve them cdef JavaField jf cdef JNIEnv *j_env = get_jnienv() - for name, value in self.__class__.__dict__.iteritems(): + for name, value in items_compat(self.__class__.__dict__): if not isinstance(value, JavaField): continue jf = value @@ -283,10 +284,10 @@ cdef class JavaField(object): cdef jfieldID j_field cdef JNIEnv *j_env cdef jclass j_cls - cdef bytes definition cdef object is_static - cdef bytes name - cdef bytes classname + cdef name + cdef classname + cdef definition def __cinit__(self, definition, **kwargs): self.j_field = NULL @@ -298,7 +299,7 @@ cdef class JavaField(object): self.is_static = kwargs.get('static', False) cdef void set_resolve_info(self, JNIEnv *j_env, jclass j_cls, - bytes name, bytes classname): + name, classname): j_env = get_jnienv() self.name = name self.classname = classname @@ -309,13 +310,16 @@ cdef class JavaField(object): if self.j_field != NULL: return if self.is_static: + defstr = str_for_c(self.definition) self.j_field = j_env[0].GetStaticFieldID( j_env, self.j_cls, self.name, - self.definition) + defstr) else: + defstr = str_for_c(self.definition) + namestr = str_for_c(self.name) self.j_field = j_env[0].GetFieldID( - j_env, self.j_cls, self.name, - self.definition) + j_env, self.j_cls, namestr, + defstr) if self.j_field == NULL: raise JavaException('Unable to found the field {0}'.format(self.name)) @@ -537,9 +541,9 @@ cdef class JavaMethod(object): cdef jmethodID j_method cdef jclass j_cls cdef LocalRef j_self - cdef bytes name - cdef bytes classname - cdef bytes definition + cdef name + cdef classname + cdef definition cdef object is_static cdef bint is_varargs cdef object definition_return @@ -552,7 +556,7 @@ cdef class JavaMethod(object): def __init__(self, definition, **kwargs): super(JavaMethod, self).__init__() - self.definition = definition + self.definition = definition self.definition_return, self.definition_args = \ parse_definition(definition) self.is_static = kwargs.get('static', False) @@ -565,20 +569,22 @@ cdef class JavaMethod(object): if self.name is None: raise JavaException('Unable to find a None method!') if self.is_static: + defstr = str_for_c(self.definition) self.j_method = j_env[0].GetStaticMethodID( j_env, self.j_cls, self.name, - self.definition) + defstr) else: + defstr = str_for_c(self.definition) self.j_method = j_env[0].GetMethodID( j_env, self.j_cls, self.name, - self.definition) + defstr) if self.j_method == NULL: raise JavaException('Unable to find the method' ' {0}({1})'.format(self.name, self.definition)) cdef void set_resolve_info(self, JNIEnv *j_env, jclass j_cls, - LocalRef j_self, bytes name, bytes classname): + LocalRef j_self, name, classname): self.name = name self.classname = classname self.j_cls = j_cls @@ -870,7 +876,7 @@ cdef class JavaMultipleMethod(object): else: methods = self.static_methods - for signature, jm in methods.iteritems(): + for signature, jm in items_compat(methods): sign_ret, sign_args = jm.definition_return, jm.definition_args if jm.is_varargs: args_ = args[:len(sign_args) - 1] + (args[len(sign_args) - 1:],) diff --git a/jnius/jnius_export_func.pxi b/jnius/jnius_export_func.pxi index f02f97f..5ee69f4 100644 --- a/jnius/jnius_export_func.pxi +++ b/jnius/jnius_export_func.pxi @@ -1,23 +1,25 @@ +from cpython.version cimport PY_MAJOR_VERSION def cast(destclass, obj): cdef JavaClass jc cdef JavaClass jobj = obj - from reflect import autoclass - if isinstance(destclass, basestring): + from .reflect import autoclass + if (PY_MAJOR_VERSION < 3 and isinstance(destclass, basestring)) or \ + (PY_MAJOR_VERSION >=3 and isinstance(destclass, str)): jc = autoclass(destclass)(noinstance=True) else: jc = destclass(noinstance=True) jc.instanciate_from(jobj.j_self) return jc -def find_javaclass(bytes name): +def find_javaclass(namestr): + namestr = namestr.replace('.', '/') + cdef bytes name = str_for_c(namestr) from .reflect import Class cdef JavaClass cls cdef jclass jc cdef JNIEnv *j_env = get_jnienv() - name = name.replace('.', '/') - jc = j_env[0].FindClass(j_env, name) if jc == NULL: raise JavaException('Class not found {0!r}'.format(name)) diff --git a/jnius/jnius_jvm_desktop.pxi b/jnius/jnius_jvm_desktop.pxi index 9a24b81..e3834d9 100644 --- a/jnius/jnius_jvm_desktop.pxi +++ b/jnius/jnius_jvm_desktop.pxi @@ -1,3 +1,5 @@ +from cpython.version cimport PY_MAJOR_VERSION + # on desktop, we need to create an env :) # example taken from http://www.inonit.com/cygwin/jni/invocationApi/c.html @@ -30,6 +32,8 @@ cdef void create_jnienv() except *: options = malloc(sizeof(JavaVMOption) * len(optarr)) for i, opt in enumerate(optarr): + if PY_MAJOR_VERSION >= 3: + opt = opt.encode('utf-8') options[i].optionString = (opt) options[i].extraInfo = NULL diff --git a/jnius/jnius_nativetypes.pxi b/jnius/jnius_nativetypes.pxi index 296cb49..279bbb1 100644 --- a/jnius/jnius_nativetypes.pxi +++ b/jnius/jnius_nativetypes.pxi @@ -12,6 +12,8 @@ cdef python_op(int op, object a, object b): elif op == 5: return a != b + + cdef class ByteArray: cdef LocalRef _jobject cdef long _size @@ -23,6 +25,8 @@ cdef class ByteArray: self._buf = NULL self._arr = None + + def __dealloc__(self): cdef JNIEnv *j_env if self._buf != NULL: @@ -47,8 +51,21 @@ cdef class ByteArray: def __len__(self): return self._size - def __getitem__(self, long index): - return self._arr[index] + def __getitem__(self, index): + cdef long xx + if isinstance(index, slice): + val = [] + (start, stop, step) = index.indices(len(self._arr)) + for x in range(start, stop, step): + xx = x + val.append(self._arr[xx]) + return val + else: + xx = index + return self._arr[xx] + + def __getslice__(self, long i, long j): + return self._arr[i:j] def __richcmp__(self, other, op): cdef ByteArray b_other @@ -60,9 +77,6 @@ cdef class ByteArray: else: return False - def __getslice__(self, long i, long j): - return self._arr[i:j] - def tolist(self): return list(self[:]) diff --git a/jnius/jnius_nativetypes3.pxi b/jnius/jnius_nativetypes3.pxi new file mode 100644 index 0000000..c205538 --- /dev/null +++ b/jnius/jnius_nativetypes3.pxi @@ -0,0 +1,78 @@ + +cdef python_op(int op, object a, object b): + if op == 0: + return a < b + elif op == 1: + return a <= b + elif op == 2: + return a == b + elif op == 3: + return a >= b + elif op == 4: + return a > b + elif op == 5: + return a != b + +cdef class ByteArray: + cdef LocalRef _jobject + cdef long _size + cdef jbyte *_buf + cdef jbyte[:] _arr + + def __cinit__(self): + self._size = 0 + self._buf = NULL + self._arr = None + + def __dealloc__(self): + cdef JNIEnv *j_env + if self._buf != NULL: + j_env = get_jnienv() + j_env[0].ReleaseByteArrayElements(j_env, self._jobject.obj, self._buf, 0) + self._buf = NULL + self._jobject = None + + cdef void set_buffer(self, JNIEnv *env, jobject obj, long size, jbyte *buf): + if self._buf != NULL: + raise Exception('Cannot call set_buffer() twice.') + self._jobject = LocalRef() + self._jobject.create(env, obj) + self._size = size + self._buf = buf + self._arr = self._buf + + def __str__(self): + return ''.format( + self._size, id(self)) + + def __len__(self): + return self._size + + def __getitem__(self, index): + cdef long xx + if isinstance(index, slice): + val = [] + (start, stop, step) = index.indices(len(self._arr)) + for x in range(start, stop, step): + xx = x + val.append(self._arr[xx]) + return val + else: + xx = index + return self._arr[xx] + + def __richcmp__(self, other, op): + cdef ByteArray b_other + if isinstance(other, (list, tuple)): + return python_op(op, self.tolist(), other) + elif isinstance(other, ByteArray): + b_other = other + return python_op(op, self.tostring(), other.tostring()) + else: + return False + + def tolist(self): + return list(self[:]) + + def tostring(self): + return self._buf[:self._size] diff --git a/jnius/jnius_utils.pxi b/jnius/jnius_utils.pxi index 2e2b0ae..3c96844 100644 --- a/jnius/jnius_utils.pxi +++ b/jnius/jnius_utils.pxi @@ -1,3 +1,20 @@ +from cpython.version cimport PY_MAJOR_VERSION + +cdef str_for_c(s): + if PY_MAJOR_VERSION < 3: + if isinstance(s, unicode): + return s.encode('utf-8') + else: + return s + else: + return s.encode('utf-8') + +cdef items_compat(d): + if PY_MAJOR_VERSION >= 3: + return d.items() + else: + return d.iteritems() + cdef parse_definition(definition): # not a function, just a field if definition[0] != '(': @@ -114,7 +131,7 @@ cdef void _append_exception_trace_messages( cdef dict assignable_from = {} cdef int assignable_from_order = 0 -cdef void check_assignable_from(JNIEnv *env, JavaClass jc, bytes signature) except *: +cdef void check_assignable_from(JNIEnv *env, JavaClass jc, signature) except *: global assignable_from_order cdef jclass cls, clsA, clsB cdef jthrowable exc @@ -155,7 +172,7 @@ cdef void check_assignable_from(JNIEnv *env, JavaClass jc, bytes signature) exce # we got an object that doesn't match with the signature # check if we can use it. - cls = env[0].FindClass(env, signature) + cls = env[0].FindClass(env, str_for_c(signature)) if cls == NULL: raise JavaException('Unable to found the class for {0!r}'.format( signature)) @@ -177,12 +194,12 @@ cdef void check_assignable_from(JNIEnv *env, JavaClass jc, bytes signature) exce jc.__javaclass__, signature)) -cdef bytes lookup_java_object_name(JNIEnv *j_env, jobject j_obj): +cdef lookup_java_object_name(JNIEnv *j_env, jobject j_obj): cdef jclass jcls = j_env[0].GetObjectClass(j_env, j_obj) cdef jclass jcls2 = j_env[0].GetObjectClass(j_env, jcls) cdef jmethodID jmeth = j_env[0].GetMethodID(j_env, jcls2, 'getName', '()Ljava/lang/String;') cdef jobject js = j_env[0].CallObjectMethod(j_env, jcls, jmeth) - name = convert_jobject_to_python(j_env, b'Ljava/lang/String;', js) + name = convert_jobject_to_python(j_env, 'Ljava/lang/String;', js) j_env[0].DeleteLocalRef(j_env, js) j_env[0].DeleteLocalRef(j_env, jcls) j_env[0].DeleteLocalRef(j_env, jcls2) @@ -192,7 +209,6 @@ cdef bytes lookup_java_object_name(JNIEnv *j_env, jobject j_obj): cdef int calculate_score(sign_args, args, is_varargs=False) except *: cdef int index cdef int score = 0 - cdef bytes r cdef JavaClass jc if len(args) != len(sign_args) and not is_varargs: @@ -263,7 +279,11 @@ cdef int calculate_score(sign_args, args, is_varargs=False) except *: continue # if it's a string, accept any python string - if r == 'java/lang/String' and isinstance(arg, basestring): + if r == 'java/lang/String' and isinstance(arg, basestring) and PY_MAJOR_VERSION < 3: + score += 10 + continue + + if r == 'java/lang/String' and isinstance(arg, str) and PY_MAJOR_VERSION >= 3: score += 10 continue @@ -315,7 +335,15 @@ cdef int calculate_score(sign_args, args, is_varargs=False) except *: score += 10 continue - if (r == '[B' or r == '[C') and isinstance(arg, basestring): + if (r == '[B' or r == '[C') and isinstance(arg, basestring) and PY_MAJOR_VERSION < 3: + score += 10 + continue + + if (r == '[B') and isinstance(arg, bytes) and PY_MAJOR_VERSION >= 3: + score += 10 + continue + + if (r == '[C') and isinstance(arg, str) and PY_MAJOR_VERSION >= 3: score += 10 continue diff --git a/jnius/reflect.py b/jnius/reflect.py index 2e0f1f5..db820b4 100644 --- a/jnius/reflect.py +++ b/jnius/reflect.py @@ -1,13 +1,19 @@ +from __future__ import absolute_import +from __future__ import unicode_literals +from __future__ import print_function +from __future__ import division +from future import standard_library +standard_library.install_aliases() +from future.utils import with_metaclass __all__ = ('autoclass', 'ensureclass') -from jnius import ( +from .jnius import ( JavaClass, MetaJavaClass, JavaMethod, JavaStaticMethod, JavaField, JavaStaticField, JavaMultipleMethod, find_javaclass ) -class Class(JavaClass): - __metaclass__ = MetaJavaClass +class Class(with_metaclass(MetaJavaClass, JavaClass)): __javaclass__ = 'java/lang/Class' desiredAssertionStatus = JavaMethod('()Z') @@ -49,16 +55,14 @@ class Class(JavaClass): toString = JavaMethod('()Ljava/lang/String;') -class Object(JavaClass): - __metaclass__ = MetaJavaClass +class Object(with_metaclass(MetaJavaClass, JavaClass)): __javaclass__ = 'java/lang/Object' getClass = JavaMethod('()Ljava/lang/Class;') hashCode = JavaMethod('()I') -class Modifier(JavaClass): - __metaclass__ = MetaJavaClass +class Modifier(with_metaclass(MetaJavaClass, JavaClass)): __javaclass__ = 'java/lang/reflect/Modifier' isAbstract = JavaStaticMethod('(I)Z') @@ -75,8 +79,7 @@ class Modifier(JavaClass): isVolatile = JavaStaticMethod('(I)Z') -class Method(JavaClass): - __metaclass__ = MetaJavaClass +class Method(with_metaclass(MetaJavaClass, JavaClass)): __javaclass__ = 'java/lang/reflect/Method' getName = JavaMethod('()Ljava/lang/String;') @@ -87,8 +90,7 @@ class Method(JavaClass): isVarArgs = JavaMethod('()Z') -class Field(JavaClass): - __metaclass__ = MetaJavaClass +class Field(with_metaclass(MetaJavaClass, JavaClass)): __javaclass__ = 'java/lang/reflect/Field' getName = JavaMethod('()Ljava/lang/String;') @@ -97,8 +99,7 @@ class Field(JavaClass): getModifiers = JavaMethod('()I') -class Constructor(JavaClass): - __metaclass__ = MetaJavaClass +class Constructor(with_metaclass(MetaJavaClass, JavaClass)): __javaclass__ = 'java/lang/reflect/Constructor' toString = JavaMethod('()Ljava/lang/String;') diff --git a/setup.py b/setup.py index e696d3f..2e7e16d 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,4 @@ +from __future__ import print_function from distutils.core import setup, Extension from os import environ from os.path import dirname, join, exists @@ -45,8 +46,9 @@ if platform == 'android': library_dirs = ['libs/' + environ['ARCH']] elif platform == 'darwin': import subprocess + # That decode won't work in Py2? framework = subprocess.Popen('/usr/libexec/java_home', - shell=True, stdout=subprocess.PIPE).communicate()[0].strip() + shell=True, stdout=subprocess.PIPE).communicate()[0].decode().strip() print('java_home: {0}\n'.format(framework)); if not framework: raise Exception('You must install Java on your Mac OS X distro') @@ -107,7 +109,13 @@ else: # generate the config.pxi with open(join(dirname(__file__), 'jnius', 'config.pxi'), 'w') as fd: - fd.write('DEF JNIUS_PLATFORM = {0!r}'.format(platform)) + fd.write('DEF JNIUS_PLATFORM = {0!r}\n\n'.format(platform)) + if sys.version_info>=(3,0,0): + fd.write('DEF JNIUS_PYTHON3 = True\n\n') + else: + fd.write('DEF JNIUS_PYTHON3 = False\n\n') + if lib_location is not None: + fd.write('DEF JNIUS_LIB_SUFFIX = {0!r}\n\n'.format(lib_location)) with open(join('jnius', '__init__.py')) as fd: versionline = [x for x in fd.readlines() if x.startswith('__version__')] diff --git a/tests/test_assignable.py b/tests/test_assignable.py index 9306250..2922db8 100644 --- a/tests/test_assignable.py +++ b/tests/test_assignable.py @@ -1,3 +1,8 @@ +from __future__ import print_function +from __future__ import division +from __future__ import absolute_import +from future import standard_library +standard_library.install_aliases() import unittest from jnius import autoclass, JavaException diff --git a/tests/test_bad_declaration.py b/tests/test_bad_declaration.py index a1f66f4..90ffef6 100644 --- a/tests/test_bad_declaration.py +++ b/tests/test_bad_declaration.py @@ -1,3 +1,8 @@ +from __future__ import print_function +from __future__ import division +from __future__ import absolute_import +from future import standard_library +standard_library.install_aliases() import unittest from jnius import JavaException, JavaClass from jnius.reflect import autoclass @@ -48,4 +53,4 @@ class BadDeclarationTest(unittest.TestCase): self.assertEquals("java.lang.IllegalArgumentException", je.classname) self.assertEquals("helloworld2", je.innermessage) self.assertIn("Caused by:", je.stacktrace) - self.assertEquals(11, len(je.stacktrace)) \ No newline at end of file + self.assertEquals(11, len(je.stacktrace)) diff --git a/tests/test_basics.py b/tests/test_basics.py index a28b620..18ccc79 100644 --- a/tests/test_basics.py +++ b/tests/test_basics.py @@ -1,3 +1,8 @@ +from __future__ import print_function +from __future__ import division +from __future__ import absolute_import +from future import standard_library +standard_library.install_aliases() import unittest from jnius.reflect import autoclass @@ -118,7 +123,7 @@ class BasicsTest(unittest.TestCase): def test_instances_methods_params_object_list_long(self): test = autoclass('org.jnius.BasicsTest')() - self.assertEquals(test.methodParamsObject([1L, 2L]), True) + self.assertEquals(test.methodParamsObject([1, 2]), True) def test_instances_methods_params_array_byte(self): test = autoclass('org.jnius.BasicsTest')() diff --git a/tests/test_bytearray.py b/tests/test_bytearray.py index 226289d..9fbca69 100644 --- a/tests/test_bytearray.py +++ b/tests/test_bytearray.py @@ -1,3 +1,8 @@ +from __future__ import print_function +from __future__ import division +from __future__ import absolute_import +from future import standard_library +standard_library.install_aliases() import unittest from jnius import autoclass @@ -6,11 +11,11 @@ class StringArgumentForByteArrayTest(unittest.TestCase): def test_string_arg_for_byte_array(self): # the ByteBuffer.wrap() accept only byte[]. ByteBuffer = autoclass('java.nio.ByteBuffer') - self.assertIsNotNone(ByteBuffer.wrap('hello world')) + self.assertIsNotNone(ByteBuffer.wrap(b'hello world')) def test_string_arg_with_signed_char(self): ByteBuffer = autoclass('java.nio.ByteBuffer') - self.assertIsNotNone(ByteBuffer.wrap('\x00\xffHello World\x7f')) + self.assertIsNotNone(ByteBuffer.wrap(b'\x00\xffHello World\x7f')) def test_fill_byte_array(self): arr = [0, 0, 0] diff --git a/tests/test_cast.py b/tests/test_cast.py index 6498821..6dcf12f 100644 --- a/tests/test_cast.py +++ b/tests/test_cast.py @@ -1,3 +1,8 @@ +from __future__ import print_function +from __future__ import division +from __future__ import absolute_import +from future import standard_library +standard_library.install_aliases() import unittest from jnius.reflect import autoclass from jnius import cast diff --git a/tests/test_class_argument.py b/tests/test_class_argument.py index f4707fd..0264383 100644 --- a/tests/test_class_argument.py +++ b/tests/test_class_argument.py @@ -1,3 +1,8 @@ +from __future__ import print_function +from __future__ import division +from __future__ import absolute_import +from future import standard_library +standard_library.install_aliases() import unittest from jnius.reflect import autoclass diff --git a/tests/test_enum.py b/tests/test_enum.py index 8c10392..1e6edee 100644 --- a/tests/test_enum.py +++ b/tests/test_enum.py @@ -1,3 +1,8 @@ +from __future__ import print_function +from __future__ import division +from __future__ import absolute_import +from future import standard_library +standard_library.install_aliases() import unittest from jnius.reflect import autoclass diff --git a/tests/test_implementation.py b/tests/test_implementation.py index 9237ac4..c6f3d31 100644 --- a/tests/test_implementation.py +++ b/tests/test_implementation.py @@ -1,4 +1,9 @@ # -*- coding: utf-8 -*- +from __future__ import print_function +from __future__ import division +from __future__ import absolute_import +from future import standard_library +standard_library.install_aliases() import unittest from jnius.reflect import autoclass diff --git a/tests/test_interface.py b/tests/test_interface.py index 61b9a5c..42b2768 100644 --- a/tests/test_interface.py +++ b/tests/test_interface.py @@ -1,3 +1,8 @@ +from __future__ import print_function +from __future__ import division +from __future__ import absolute_import +from future import standard_library +standard_library.install_aliases() import unittest from jnius import autoclass, JavaException diff --git a/tests/test_jnitable_overflow.py b/tests/test_jnitable_overflow.py index 4bf379d..cef4b85 100644 --- a/tests/test_jnitable_overflow.py +++ b/tests/test_jnitable_overflow.py @@ -1,3 +1,8 @@ +from __future__ import print_function +from __future__ import division +from __future__ import absolute_import +from future import standard_library +standard_library.install_aliases() # run it, and check with Java VisualVM if we are eating too much memory or not! if __name__ == '__main__': from jnius import autoclass diff --git a/tests/test_method_multiple_signatures.py b/tests/test_method_multiple_signatures.py index b13db57..3f892fd 100644 --- a/tests/test_method_multiple_signatures.py +++ b/tests/test_method_multiple_signatures.py @@ -1,3 +1,8 @@ +from __future__ import print_function +from __future__ import division +from __future__ import absolute_import +from future import standard_library +standard_library.install_aliases() import unittest from jnius.reflect import autoclass diff --git a/tests/test_multidimension.py b/tests/test_multidimension.py index 87e16b1..6ab0fd7 100644 --- a/tests/test_multidimension.py +++ b/tests/test_multidimension.py @@ -1,3 +1,8 @@ +from __future__ import print_function +from __future__ import division +from __future__ import absolute_import +from future import standard_library +standard_library.install_aliases() import unittest from jnius.reflect import autoclass diff --git a/tests/test_output_args.py b/tests/test_output_args.py index e8f558e..e6a0fff 100644 --- a/tests/test_output_args.py +++ b/tests/test_output_args.py @@ -1,3 +1,8 @@ +from __future__ import print_function +from __future__ import division +from __future__ import absolute_import +from future import standard_library +standard_library.install_aliases() import unittest from jnius import autoclass diff --git a/tests/test_proxy.py b/tests/test_proxy.py index c7630ce..e8b4337 100644 --- a/tests/test_proxy.py +++ b/tests/test_proxy.py @@ -1,7 +1,14 @@ +from __future__ import print_function +from __future__ import division +from __future__ import absolute_import +from builtins import super +from future import standard_library +standard_library.install_aliases() +from builtins import range from jnius import autoclass, java_method, PythonJavaClass, cast from nose.tools import * -print '1: declare a TestImplem that implement Collection' +print('1: declare a TestImplem that implement Collection') class TestImplemIterator(PythonJavaClass): @@ -105,48 +112,48 @@ class TestBadSignature(PythonJavaClass): pass -print '2: instantiate the class, with some data' -a = TestImplem(*range(10)) -print a -print dir(a) +print('2: instantiate the class, with some data') +a = TestImplem(*list(range(10))) +print(a) +print(dir(a)) -print 'tries to get a ListIterator' +print('tries to get a ListIterator') iterator = a.listIterator() -print 'iterator is', iterator +print('iterator is', iterator) while iterator.hasNext(): - print 'at index', iterator.index, 'value is', iterator.next() + print('at index', iterator.index, 'value is', iterator.next()) -print '3: Do cast to a collection' +print('3: Do cast to a collection') a2 = cast('java/util/Collection', a.j_self) -print a2 +print(a2) -print '4: Try few method on the collection' +print('4: Try few method on the collection') Collections = autoclass('java.util.Collections') #print Collections.enumeration(a) #print Collections.enumeration(a) ret = Collections.max(a) -print "reverse" -print Collections.reverse(a) -print a.data +print("reverse") +print(Collections.reverse(a)) +print(a.data) -print "before swap" -print Collections.swap(a, 2, 3) -print "after swap" -print a.data +print("before swap") +print(Collections.swap(a, 2, 3)) +print("after swap") +print(a.data) -print "rotate" -print Collections.rotate(a, 5) -print a.data +print("rotate") +print(Collections.rotate(a, 5)) +print(a.data) -print 'Order of data before shuffle()', a.data -print Collections.shuffle(a) -print 'Order of data after shuffle()', a.data +print('Order of data before shuffle()', a.data) +print(Collections.shuffle(a)) +print('Order of data after shuffle()', a.data) # XXX We have issues for methosd with multiple signature -print '-> Collections.max(a)' -print Collections.max(a2) +print('-> Collections.max(a)') +print(Collections.max(a2)) #print '-> Collections.shuffle(a)' #print Collections.shuffle(a2) diff --git a/tests/test_reflect.py b/tests/test_reflect.py index fedc95a..6a166a6 100644 --- a/tests/test_reflect.py +++ b/tests/test_reflect.py @@ -1,3 +1,8 @@ +from __future__ import print_function +from __future__ import division +from __future__ import absolute_import +from future import standard_library +standard_library.install_aliases() import unittest from jnius.reflect import autoclass diff --git a/tests/test_simple.py b/tests/test_simple.py index 2601562..6855901 100644 --- a/tests/test_simple.py +++ b/tests/test_simple.py @@ -1,12 +1,17 @@ +from __future__ import print_function +from __future__ import division +from __future__ import absolute_import +from future import standard_library +standard_library.install_aliases() import unittest from jnius import JavaClass, MetaJavaClass, JavaMethod +from future.utils import with_metaclass class HelloWorldTest(unittest.TestCase): def test_helloworld(self): - class HelloWorld(JavaClass): - __metaclass__ = MetaJavaClass + class HelloWorld(with_metaclass(MetaJavaClass, JavaClass)): __javaclass__ = 'org/jnius/HelloWorld' hello = JavaMethod('()Ljava/lang/String;') From fa9f4531c095dc6a04209e74dcc0cd27289eeaea Mon Sep 17 00:00:00 2001 From: Benson Margulies Date: Fri, 1 May 2015 12:16:58 -0400 Subject: [PATCH 50/74] Use dlopen instead of linking -ljvn. --- Makefile | 2 +- jnius/jnius.pyx | 4 +- jnius/jnius_jvm_dlopen.pxi | 87 ++++++++++++++++++++++++++++++++++++++ setup.py | 18 ++++---- 4 files changed, 100 insertions(+), 11 deletions(-) create mode 100644 jnius/jnius_jvm_dlopen.pxi diff --git a/Makefile b/Makefile index 38e21a6..3539e96 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ build_ext: clean: $(ANT) clean - rm -rf build + rm -rf build jnius/config.pxi html: $(MAKE) -C docs html diff --git a/jnius/jnius.pyx b/jnius/jnius.pyx index 678fd5e..41571bf 100644 --- a/jnius/jnius.pyx +++ b/jnius/jnius.pyx @@ -99,8 +99,10 @@ include "config.pxi" IF JNIUS_PLATFORM == "android": include "jnius_jvm_android.pxi" -ELSE: +ELIF JNIUS_PLATFORM == "win32": include "jnius_jvm_desktop.pxi" +ELSE: + include "jnius_jvm_dlopen.pxi" include "jnius_env.pxi" include "jnius_utils.pxi" diff --git a/jnius/jnius_jvm_dlopen.pxi b/jnius/jnius_jvm_dlopen.pxi new file mode 100644 index 0000000..cfb8270 --- /dev/null +++ b/jnius/jnius_jvm_dlopen.pxi @@ -0,0 +1,87 @@ +include "config.pxi" +import os + +cdef extern from *: + ctypedef char const_char "const char" + +cdef extern from 'dlfcn.h' nogil: + void* dlopen(const_char *filename, int flag) + char *dlerror() + void *dlsym(void *handle, const_char *symbol) + int dlclose(void *handle) + + unsigned int RTLD_LAZY + unsigned int RTLD_NOW + unsigned int RTLD_GLOBAL + unsigned int RTLD_LOCAL + unsigned int RTLD_NODELETE + unsigned int RTLD_NOLOAD + unsigned int RTLD_DEEPBIND + + unsigned int RTLD_DEFAULT + long unsigned int RTLD_NEXT + + +cdef extern from "jni.h": + int JNI_VERSION_1_6 + int JNI_OK + jboolean JNI_FALSE + ctypedef struct JavaVMInitArgs: + jint version + jint nOptions + jboolean ignoreUnrecognized + JavaVMOption *options + ctypedef struct JavaVMOption: + char *optionString + void *extraInfo + +cdef JNIEnv *_platform_default_env = NULL + +cdef void create_jnienv() except *: + cdef JavaVM* jvm + cdef JavaVMInitArgs args + cdef JavaVMOption *options + cdef int ret + cdef bytes py_bytes + import jnius_config + + JAVA_HOME = os.environ['JAVA_HOME'] + if JAVA_HOME is None or JAVA_HOME == '': + raise SystemError("JAVA_HOME is not set.") + + lib_path = os.path.join(JAVA_HOME, JNIUS_LIB_SUFFIX) + + cdef void *handle = dlopen(lib_path, RTLD_NOW | RTLD_GLOBAL) + if handle == NULL: + raise SystemError("Error calling dlopen({0}: {1}".format(lib_path, dlerror())) + + cdef void *jniCreateJVM = dlsym(handle, "JNI_CreateJavaVM") + + if jniCreateJVM == NULL: + raise SystemError("Error calling dlfcn for JNI_CreateJavaVM: {0}".format(dlerror())) + + optarr = jnius_config.options + optarr.append("-Djava.class.path=" + jnius_config.expand_classpath()) + + options = malloc(sizeof(JavaVMOption) * len(optarr)) + for i, opt in enumerate(optarr): + options[i].optionString = (opt) + options[i].extraInfo = NULL + + args.version = JNI_VERSION_1_6 + args.options = options + args.nOptions = len(optarr) + args.ignoreUnrecognized = JNI_FALSE + + ret = ( jniCreateJVM)(&jvm, &_platform_default_env, &args) + free(options) + + if ret != JNI_OK: + raise SystemError("JVM failed to start: {0}".format(ret)) + + jnius_config.vm_running = True + +cdef JNIEnv *get_platform_jnienv() except NULL: + if _platform_default_env == NULL: + create_jnienv() + return _platform_default_env diff --git a/setup.py b/setup.py index 2e7e16d..bcb46c6 100644 --- a/setup.py +++ b/setup.py @@ -12,6 +12,7 @@ files = [ 'jnius_export_func.pxi', 'jnius_jvm_android.pxi', 'jnius_jvm_desktop.pxi', + 'jnius_jvm_dlopen.pxi', 'jnius_localref.pxi', 'jnius.pyx', 'jnius_utils.pxi', @@ -38,6 +39,8 @@ except ImportError: if platform != 'android': print('\n\nYou need Cython to compile Pyjnius.\n\n') raise + # On Android we expect to see 'c' files lying about. + # and we go ahead with the 'desktop' file? Odd. files = [fn[:-3] + 'c' for fn in files if fn.endswith('pyx')] if platform == 'android': @@ -53,14 +56,12 @@ elif platform == 'darwin': if not framework: raise Exception('You must install Java on your Mac OS X distro') if '1.6' in framework: - extra_link_args = ['-framework', 'JavaVM'] + lib_location = '../Libraries/libjvm.dylib' include_dirs = [join(framework, 'System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers')] else: - # TODO: This won't make a dylib that moves from one machine to another. - # TODO: it needs a switch to dlopen and a configuration of the path. - java_runtime_lib = '{0}/jre/lib/server'.format(framework) - extra_link_args = ['-L', java_runtime_lib, '-ljvm', '-rpath', java_runtime_lib] + lib_location = 'jre/lib/server/libjvm.dylib' include_dirs = ['{0}/include'.format(framework), '{0}/include/darwin'.format(framework)] + else: import subprocess # otherwise, we need to search the JDK_HOME @@ -92,20 +93,19 @@ else: if platform == 'win32': incl_dir = join(jdk_home, 'include', 'win32') + libraries = ['jvm'] else: incl_dir = join(jdk_home, 'include', 'linux') + lib_location = 'jre/lib/amd64/server/libjvm.so' include_dirs = [ join(jdk_home, 'include'), incl_dir] + if platform == 'win32': library_dirs = [ join(jdk_home, 'lib'), join(jre_home, 'bin', 'server')] - else: - library_dirs = [join(jre_home, 'lib', cpu, 'server')] - extra_link_args = ['-Wl,-rpath', library_dirs[0]] - libraries = ['jvm'] # generate the config.pxi with open(join(dirname(__file__), 'jnius', 'config.pxi'), 'w') as fd: From 953fab9bd8e4f734dcd9aa318bb6036f528707b2 Mon Sep 17 00:00:00 2001 From: Benson Margulies Date: Sat, 2 May 2015 08:16:19 -0400 Subject: [PATCH 51/74] fix up new dlopen code. --- jnius/jnius_jvm_dlopen.pxi | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/jnius/jnius_jvm_dlopen.pxi b/jnius/jnius_jvm_dlopen.pxi index cfb8270..e7f7ddf 100644 --- a/jnius/jnius_jvm_dlopen.pxi +++ b/jnius/jnius_jvm_dlopen.pxi @@ -49,13 +49,13 @@ cdef void create_jnienv() except *: if JAVA_HOME is None or JAVA_HOME == '': raise SystemError("JAVA_HOME is not set.") - lib_path = os.path.join(JAVA_HOME, JNIUS_LIB_SUFFIX) + lib_path = str_for_c(os.path.join(JAVA_HOME, JNIUS_LIB_SUFFIX)) cdef void *handle = dlopen(lib_path, RTLD_NOW | RTLD_GLOBAL) if handle == NULL: raise SystemError("Error calling dlopen({0}: {1}".format(lib_path, dlerror())) - cdef void *jniCreateJVM = dlsym(handle, "JNI_CreateJavaVM") + cdef void *jniCreateJVM = dlsym(handle, b"JNI_CreateJavaVM") if jniCreateJVM == NULL: raise SystemError("Error calling dlfcn for JNI_CreateJavaVM: {0}".format(dlerror())) @@ -65,7 +65,8 @@ cdef void create_jnienv() except *: options = malloc(sizeof(JavaVMOption) * len(optarr)) for i, opt in enumerate(optarr): - options[i].optionString = (opt) + optbytes = str_for_c(opt) + options[i].optionString = (optbytes) options[i].extraInfo = NULL args.version = JNI_VERSION_1_6 From 267ca88fe1cbb3a054fde7240e5872ebc6391b4d Mon Sep 17 00:00:00 2001 From: Benson Margulies Date: Thu, 30 Apr 2015 21:29:55 -0400 Subject: [PATCH 52/74] Use the java from java_home on OSX. --- setup.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index c6beedd..e696d3f 100644 --- a/setup.py +++ b/setup.py @@ -45,12 +45,20 @@ if platform == 'android': library_dirs = ['libs/' + environ['ARCH']] elif platform == 'darwin': import subprocess - framework = subprocess.Popen('xcrun --sdk macosx --show-sdk-path', + framework = subprocess.Popen('/usr/libexec/java_home', shell=True, stdout=subprocess.PIPE).communicate()[0].strip() + print('java_home: {0}\n'.format(framework)); if not framework: raise Exception('You must install Java on your Mac OS X distro') - extra_link_args = ['-framework', 'JavaVM'] - include_dirs = [join(framework, 'System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers')] + if '1.6' in framework: + extra_link_args = ['-framework', 'JavaVM'] + include_dirs = [join(framework, 'System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers')] + else: + # TODO: This won't make a dylib that moves from one machine to another. + # TODO: it needs a switch to dlopen and a configuration of the path. + java_runtime_lib = '{0}/jre/lib/server'.format(framework) + extra_link_args = ['-L', java_runtime_lib, '-ljvm', '-rpath', java_runtime_lib] + include_dirs = ['{0}/include'.format(framework), '{0}/include/darwin'.format(framework)] else: import subprocess # otherwise, we need to search the JDK_HOME From 37d702622edc5a4156cfde06e18cb1b1cc88b85f Mon Sep 17 00:00:00 2001 From: dessant Date: Tue, 19 May 2015 22:01:00 +0300 Subject: [PATCH 53/74] update copyright year --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index f597403..b336a3a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2010-2013 Kivy Team and other contributors +Copyright (c) 2010-2015 Kivy Team and other contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 0f4604d6fed010fd97ce1299945dbe65848032dd Mon Sep 17 00:00:00 2001 From: dessant Date: Wed, 24 Jun 2015 22:41:22 +0300 Subject: [PATCH 54/74] Update .travis.yml --- .travis.yml | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 05d6869..67c518d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,25 @@ language: python + python: - - "2.7" -# command to install dependencies + - 2.7 + - 3.4 + before_install: - sudo apt-get update - - sudo apt-get install python-pip openjdk-7-jdk install: - - pip install --upgrade cython --use-mirrors - - make + - sudo apt-get install python-pip openjdk-7-jdk + - pip install --upgrade cython -# command to run tests script: + - make - make tests + +notifications: + irc: + channels: + - chat.freenode.net#kivy-dev + on_success: change + on_failure: always + use_notice: true + skip_join: true From ae359e054d3c5193ef7965f608b25d1aece9f3b5 Mon Sep 17 00:00:00 2001 From: gabriel pettier Date: Tue, 21 Jul 2015 16:36:42 +0200 Subject: [PATCH 55/74] allow setting java fields of object type using convert_python_to_jobject --- jnius/jnius_export_class.pxi | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/jnius/jnius_export_class.pxi b/jnius/jnius_export_class.pxi index abf075a..10dd85b 100644 --- a/jnius/jnius_export_class.pxi +++ b/jnius/jnius_export_class.pxi @@ -349,6 +349,7 @@ cdef class JavaField(object): cdef jlong j_long cdef jfloat j_float cdef jdouble j_double + cdef jobject j_object cdef JNIEnv *j_env = get_jnienv() # type of the java field @@ -379,6 +380,10 @@ cdef class JavaField(object): elif r == 'D': j_double = value j_env[0].SetDoubleField(j_env, j_self, self.j_field, j_double) + elif r == 'L': + j_object = convert_python_to_jobject(j_env, self.definition, value) + j_env[0].SetObjectField(j_env, j_self, self.j_field, j_object) + j_env[0].DeleteLocalRef(j_env, j_object) else: raise Exception('Invalid field definition') From 2627d952273c50e0a16cc476c1b188a150807ebb Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Sun, 26 Jul 2015 14:29:51 +0200 Subject: [PATCH 56/74] Fixes multiples memory leak in exception analysis, find_javaclass, and class instanciations --- jnius/jnius_export_class.pxi | 2 ++ jnius/jnius_export_func.pxi | 1 + jnius/jnius_utils.pxi | 27 ++++++++++++++++++++------- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/jnius/jnius_export_class.pxi b/jnius/jnius_export_class.pxi index 10dd85b..5122843 100644 --- a/jnius/jnius_export_class.pxi +++ b/jnius/jnius_export_class.pxi @@ -88,6 +88,8 @@ class MetaJavaClass(type): jcs.j_cls = j_env[0].CallStaticObjectMethod( j_env, baseclass, getProxyClass, jargs) + j_env[0].DeleteLocalRef(j_env, baseclass) + if jcs.j_cls == NULL: raise JavaException('Unable to create the class' ' {0}'.format(__javaclass__)) diff --git a/jnius/jnius_export_func.pxi b/jnius/jnius_export_func.pxi index f02f97f..06989ef 100644 --- a/jnius/jnius_export_func.pxi +++ b/jnius/jnius_export_func.pxi @@ -24,5 +24,6 @@ def find_javaclass(bytes name): cls = Class(noinstance=True) cls.instanciate_from(create_local_ref(j_env, jc)) + j_env[0].DeleteLocalRef(j_env, jc) return cls diff --git a/jnius/jnius_utils.pxi b/jnius/jnius_utils.pxi index 31bdc6d..118a51a 100644 --- a/jnius/jnius_utils.pxi +++ b/jnius/jnius_utils.pxi @@ -43,16 +43,21 @@ cdef void check_exception(JNIEnv *j_env) except *: cdef jstring e_msg cdef jboolean isCopy cdef jthrowable exc = j_env[0].ExceptionOccurred(j_env) + cdef jclass cls_object = NULL + cdef jclass cls_throwable = NULL if exc: # ExceptionDescribe always writes to stderr, preventing tidy exception # handling, so should only be for debugging # j_env[0].ExceptionDescribe(j_env) j_env[0].ExceptionClear(j_env) - toString = j_env[0].GetMethodID(j_env, j_env[0].FindClass(j_env, "java/lang/Object"), "toString", "()Ljava/lang/String;"); - getMessage = j_env[0].GetMethodID(j_env, j_env[0].FindClass(j_env, "java/lang/Throwable"), "getMessage", "()Ljava/lang/String;"); - getCause = j_env[0].GetMethodID(j_env, j_env[0].FindClass(j_env, "java/lang/Throwable"), "getCause", "()Ljava/lang/Throwable;"); - getStackTrace = j_env[0].GetMethodID(j_env, j_env[0].FindClass(j_env, "java/lang/Throwable"), "getStackTrace", "()[Ljava/lang/StackTraceElement;"); + cls_object = j_env[0].FindClass(j_env, "java/lang/Object") + cls_throwable = j_env[0].FindClass(j_env, "java/lang/Throwable") + + toString = j_env[0].GetMethodID(j_env, cls_object, "toString", "()Ljava/lang/String;"); + getMessage = j_env[0].GetMethodID(j_env, cls_throwable, "getMessage", "()Ljava/lang/String;"); + getCause = j_env[0].GetMethodID(j_env, cls_throwable, "getCause", "()Ljava/lang/Throwable;"); + getStackTrace = j_env[0].GetMethodID(j_env, cls_throwable, "getStackTrace", "()[Ljava/lang/StackTraceElement;"); e_msg = j_env[0].CallObjectMethod(j_env, exc, getMessage); pymsg = None if e_msg == NULL else convert_jobject_to_python(j_env, 'Ljava/lang/String;', e_msg) @@ -62,6 +67,12 @@ cdef void check_exception(JNIEnv *j_env) except *: pyexcclass = lookup_java_object_name(j_env, exc).replace('/', '.') + j_env[0].DeleteLocalRef(j_env, cls_object) + j_env[0].DeleteLocalRef(j_env, cls_throwable) + if e_msg != NULL: + j_env[0].DeleteLocalRef(j_env, e_msg) + j_env[0].DeleteLocalRef(j_env, exc) + raise JavaException('JVM exception occurred: %s' % (pymsg if pymsg is not None else pyexcclass), pyexcclass, pymsg, pystack) @@ -88,7 +99,8 @@ cdef void _append_exception_trace_messages( if len(pystack) > 0: pystack.append("Caused by:") pystack.append(pystr) - j_env[0].DeleteLocalRef(j_env, msg_obj) + if msg_obj != NULL: + j_env[0].DeleteLocalRef(j_env, msg_obj) # Append stack trace messages if there are any. if frames_length > 0: @@ -98,7 +110,8 @@ cdef void _append_exception_trace_messages( msg_obj = j_env[0].CallObjectMethod(j_env, frame, mid_toString) pystr = None if msg_obj == NULL else convert_jobject_to_python(j_env, 'Ljava/lang/String;', msg_obj) pystack.append(pystr) - j_env[0].DeleteLocalRef(j_env, msg_obj) + if msg_obj != NULL: + j_env[0].DeleteLocalRef(j_env, msg_obj) j_env[0].DeleteLocalRef(j_env, frame) # If 'exc' has a cause then append the stack trace messages from the cause. @@ -107,7 +120,7 @@ cdef void _append_exception_trace_messages( if cause != NULL: _append_exception_trace_messages(j_env, pystack, cause, mid_getCause, mid_getStackTrace, mid_toString) - j_env[0].DeleteLocalRef(j_env, cause) + j_env[0].DeleteLocalRef(j_env, cause) j_env[0].DeleteLocalRef(j_env, frames) From 161ecce63facaed3fd991365c3e1c331bb4de2fe Mon Sep 17 00:00:00 2001 From: dessant Date: Tue, 28 Jul 2015 12:07:36 +0300 Subject: [PATCH 57/74] change travis notification target --- .travis.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 67c518d..71965a2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,10 +16,9 @@ script: - make tests notifications: - irc: - channels: - - chat.freenode.net#kivy-dev - on_success: change + webhooks: + urls: + - http://kivy.org:5000/travisevent + on_success: always on_failure: always - use_notice: true - skip_join: true + on_start: always From edc0f9010a834e812f1a93597fb90ced3e6c1a67 Mon Sep 17 00:00:00 2001 From: dessant Date: Tue, 3 Nov 2015 15:56:50 +0200 Subject: [PATCH 58/74] add travis badge, support, contribute and license sections --- README.md | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e27cc67..ccc55c4 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ A Python module to access Java classes as Python classes using JNI. PyJNIus is a "Work In Progress". +[![Build Status](https://travis-ci.org/kivy/pyjnius.svg?branch=master)](https://travis-ci.org/kivy/pyjnius) + Quick overview -------------- @@ -112,7 +114,41 @@ for x in xrange(20): sleep(.1) ``` -Support/Discussion ------------------- +Support +------- -mailto:pyjnius-dev@googlegroups.com +If you need assistance, you can ask for help on our mailing list: + +* User Group : https://groups.google.com/group/kivy-users +* Email : kivy-users@googlegroups.com + +We also have an IRC channel: + +* Server : irc.freenode.net +* Port : 6667, 6697 (SSL only) +* Channel : #kivy + +Contributing +------------ + +We love pull requests and discussing novel ideas. Check out our +[contribution guide](http://kivy.org/docs/contribute.html) and +feel free to improve PyJNIus. + +The following mailing list and IRC channel are used exclusively for +discussions about developing the Kivy framework and its sister projects: + +* Dev Group : https://groups.google.com/group/kivy-dev +* Email : kivy-dev@googlegroups.com + +IRC channel: + +* Server : irc.freenode.net +* Port : 6667, 6697 (SSL only) +* Channel : #kivy-dev + +License +------- + +PyJNIus is released under the terms of the MIT License. Please refer to the +LICENSE file. From 341255ce3342d7ce302e336887d2764e481a5557 Mon Sep 17 00:00:00 2001 From: Benson Margulies Date: Tue, 3 Nov 2015 08:57:04 -0500 Subject: [PATCH 59/74] setup.py fixes for py3. --- setup.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/setup.py b/setup.py index 582cfe1..029b2b9 100644 --- a/setup.py +++ b/setup.py @@ -5,6 +5,13 @@ from os.path import dirname, join, exists import sys from platform import architecture +def getenv(key): + val = environ.get(key) + if val is not None: + return val.decode('utf-8') + else: + return val + files = [ 'jni.pxi', 'jnius_conversion.pxi', @@ -26,8 +33,8 @@ install_requires = [] # detect Python for android platform = sys.platform -ndkplatform = environ.get('NDKPLATFORM') -if ndkplatform is not None and environ.get('LIBLINK'): +ndkplatform = getenv('NDKPLATFORM') +if ndkplatform is not None and getenv('LIBLINK'): platform = 'android' # detect cython @@ -46,7 +53,7 @@ except ImportError: if platform == 'android': # for android, we use SDL... libraries = ['sdl', 'log'] - library_dirs = ['libs/' + environ['ARCH']] + library_dirs = ['libs/' + getenv('ARCH')] elif platform == 'darwin': import subprocess framework = subprocess.Popen('/usr/libexec/java_home', @@ -63,10 +70,10 @@ elif platform == 'darwin': else: import subprocess # otherwise, we need to search the JDK_HOME - jdk_home = environ.get('JDK_HOME') + jdk_home = getenv('JDK_HOME') if not jdk_home: if platform == 'win32': - env_var = environ.get('JAVA_HOME') + env_var = getenv('JAVA_HOME') if env_var and 'jdk' in env_var: jdk_home = env_var @@ -76,10 +83,11 @@ else: else: jdk_home = subprocess.Popen('readlink -f `which javac` | sed "s:bin/javac::"', shell=True, stdout=subprocess.PIPE).communicate()[0].strip() + if jdk_home is not None: + jdk_home = jdk_home.decode('utf-8') if not jdk_home or not exists(jdk_home): raise Exception('Unable to determine JDK_HOME') - jre_home = environ.get('JRE_HOME') if exists(join(jdk_home, 'jre')): jre_home = join(jdk_home, 'jre') if not jre_home: From f1dc7ea2c6e8d12f28c2e54e47df952d2aa63fda Mon Sep 17 00:00:00 2001 From: Benson Margulies Date: Tue, 3 Nov 2015 10:10:55 -0500 Subject: [PATCH 60/74] We need 'future' --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 43787f8..410af42 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,8 +9,8 @@ before_install: - sudo apt-get install python-pip openjdk-7-jdk install: - - sudo apt-get install python-pip openjdk-7-jdk - pip install --upgrade cython + - pip install --upgrade future script: - make From 2f50f0c86669b1a8a8bc2ea853b5f7487bfc134d Mon Sep 17 00:00:00 2001 From: Benson Margulies Date: Tue, 3 Nov 2015 10:31:06 -0500 Subject: [PATCH 61/74] More work to get travis and python3 working. --- Makefile | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 5db22d7..e07be69 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ all: build_ext ifdef PYTHON3 PYTHON=python3 -NOSETESTS=nosetests-3 +NOSETESTS=nosetests-3.4 else PYTHON=python NOSETESTS=nosetests-2.7 @@ -26,6 +26,9 @@ clean: html: $(MAKE) -C docs html -tests: - (cd tests; env CLASSPATH=../build/test-classes:../build/classes PYTHONPATH=..:$(PYTHONPATH) $(NOSETESTS) -v) +tests: test2 test3 +tests2: + (cd tests; env CLASSPATH=../build/test-classes:../build/classes PYTHONPATH=..:$(PYTHONPATH) $(NOSETESTS) -v) +tests3: + make tests2 PYTHON3=1 From 1d42cfe3676759ba6fb8df1f7d6c27890f1dde59 Mon Sep 17 00:00:00 2001 From: Benson Margulies Date: Tue, 3 Nov 2015 10:31:46 -0500 Subject: [PATCH 62/74] ignorance. --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index f671175..4673bc0 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ build .*.swp *.class .idea -*~ \ No newline at end of file +*~ +*.so From a3dbc73c20b72e5c06eb78a3a8cdedf81ad2aa4d Mon Sep 17 00:00:00 2001 From: Benson Margulies Date: Tue, 3 Nov 2015 10:37:30 -0500 Subject: [PATCH 63/74] Try something simpler for travis. --- Makefile | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index e07be69..7270012 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ PYTHON=python3 NOSETESTS=nosetests-3.4 else PYTHON=python -NOSETESTS=nosetests-2.7 +NOSETESTS=nosetests endif JAVAC_OPTS=-target 1.6 -source 1.6 @@ -26,9 +26,7 @@ clean: html: $(MAKE) -C docs html -tests: test2 test3 - -tests2: +# for use in travis; tests whatever you got. +# use PYTHON3=1 to force python3 in other environments. +tests: (cd tests; env CLASSPATH=../build/test-classes:../build/classes PYTHONPATH=..:$(PYTHONPATH) $(NOSETESTS) -v) -tests3: - make tests2 PYTHON3=1 From 9e16d75cb00a08ef95d7b00655421f1e55307042 Mon Sep 17 00:00:00 2001 From: dessant Date: Tue, 3 Nov 2015 21:22:33 +0200 Subject: [PATCH 64/74] list python 3 in classifiers and update the license --- setup.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 029b2b9..1763653 100644 --- a/setup.py +++ b/setup.py @@ -136,7 +136,7 @@ setup(name='jnius', url='http://pyjnius.readthedocs.org/', author='Mathieu Virbel and Gabriel Pettier', author_email='mat@kivy.org,gabriel@kivy.org', - license='LGPL', + license='MIT', description='Python library to access Java classes', install_requires=install_requires, ext_package='jnius', @@ -153,9 +153,12 @@ setup(name='jnius', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Natural Language :: English', - 'Operating System :: MacOS :: MacOS X', + 'Operating System :: MacOS :: OS X', 'Operating System :: Microsoft :: Windows', 'Operating System :: POSIX :: Linux', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', 'Topic :: Software Development :: Libraries :: Application Frameworks']) From 948fb13bd751097c9dccec4ba97d175789888006 Mon Sep 17 00:00:00 2001 From: dessant Date: Tue, 3 Nov 2015 21:42:06 +0200 Subject: [PATCH 65/74] upgrade from py3.4 to py3.5 for travis builds --- .travis.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 410af42..3cffc0f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,15 +2,14 @@ language: python python: - 2.7 - - 3.4 + - 3.5 before_install: - sudo apt-get update - sudo apt-get install python-pip openjdk-7-jdk install: - - pip install --upgrade cython - - pip install --upgrade future + - pip install --upgrade cython future script: - make From fe362dc8785d5a7959836d68a9076c8a8a4bcddc Mon Sep 17 00:00:00 2001 From: dessant Date: Wed, 4 Nov 2015 01:34:56 +0200 Subject: [PATCH 66/74] fix NameError: name 'lib_location' is not defined --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 1763653..740510f 100644 --- a/setup.py +++ b/setup.py @@ -27,6 +27,7 @@ files = [ libraries = [] library_dirs = [] +lib_location = None extra_link_args = [] include_dirs = [] install_requires = [] From a775a9b04873b2287fc4292f169fec8de1135971 Mon Sep 17 00:00:00 2001 From: Benson Margulies Date: Tue, 3 Nov 2015 21:26:14 -0500 Subject: [PATCH 67/74] ignorance. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 4673bc0..f48aca7 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ build .idea *~ *.so +*.iml From 209e57ed0d866e69995dbf9f03af92345e5386e8 Mon Sep 17 00:00:00 2001 From: Benson Margulies Date: Wed, 4 Nov 2015 07:33:15 -0500 Subject: [PATCH 68/74] eliminate use of 'future' outside of tests, try to fix p2/p3 issues with setup. --- jnius/reflect.py | 4 +--- setup.py | 17 ++++++++++++----- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/jnius/reflect.py b/jnius/reflect.py index db820b4..202ad69 100644 --- a/jnius/reflect.py +++ b/jnius/reflect.py @@ -2,10 +2,8 @@ from __future__ import absolute_import from __future__ import unicode_literals from __future__ import print_function from __future__ import division -from future import standard_library -standard_library.install_aliases() -from future.utils import with_metaclass __all__ = ('autoclass', 'ensureclass') +from six import with_metaclass from .jnius import ( JavaClass, MetaJavaClass, JavaMethod, JavaStaticMethod, diff --git a/setup.py b/setup.py index 740510f..1f1bdec 100644 --- a/setup.py +++ b/setup.py @@ -4,11 +4,15 @@ from os import environ from os.path import dirname, join, exists import sys from platform import architecture +import six def getenv(key): val = environ.get(key) if val is not None: - return val.decode('utf-8') + if six.PY3: + return val.decode() + else: + return val else: return val @@ -30,7 +34,7 @@ library_dirs = [] lib_location = None extra_link_args = [] include_dirs = [] -install_requires = [] +install_requires = ['six'] # detect Python for android platform = sys.platform @@ -58,7 +62,10 @@ if platform == 'android': elif platform == 'darwin': import subprocess framework = subprocess.Popen('/usr/libexec/java_home', - shell=True, stdout=subprocess.PIPE).communicate()[0].decode().strip() + shell=True, stdout=subprocess.PIPE).communicate()[0] + if six.PY3: + framework = framework.decode(); + framework = framework.strip() print('java_home: {0}\n'.format(framework)); if not framework: raise Exception('You must install Java on your Mac OS X distro') @@ -84,8 +91,8 @@ else: else: jdk_home = subprocess.Popen('readlink -f `which javac` | sed "s:bin/javac::"', shell=True, stdout=subprocess.PIPE).communicate()[0].strip() - if jdk_home is not None: - jdk_home = jdk_home.decode('utf-8') + if jdk_home is not None and six.PY3: + jdk_home = jdk_home.decode() if not jdk_home or not exists(jdk_home): raise Exception('Unable to determine JDK_HOME') From e9a1ed389f98f4a8cd74ec92da5210988e77e748 Mon Sep 17 00:00:00 2001 From: Benson Margulies Date: Wed, 4 Nov 2015 08:15:45 -0500 Subject: [PATCH 69/74] add six to the travis build. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3cffc0f..b4c798c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ before_install: - sudo apt-get install python-pip openjdk-7-jdk install: - - pip install --upgrade cython future + - pip install --upgrade cython future six script: - make From b5db4c51f3c26c46b5f2f82203c7d675949628a7 Mon Sep 17 00:00:00 2001 From: Benson Margulies Date: Wed, 4 Nov 2015 17:01:48 -0500 Subject: [PATCH 70/74] Fix another decode, remove use of 'six' from setup.py, use setuptools when available. --- setup.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/setup.py b/setup.py index 1f1bdec..3f277c3 100644 --- a/setup.py +++ b/setup.py @@ -1,15 +1,19 @@ from __future__ import print_function -from distutils.core import setup, Extension +try: + from setuptools import setup, Extension +except ImportError: + from distutils.core import setup, Extension from os import environ from os.path import dirname, join, exists import sys from platform import architecture -import six + +PY3 = sys.version_info >= (3,0,0) def getenv(key): val = environ.get(key) if val is not None: - if six.PY3: + if PY3: return val.decode() else: return val @@ -47,7 +51,10 @@ try: from Cython.Distutils import build_ext install_requires.append('cython') except ImportError: - from distutils.command.build_ext import build_ext + try: + from setuptools.command.build_ext import build_ext + except ImportError: + from distutils.command.build_ext import build_ext if platform != 'android': print('\n\nYou need Cython to compile Pyjnius.\n\n') raise @@ -63,7 +70,7 @@ elif platform == 'darwin': import subprocess framework = subprocess.Popen('/usr/libexec/java_home', shell=True, stdout=subprocess.PIPE).communicate()[0] - if six.PY3: + if PY3: framework = framework.decode(); framework = framework.strip() print('java_home: {0}\n'.format(framework)); @@ -91,7 +98,7 @@ else: else: jdk_home = subprocess.Popen('readlink -f `which javac` | sed "s:bin/javac::"', shell=True, stdout=subprocess.PIPE).communicate()[0].strip() - if jdk_home is not None and six.PY3: + if jdk_home is not None and PY3: jdk_home = jdk_home.decode() if not jdk_home or not exists(jdk_home): raise Exception('Unable to determine JDK_HOME') @@ -124,7 +131,7 @@ else: # generate the config.pxi with open(join(dirname(__file__), 'jnius', 'config.pxi'), 'w') as fd: fd.write('DEF JNIUS_PLATFORM = {0!r}\n\n'.format(platform)) - if sys.version_info>=(3,0,0): + if PY3: fd.write('DEF JNIUS_PYTHON3 = True\n\n') else: fd.write('DEF JNIUS_PYTHON3 = False\n\n') From 8b17b7483230b85ae21d87f92fb81fe3c71b979f Mon Sep 17 00:00:00 2001 From: Benson Margulies Date: Wed, 4 Nov 2015 17:08:51 -0500 Subject: [PATCH 71/74] Add an apk test target to the Makefile. --- Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Makefile b/Makefile index 7270012..942d33c 100644 --- a/Makefile +++ b/Makefile @@ -30,3 +30,9 @@ html: # use PYTHON3=1 to force python3 in other environments. tests: (cd tests; env CLASSPATH=../build/test-classes:../build/classes PYTHONPATH=..:$(PYTHONPATH) $(NOSETESTS) -v) + +APK_PRIVATE_PATH=/home/noone/devel/python-for-android/tests/testapp + +# this is an example of how to build an APK. +apk: + python-for-android apk --requirements=kivy,sdl2 --private $(APK_PRIVATE_PATH) --package=net.inclem.sdl2 --name=sdl2 --version=0.6 --debug From da68d701f12300028e1b2027d1a9189d29817e02 Mon Sep 17 00:00:00 2001 From: Benson Margulies Date: Wed, 4 Nov 2015 17:30:24 -0500 Subject: [PATCH 72/74] Removr all use of 'future'. --- .travis.yml | 2 +- tests/test_assignable.py | 2 -- tests/test_bad_declaration.py | 2 -- tests/test_basics.py | 2 -- tests/test_bytearray.py | 2 -- tests/test_cast.py | 2 -- tests/test_class_argument.py | 2 -- tests/test_enum.py | 2 -- tests/test_implementation.py | 2 -- tests/test_interface.py | 2 -- tests/test_jnitable_overflow.py | 2 -- tests/test_method_multiple_signatures.py | 2 -- tests/test_multidimension.py | 2 -- tests/test_output_args.py | 2 -- tests/test_proxy.py | 6 ++---- tests/test_reflect.py | 2 -- tests/test_simple.py | 4 +--- 17 files changed, 4 insertions(+), 36 deletions(-) diff --git a/.travis.yml b/.travis.yml index b4c798c..bcfec1e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ before_install: - sudo apt-get install python-pip openjdk-7-jdk install: - - pip install --upgrade cython future six + - pip install --upgrade cython six script: - make diff --git a/tests/test_assignable.py b/tests/test_assignable.py index 2922db8..9a2783a 100644 --- a/tests/test_assignable.py +++ b/tests/test_assignable.py @@ -1,8 +1,6 @@ from __future__ import print_function from __future__ import division from __future__ import absolute_import -from future import standard_library -standard_library.install_aliases() import unittest from jnius import autoclass, JavaException diff --git a/tests/test_bad_declaration.py b/tests/test_bad_declaration.py index 90ffef6..93b6b33 100644 --- a/tests/test_bad_declaration.py +++ b/tests/test_bad_declaration.py @@ -1,8 +1,6 @@ from __future__ import print_function from __future__ import division from __future__ import absolute_import -from future import standard_library -standard_library.install_aliases() import unittest from jnius import JavaException, JavaClass from jnius.reflect import autoclass diff --git a/tests/test_basics.py b/tests/test_basics.py index 18ccc79..d6ebd5c 100644 --- a/tests/test_basics.py +++ b/tests/test_basics.py @@ -1,8 +1,6 @@ from __future__ import print_function from __future__ import division from __future__ import absolute_import -from future import standard_library -standard_library.install_aliases() import unittest from jnius.reflect import autoclass diff --git a/tests/test_bytearray.py b/tests/test_bytearray.py index 9fbca69..50fcd0b 100644 --- a/tests/test_bytearray.py +++ b/tests/test_bytearray.py @@ -1,8 +1,6 @@ from __future__ import print_function from __future__ import division from __future__ import absolute_import -from future import standard_library -standard_library.install_aliases() import unittest from jnius import autoclass diff --git a/tests/test_cast.py b/tests/test_cast.py index 6dcf12f..6562cd9 100644 --- a/tests/test_cast.py +++ b/tests/test_cast.py @@ -1,8 +1,6 @@ from __future__ import print_function from __future__ import division from __future__ import absolute_import -from future import standard_library -standard_library.install_aliases() import unittest from jnius.reflect import autoclass from jnius import cast diff --git a/tests/test_class_argument.py b/tests/test_class_argument.py index 0264383..31f4b40 100644 --- a/tests/test_class_argument.py +++ b/tests/test_class_argument.py @@ -1,8 +1,6 @@ from __future__ import print_function from __future__ import division from __future__ import absolute_import -from future import standard_library -standard_library.install_aliases() import unittest from jnius.reflect import autoclass diff --git a/tests/test_enum.py b/tests/test_enum.py index 1e6edee..d590639 100644 --- a/tests/test_enum.py +++ b/tests/test_enum.py @@ -1,8 +1,6 @@ from __future__ import print_function from __future__ import division from __future__ import absolute_import -from future import standard_library -standard_library.install_aliases() import unittest from jnius.reflect import autoclass diff --git a/tests/test_implementation.py b/tests/test_implementation.py index c6f3d31..81b66be 100644 --- a/tests/test_implementation.py +++ b/tests/test_implementation.py @@ -2,8 +2,6 @@ from __future__ import print_function from __future__ import division from __future__ import absolute_import -from future import standard_library -standard_library.install_aliases() import unittest from jnius.reflect import autoclass diff --git a/tests/test_interface.py b/tests/test_interface.py index 42b2768..1d8282a 100644 --- a/tests/test_interface.py +++ b/tests/test_interface.py @@ -1,8 +1,6 @@ from __future__ import print_function from __future__ import division from __future__ import absolute_import -from future import standard_library -standard_library.install_aliases() import unittest from jnius import autoclass, JavaException diff --git a/tests/test_jnitable_overflow.py b/tests/test_jnitable_overflow.py index cef4b85..0a98bc7 100644 --- a/tests/test_jnitable_overflow.py +++ b/tests/test_jnitable_overflow.py @@ -1,8 +1,6 @@ from __future__ import print_function from __future__ import division from __future__ import absolute_import -from future import standard_library -standard_library.install_aliases() # run it, and check with Java VisualVM if we are eating too much memory or not! if __name__ == '__main__': from jnius import autoclass diff --git a/tests/test_method_multiple_signatures.py b/tests/test_method_multiple_signatures.py index 3f892fd..3e4266f 100644 --- a/tests/test_method_multiple_signatures.py +++ b/tests/test_method_multiple_signatures.py @@ -1,8 +1,6 @@ from __future__ import print_function from __future__ import division from __future__ import absolute_import -from future import standard_library -standard_library.install_aliases() import unittest from jnius.reflect import autoclass diff --git a/tests/test_multidimension.py b/tests/test_multidimension.py index 6ab0fd7..0c4b7de 100644 --- a/tests/test_multidimension.py +++ b/tests/test_multidimension.py @@ -1,8 +1,6 @@ from __future__ import print_function from __future__ import division from __future__ import absolute_import -from future import standard_library -standard_library.install_aliases() import unittest from jnius.reflect import autoclass diff --git a/tests/test_output_args.py b/tests/test_output_args.py index e6a0fff..2547357 100644 --- a/tests/test_output_args.py +++ b/tests/test_output_args.py @@ -1,8 +1,6 @@ from __future__ import print_function from __future__ import division from __future__ import absolute_import -from future import standard_library -standard_library.install_aliases() import unittest from jnius import autoclass diff --git a/tests/test_proxy.py b/tests/test_proxy.py index e8b4337..1ed01b9 100644 --- a/tests/test_proxy.py +++ b/tests/test_proxy.py @@ -1,10 +1,8 @@ from __future__ import print_function from __future__ import division from __future__ import absolute_import -from builtins import super -from future import standard_library -standard_library.install_aliases() -from builtins import range +from six.moves import range + from jnius import autoclass, java_method, PythonJavaClass, cast from nose.tools import * diff --git a/tests/test_reflect.py b/tests/test_reflect.py index 6a166a6..fc6e8c9 100644 --- a/tests/test_reflect.py +++ b/tests/test_reflect.py @@ -1,8 +1,6 @@ from __future__ import print_function from __future__ import division from __future__ import absolute_import -from future import standard_library -standard_library.install_aliases() import unittest from jnius.reflect import autoclass diff --git a/tests/test_simple.py b/tests/test_simple.py index 6855901..edfcd99 100644 --- a/tests/test_simple.py +++ b/tests/test_simple.py @@ -1,11 +1,9 @@ from __future__ import print_function from __future__ import division from __future__ import absolute_import -from future import standard_library -standard_library.install_aliases() import unittest from jnius import JavaClass, MetaJavaClass, JavaMethod -from future.utils import with_metaclass +from six import with_metaclass class HelloWorldTest(unittest.TestCase): From 962add95c870cb975baeac139ea6cb202a9255a7 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Wed, 4 Nov 2015 22:45:46 +0000 Subject: [PATCH 73/74] Removed apk instructions --- Makefile | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Makefile b/Makefile index 942d33c..7270012 100644 --- a/Makefile +++ b/Makefile @@ -30,9 +30,3 @@ html: # use PYTHON3=1 to force python3 in other environments. tests: (cd tests; env CLASSPATH=../build/test-classes:../build/classes PYTHONPATH=..:$(PYTHONPATH) $(NOSETESTS) -v) - -APK_PRIVATE_PATH=/home/noone/devel/python-for-android/tests/testapp - -# this is an example of how to build an APK. -apk: - python-for-android apk --requirements=kivy,sdl2 --private $(APK_PRIVATE_PATH) --package=net.inclem.sdl2 --name=sdl2 --version=0.6 --debug From c2cb331f419618681abc2d235f578603548bd781 Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 9 Nov 2015 14:08:57 +0200 Subject: [PATCH 74/74] compilation error fixed --- jnius/jnius_utils.pxi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jnius/jnius_utils.pxi b/jnius/jnius_utils.pxi index 27663cd..b1b76da 100644 --- a/jnius/jnius_utils.pxi +++ b/jnius/jnius_utils.pxi @@ -185,7 +185,8 @@ cdef void check_assignable_from(JNIEnv *env, JavaClass jc, signature) except *: # we got an object that doesn't match with the signature # check if we can use it. - cls = env[0].FindClass(env, str_for_c(signature)) + s = str_for_c(signature) + cls = env[0].FindClass(env, s) if cls == NULL: raise JavaException('Unable to found the class for {0!r}'.format( signature))