diff --git a/.gitignore b/.gitignore
index d4f8b5a..f48aca7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,6 @@ build
.*.swp
*.class
.idea
+*~
+*.so
+*.iml
diff --git a/.travis.yml b/.travis.yml
index 05d6869..bcfec1e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,15 +1,24 @@
language: python
+
python:
- - "2.7"
-# command to install dependencies
+ - 2.7
+ - 3.5
+
before_install:
- sudo apt-get update
- sudo apt-get install python-pip openjdk-7-jdk
install:
- - pip install --upgrade cython --use-mirrors
- - make
+ - pip install --upgrade cython six
-# command to run tests
script:
+ - make
- make tests
+
+notifications:
+ webhooks:
+ urls:
+ - http://kivy.org:5000/travisevent
+ on_success: always
+ on_failure: always
+ on_start: always
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.
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
diff --git a/Makefile b/Makefile
index 5f452dc..7270012 100644
--- a/Makefile
+++ b/Makefile
@@ -1,17 +1,32 @@
+all: build_ext
+
.PHONY: build_ext tests
+ifdef PYTHON3
+PYTHON=python3
+NOSETESTS=nosetests-3.4
+else
+PYTHON=python
+NOSETESTS=nosetests
+endif
+
+JAVAC_OPTS=-target 1.6 -source 1.6
+JAVAC=javac $(JAVAC_OPTS)
+
+ANT=ant
+
build_ext:
- javac jnius/src/org/jnius/NativeInvocationHandler.java
- python setup.py build_ext --inplace -f -g
+ $(ANT) all
+ $(PYTHON) setup.py build_ext --inplace -f -g
+
+clean:
+ $(ANT) clean
+ rm -rf build jnius/config.pxi
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 && env PYTHONPATH=..:$(PYTHONPATH) nosetests-2.7 -v
+# 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)
diff --git a/README.md b/README.md
index 7a7acdf..ccc55c4 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,11 @@
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".
+
+[![Build Status](https://travis-ci.org/kivy/pyjnius.svg?branch=master)](https://travis-ci.org/kivy/pyjnius)
Quick overview
--------------
@@ -26,22 +28,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 +89,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
@@ -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.
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/docs/source/android.rst b/docs/source/android.rst
index 2c069d1..8da6f1b 100644
--- a/docs/source/android.rst
+++ b/docs/source/android.rst
@@ -3,37 +3,40 @@
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')
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
-----------------------
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')
@@ -42,9 +45,9 @@ Let's do in 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
@@ -83,16 +86,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 +112,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 +124,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`.
@@ -147,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) {
diff --git a/docs/source/api.rst b/docs/source/api.rst
index 959be7c..9f09a54 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,14 +302,18 @@ 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.
-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
@@ -323,3 +327,59 @@ 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.
+
+
+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):
+ ...
diff --git a/jnius/__init__.py b/jnius/__init__.py
index 06435a0..34c2599 100644
--- a/jnius/__init__.py
+++ b/jnius/__init__.py
@@ -11,3 +11,28 @@ __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
+
+HASHCODE_MAX = 2 ** 31 - 1
+
+class PythonJavaClass_(PythonJavaClass):
+
+ @java_method('()I', name='hashCode')
+ def hashCode(self):
+ return id(self) % HASHCODE_MAX
+
+ @java_method('()Ljava/lang/String;', name='hashCode')
+ def hashCode_(self):
+ return '{}'.format(self.hashCode())
+
+ @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/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..41571bf 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
@@ -99,14 +99,19 @@ 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"
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 35f3807..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,7 +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/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)
@@ -19,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
@@ -27,6 +33,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':
@@ -48,10 +55,10 @@ 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/Object;'):
- j_args[index].l = j_env[0].NewStringUTF(
- j_env, py_arg.encode('utf-8'))
+ 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):
jc = py_arg
check_assignable_from(j_env, jc, argtype[1:-1])
@@ -82,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
@@ -112,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)
@@ -122,11 +132,14 @@ 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)
- 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.
@@ -171,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)
@@ -274,6 +287,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')
@@ -295,9 +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/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;',
@@ -337,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):
@@ -399,6 +438,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
@@ -418,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
@@ -445,7 +495,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)
@@ -492,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]))
@@ -504,12 +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/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])
@@ -526,6 +585,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_env.pxi b/jnius/jnius_env.pxi
index 40da529..336ab0e 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
@@ -17,3 +19,7 @@ cdef JNIEnv *get_jnienv():
jvm[0].AttachCurrentThread(jvm, &env, NULL)
return env
+
+def detach():
+ jvm[0].DetachCurrentThread(jvm)
+
diff --git a/jnius/jnius_export_class.pxi b/jnius/jnius_export_class.pxi
index 0e321d9..00242de 100644
--- a/jnius/jnius_export_class.pxi
+++ b/jnius/jnius_export_class.pxi
@@ -1,8 +1,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):
@@ -34,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
@@ -73,19 +80,22 @@ 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
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__))
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__))
@@ -100,29 +110,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):
@@ -209,8 +219,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__))
@@ -238,23 +249,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
@@ -275,10 +286,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
@@ -290,7 +301,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
@@ -301,13 +312,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))
@@ -321,6 +335,66 @@ 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 jobject j_object
+ 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)
+ 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')
+
+ check_exception(j_env)
+
cdef read_field(self, jobject j_self):
cdef jboolean j_boolean
cdef jbyte j_byte
@@ -474,9 +548,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
@@ -489,7 +563,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)
@@ -502,20 +576,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
@@ -807,7 +883,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..8633adb 100644
--- a/jnius/jnius_export_func.pxi
+++ b/jnius/jnius_export_func.pxi
@@ -1,28 +1,31 @@
+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))
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_jvm_desktop.pxi b/jnius/jnius_jvm_desktop.pxi
index 114647a..e3834d9 100644
--- a/jnius/jnius_jvm_desktop.pxi
+++ b/jnius/jnius_jvm_desktop.pxi
@@ -1,9 +1,12 @@
+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
-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
+ int JNI_OK
jboolean JNI_FALSE
ctypedef struct JavaVMInitArgs:
jint version
@@ -16,52 +19,38 @@ 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):
+ if PY_MAJOR_VERSION >= 3:
+ opt = opt.encode('utf-8')
+ 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/jnius_jvm_dlopen.pxi b/jnius/jnius_jvm_dlopen.pxi
new file mode 100644
index 0000000..9fce931
--- /dev/null
+++ b/jnius/jnius_jvm_dlopen.pxi
@@ -0,0 +1,90 @@
+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.")
+ IF JNIUS_PYTHON3:
+ lib_path = str_for_c(os.path.join(JAVA_HOME, JNIUS_LIB_SUFFIX.decode("utf-8")))
+ ELSE:
+ 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, b"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):
+ optbytes = str_for_c(opt)
+ options[i].optionString = (optbytes)
+ 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/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_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 4318a96..b1b76da 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] != '(':
@@ -10,10 +27,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]
@@ -27,26 +44,137 @@ 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)
cdef void check_exception(JNIEnv *j_env) except *:
+ cdef jmethodID toString = NULL
+ 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)
+ cdef jclass cls_object = NULL
+ cdef jclass cls_throwable = NULL
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)
- raise JavaException('JVM exception occured')
+
+ 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)
+
+ pystack = []
+ _append_exception_trace_messages(j_env, pystack, exc, getCause, getStackTrace, toString)
+
+ 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)
+
+
+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)
+ if msg_obj != NULL:
+ 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)
+ 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.
+ 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 = {}
-cdef void check_assignable_from(JNIEnv *env, JavaClass jc, bytes signature) except *:
- cdef jclass cls
+cdef int assignable_from_order = 0
+cdef void check_assignable_from(JNIEnv *env, JavaClass jc, signature) except *:
+ 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':
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
@@ -57,13 +185,22 @@ 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)
+ 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))
- result = bool(env[0].IsAssignableFrom(env, jc.j_cls, cls))
- 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:
@@ -71,12 +208,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)
@@ -86,7 +223,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:
@@ -157,7 +293,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
@@ -172,6 +312,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):
@@ -204,7 +349,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 b3b1ea7..202ad69 100644
--- a/jnius/reflect.py
+++ b/jnius/reflect.py
@@ -1,16 +1,20 @@
+from __future__ import absolute_import
+from __future__ import unicode_literals
+from __future__ import print_function
+from __future__ import division
__all__ = ('autoclass', 'ensureclass')
+from six import with_metaclass
-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;')
+ 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 +36,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,25 +44,23 @@ 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;')
-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 +77,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 +88,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 +97,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;')
@@ -137,6 +136,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 +180,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 +214,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())
diff --git a/jnius/signatures.py b/jnius/signatures.py
new file mode 100644
index 0000000..9bc1830
--- /dev/null
+++ b/jnius/signatures.py
@@ -0,0 +1,99 @@
+'''
+signatures.py
+=============
+
+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'
+
+from . import JavaClass
+from . import java_method
+
+
+''' Type specifiers for primitives '''
+
+class _JavaSignaturePrimitive(object):
+ _spec = ""
+
+def _MakeSignaturePrimitive(name, spec):
+ class __Primitive(_JavaSignaturePrimitive):
+ ''' PyJnius signature for Java %s type ''' % name
+ _name = name
+ _spec = spec
+ __Primitive.__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):
+ ''' Signature helper for identifying arrays of a given object or
+ primitive type. '''
+
+ spec = "[" + _jni_type_spec(of_type)
+ return _MakeSignaturePrimitive("array", spec)
+
+def with_signature(returns, takes):
+ ''' Alternative version of @java_method that takes JavaClass
+ objects to produce the method signature. '''
+
+ sig = 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. '''
+
+ 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, _JavaSignaturePrimitive):
+ return jclass._spec
+
\ No newline at end of file
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 1715e82..3f277c3 100644
--- a/setup.py
+++ b/setup.py
@@ -1,7 +1,24 @@
-from distutils.core import setup, Extension
+from __future__ import print_function
+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
+
+PY3 = sys.version_info >= (3,0,0)
+
+def getenv(key):
+ val = environ.get(key)
+ if val is not None:
+ if PY3:
+ return val.decode()
+ else:
+ return val
+ else:
+ return val
files = [
'jni.pxi',
@@ -10,6 +27,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',
@@ -17,14 +35,15 @@ files = [
libraries = []
library_dirs = []
+lib_location = None
extra_link_args = []
include_dirs = []
-install_requires = []
+install_requires = ['six']
# 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
@@ -32,34 +51,58 @@ 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'
+ 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':
# for android, we use SDL...
libraries = ['sdl', 'log']
- library_dirs = ['libs/' + environ['ARCH']]
+ library_dirs = ['libs/' + getenv('ARCH')]
elif platform == 'darwin':
- import objc
- framework = objc.pathForFramework('JavaVM.framework')
+ import subprocess
+ framework = subprocess.Popen('/usr/libexec/java_home',
+ shell=True, stdout=subprocess.PIPE).communicate()[0]
+ if 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')
- extra_link_args = ['-framework', 'JavaVM']
- include_dirs = [join(framework, 'Versions/A/Headers')]
+ if '1.6' in framework:
+ lib_location = '../Libraries/libjvm.dylib'
+ include_dirs = [join(framework, 'System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers')]
+ else:
+ 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
- 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()
+ jdk_home = getenv('JDK_HOME')
if not jdk_home:
+ if platform == 'win32':
+ env_var = getenv('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 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')
- jre_home = environ.get('JRE_HOME')
if exists(join(jdk_home, 'jre')):
jre_home = join(jdk_home, 'jre')
if not jre_home:
@@ -67,17 +110,33 @@ 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')
+ libraries = ['jvm']
+ else:
+ incl_dir = join(jdk_home, 'include', 'linux')
+ lib_location = 'jre/lib/amd64/server/libjvm.so'
+
include_dirs = [
join(jdk_home, 'include'),
- join(jdk_home, 'include', 'linux')]
- library_dirs = [join(jre_home, 'lib', cpu, 'server')]
- extra_link_args = ['-Wl,-rpath', library_dirs[0]]
- libraries = ['jvm']
+ incl_dir]
+
+ if platform == 'win32':
+ library_dirs = [
+ join(jdk_home, 'lib'),
+ join(jre_home, 'bin', 'server')]
# 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 PY3:
+ 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__')]
@@ -88,10 +147,11 @@ 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',
- license='LGPL',
+ license='MIT',
description='Python library to access Java classes',
install_requires=install_requires,
ext_package='jnius',
@@ -106,12 +166,14 @@ 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 :: 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'])
diff --git a/tests/org/jnius/BasicsTest.java b/tests/java-src/org/jnius/BasicsTest.java
similarity index 71%
rename from tests/org/jnius/BasicsTest.java
rename to tests/java-src/org/jnius/BasicsTest.java
index c257822..9fa4abf 100644
--- a/tests/org/jnius/BasicsTest.java
+++ b/tests/java-src/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;
@@ -43,6 +54,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 +121,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) {
@@ -141,4 +166,44 @@ 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;
+ }
+
+ 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/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/java-src/org/jnius/MultipleDimensions.java b/tests/java-src/org/jnius/MultipleDimensions.java
new file mode 100644
index 0000000..9deeec5
--- /dev/null
+++ b/tests/java-src/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/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
diff --git a/tests/test_assignable.py b/tests/test_assignable.py
index 9306250..9a2783a 100644
--- a/tests/test_assignable.py
+++ b/tests/test_assignable.py
@@ -1,3 +1,6 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
import unittest
from jnius import autoclass, JavaException
diff --git a/tests/test_bad_declaration.py b/tests/test_bad_declaration.py
index a7b46a7..93b6b33 100644
--- a/tests/test_bad_declaration.py
+++ b/tests/test_bad_declaration.py
@@ -1,3 +1,6 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
import unittest
from jnius import JavaException, JavaClass
from jnius.reflect import autoclass
@@ -21,3 +24,31 @@ 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:
+ # 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))
diff --git a/tests/test_basics.py b/tests/test_basics.py
index 3964561..d6ebd5c 100644
--- a/tests/test_basics.py
+++ b/tests/test_basics.py
@@ -1,3 +1,6 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
import unittest
from jnius.reflect import autoclass
@@ -55,6 +58,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)
@@ -98,7 +121,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 46a7296..50fcd0b 100644
--- a/tests/test_bytearray.py
+++ b/tests/test_bytearray.py
@@ -1,3 +1,6 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
import unittest
from jnius import autoclass
@@ -6,4 +9,16 @@ 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(b'\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])
diff --git a/tests/test_cast.py b/tests/test_cast.py
index 6498821..6562cd9 100644
--- a/tests/test_cast.py
+++ b/tests/test_cast.py
@@ -1,3 +1,6 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
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..31f4b40 100644
--- a/tests/test_class_argument.py
+++ b/tests/test_class_argument.py
@@ -1,3 +1,6 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
import unittest
from jnius.reflect import autoclass
diff --git a/tests/test_enum.py b/tests/test_enum.py
index 8c10392..d590639 100644
--- a/tests/test_enum.py
+++ b/tests/test_enum.py
@@ -1,3 +1,6 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
import unittest
from jnius.reflect import autoclass
diff --git a/tests/test_implementation.py b/tests/test_implementation.py
index 9237ac4..81b66be 100644
--- a/tests/test_implementation.py
+++ b/tests/test_implementation.py
@@ -1,4 +1,7 @@
# -*- coding: utf-8 -*-
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
import unittest
from jnius.reflect import autoclass
diff --git a/tests/test_interface.py b/tests/test_interface.py
index 61b9a5c..1d8282a 100644
--- a/tests/test_interface.py
+++ b/tests/test_interface.py
@@ -1,3 +1,6 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
import unittest
from jnius import autoclass, JavaException
diff --git a/tests/test_jnitable_overflow.py b/tests/test_jnitable_overflow.py
index 4bf379d..0a98bc7 100644
--- a/tests/test_jnitable_overflow.py
+++ b/tests/test_jnitable_overflow.py
@@ -1,3 +1,6 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
# 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..3e4266f 100644
--- a/tests/test_method_multiple_signatures.py
+++ b/tests/test_method_multiple_signatures.py
@@ -1,3 +1,6 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
import unittest
from jnius.reflect import autoclass
diff --git a/tests/test_multidimension.py b/tests/test_multidimension.py
new file mode 100644
index 0000000..0c4b7de
--- /dev/null
+++ b/tests/test_multidimension.py
@@ -0,0 +1,15 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+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)
diff --git a/tests/test_output_args.py b/tests/test_output_args.py
index e8f558e..2547357 100644
--- a/tests/test_output_args.py
+++ b/tests/test_output_args.py
@@ -1,3 +1,6 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
import unittest
from jnius import autoclass
diff --git a/tests/test_proxy.py b/tests/test_proxy.py
index f89f80f..1ed01b9 100644
--- a/tests/test_proxy.py
+++ b/tests/test_proxy.py
@@ -1,6 +1,12 @@
-from jnius import autoclass, java_method, PythonJavaClass, cast
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+from six.moves import range
-print '1: declare a TestImplem that implement Collection'
+from jnius import autoclass, java_method, PythonJavaClass, cast
+from nose.tools import *
+
+print('1: declare a TestImplem that implement Collection')
class TestImplemIterator(PythonJavaClass):
@@ -96,47 +102,65 @@ class TestImplem(PythonJavaClass):
return it
-print '2: instanciate the class, with some data'
-a = TestImplem(*range(10))
-print a
-print dir(a)
+class TestBadSignature(PythonJavaClass):
+ __javainterfaces__ = ['java/util/List']
-print 'tries to get a ListIterator'
+ @java_method('(Landroid/bluetooth/BluetoothDevice;IB[])V')
+ def bad_signature(self, *args):
+ pass
+
+
+print('2: instantiate the class, with some data')
+a = TestImplem(*list(range(10)))
+print(a)
+print(dir(a))
+
+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)
+
+# test bad signature
+threw = False
+try:
+ TestBadSignature()
+except Exception:
+ threw = True
+
+if not threw:
+ raise Exception("Failed to throw for bad signature")
diff --git a/tests/test_reflect.py b/tests/test_reflect.py
index fedc95a..fc6e8c9 100644
--- a/tests/test_reflect.py
+++ b/tests/test_reflect.py
@@ -1,3 +1,6 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
import unittest
from jnius.reflect import autoclass
diff --git a/tests/test_signature.py b/tests/test_signature.py
new file mode 100644
index 0000000..07c9422
--- /dev/null
+++ b/tests/test_signature.py
@@ -0,0 +1,174 @@
+import unittest
+
+from jnius import autoclass, java_method, PythonJavaClass, cast
+
+from jnius.signatures import *
+
+JObject = autoclass('java/lang/Object')
+JString = autoclass('java/lang/String')
+JListIterator = autoclass("java.util.ListIterator")
+
+class TestImplemIterator(PythonJavaClass):
+ __javainterfaces__ = [
+ 'java/util/ListIterator', ]
+
+ def __init__(self, collection, index=0):
+ super(TestImplemIterator, self).__init__()
+ self.collection = collection
+ self.index = index
+
+ @with_signature(jboolean, [])
+ def hasNext(self):
+ return self.index < len(self.collection.data) - 1
+
+ @with_signature(JObject, [])
+ def next(self):
+ obj = self.collection.data[self.index]
+ self.index += 1
+ return obj
+
+ @with_signature(jboolean, [])
+ def hasPrevious(self):
+ return self.index >= 0
+
+ @with_signature(JObject, [])
+ def previous(self):
+ self.index -= 1
+ obj = self.collection.data[self.index]
+ return obj
+
+ @with_signature(jint, [])
+ def previousIndex(self):
+ return self.index - 1
+
+ @with_signature(JString, [])
+ def toString(self):
+ return repr(self)
+
+ @with_signature(JObject, [jint])
+ def get(self, index):
+ return self.collection.data[index - 1]
+
+ @with_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)
+
+ @with_signature(autoclass("java.util.Iterator"), [])
+ def iterator(self):
+ it = TestImplemIterator(self)
+ return it
+
+ @with_signature(JString, [])
+ def toString(self):
+ return repr(self)
+
+ @with_signature(jint, [])
+ def size(self):
+ return len(self.data)
+
+ @with_signature(JObject, [jint])
+ def get(self, index):
+ return self.data[index]
+
+ @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), [])
+ def toArray(self):
+ return self.data
+
+ @with_signature(JListIterator, [])
+ def listIterator(self):
+ it = TestImplemIterator(self)
+ return it
+
+ # TODO cover this case of listIterator.
+ @java_method(signature(JListIterator, [jint]),
+ 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()
+
+ def test_return_types(self):
+
+ # Void
+ sig = signature(jvoid, [])
+ self.assertEquals(sig, "()V")
+
+ # 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;")
+
+ # 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")
+
diff --git a/tests/test_simple.py b/tests/test_simple.py
index 2601562..edfcd99 100644
--- a/tests/test_simple.py
+++ b/tests/test_simple.py
@@ -1,12 +1,15 @@
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
import unittest
from jnius import JavaClass, MetaJavaClass, JavaMethod
+from six 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;')