Merge pull request #1 from kivy/master

Updating codebase
This commit is contained in:
Thomas-Karl Pietrowski 2015-11-19 17:54:37 +01:00
commit f2e66661f5
50 changed files with 1576 additions and 429 deletions

3
.gitignore vendored
View File

@ -7,3 +7,6 @@ build
.*.swp
*.class
.idea
*~
*.so
*.iml

View File

@ -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

165
COPYING
View File

@ -1,165 +0,0 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
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.

View File

@ -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

View File

@ -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)

View File

@ -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.

25
build.xml Normal file
View File

@ -0,0 +1,25 @@
<project>
<target name="clean">
<delete dir="build/classes"/>
<delete dir="build/test-classes"/>
</target>
<target name="compile">
<mkdir dir="build/classes"/>
<javac srcdir="jnius/src" destdir="build/classes"
includeantruntime='false' source='1.6' target='1.6' />
</target>
<target name="test-compile">
<mkdir dir="build/test-classes"/>
<javac srcdir="tests/java-src" destdir="build/test-classes"
includeantruntime='false' source='1.6' target='1.6' />
</target>
<target name="jar" depends="compile">
<jar destfile="build/pyjnius.jar" basedir="build/classes">
</jar>
</target>
<target name="all" depends="jar,test-compile"/>
</project>

View File

@ -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
<http://developer.android.com/reference/android/util/DisplayMetrics.html>`_ contains multiple fields that can return a lot of information about the device's screen::
<http://developer.android.com/reference/android/util/DisplayMetrics.html>`_
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
<http://developer.android.com/guide/topics/media/audio-capture.html>`_ 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
<http://developer.android.com/reference/android/hardware/SensorManager.html>`_
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
<http://developer.android.com/reference/android/hardware/SensorEventListener.html>`_
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) {

View File

@ -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):
...

View File

@ -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_

View File

@ -401,4 +401,5 @@ cdef extern from "jni.h":
ctypedef struct JNIInvokeInterface:
jint (*AttachCurrentThread)(JavaVM *, JNIEnv **, void *)
jint (*DetachCurrentThread)(JavaVM *)

View File

@ -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"

View File

@ -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, <char *><bytes>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 = <bytes>py_arg.encode('utf-8')
j_args[index].l = j_env[0].NewStringUTF(j_env, <char *>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 = <char *>j_env[0].GetStringUTFChars(j_env, j_object, NULL)
py_str = <bytes>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, <char *><bytes>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, <char *><bytes>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 = <signed char>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, <bytes>definition[1:-1])
j_env, <bytes>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, <jobjectArray>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, <bytes>arg)
j_env[0].SetObjectArrayElement(
j_env, <jobjectArray>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, <bytes>utf8)
j_env[0].SetObjectArrayElement(
j_env, <jobjectArray>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, <jobjectArray>ret, 0, eproto)
for i in range(1, array_size):
j_env[0].SetObjectArrayElement(
j_env, <jobjectArray>ret, i, convert_pyarray_to_java(j_env, subdef, pyarray[i]))
else:
raise JavaException('Invalid array definition', definition, pyarray)

View File

@ -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)

View File

@ -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 = <jobject *>malloc(sizeof(jobject) * 2)
jargs[0] = <jobject *>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,
<char *>__javaclass__)
<char *>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, '<init>', <char *><bytes>definition)
j_env, self.j_cls, '<init>', <char *><bytes>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, <char *>self.name,
<char *>self.definition)
<char *>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, <char *>self.name,
<char *>self.definition)
j_env, self.j_cls, <char *>namestr,
<char *>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 = (<JavaClass?>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 = (<JavaClass?>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 = <jboolean>value
j_env[0].SetBooleanField(j_env, j_self, self.j_field, j_boolean)
elif r == 'B':
j_byte = <jbyte>value
j_env[0].SetByteField(j_env, j_self, self.j_field, j_byte)
elif r == 'C':
j_char = <jchar>value
j_env[0].SetCharField(j_env, j_self, self.j_field, j_char)
elif r == 'S':
j_short = <jshort>value
j_env[0].SetShortField(j_env, j_self, self.j_field, j_short)
elif r == 'I':
j_int = <jint>value
j_env[0].SetIntField(j_env, j_self, self.j_field, j_int)
elif r == 'J':
j_long = <jlong>value
j_env[0].SetLongField(j_env, j_self, self.j_field, j_long)
elif r == 'F':
j_float = <jfloat>value
j_env[0].SetFloatField(j_env, j_self, self.j_field, j_float)
elif r == 'D':
j_double = <jdouble>value
j_env[0].SetDoubleField(j_env, j_self, self.j_field, j_double)
elif r == 'L':
j_object = <jobject>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 = <bytes>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, <char *>self.name,
<char *>self.definition)
<char *>defstr)
else:
defstr = str_for_c(self.definition)
self.j_method = j_env[0].GetMethodID(
j_env, self.j_cls, <char *>self.name,
<char *>self.definition)
<char *>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:],)

View File

@ -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

View File

@ -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 = <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 = <JavaVMOption*>malloc(sizeof(JavaVMOption) * len(optarr))
for i, opt in enumerate(optarr):
if PY_MAJOR_VERSION >= 3:
opt = opt.encode('utf-8')
options[i].optionString = <bytes>(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, <void **>&_platform_default_env, &args)
ret = JNI_CreateJavaVM(&jvm, <void **>&_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

View File

@ -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 = <JavaVMOption*>malloc(sizeof(JavaVMOption) * len(optarr))
for i, opt in enumerate(optarr):
optbytes = str_for_c(opt)
options[i].optionString = <bytes>(optbytes)
options[i].extraInfo = NULL
args.version = JNI_VERSION_1_6
args.options = options
args.nOptions = len(optarr)
args.ignoreUnrecognized = JNI_FALSE
ret = (<jint (*)(JavaVM **pvm, void **penv, void *args)> jniCreateJVM)(&jvm, <void **>&_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

View File

@ -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[:])

View File

@ -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 = <jbyte[:size]>self._buf
def __str__(self):
return '<ByteArray size={} at 0x{}>'.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]

View File

@ -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:

View File

@ -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, <bytes> '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, <bytes> '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, <bytes> '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

View File

@ -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())

99
jnius/signatures.py Normal file
View File

@ -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

79
jnius_config.py Normal file
View File

@ -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)

114
setup.py
View File

@ -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'])

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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))

View File

@ -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')()

View File

@ -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])

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -1,3 +1,6 @@
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
import unittest
from jnius import autoclass

View File

@ -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")

View File

@ -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

174
tests/test_signature.py Normal file
View File

@ -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")

View File

@ -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;')