Skip to content

Commit

Permalink
Merge pull request #1 from CellProfiler/py3-leek
Browse files Browse the repository at this point in the history
Fixes #54 - Python 3.x support
  • Loading branch information
jni committed Mar 10, 2016
2 parents c20f6dc + 5ae5fa2 commit a7ed071
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 57 deletions.
7 changes: 5 additions & 2 deletions _javabridge.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1072,7 +1072,7 @@ cdef class JB_Env:
free(<void *>values)
return result

def get_field_id(self, JB_Class c, char *name, char *sig):
def get_field_id(self, JB_Class c, name, sig):
'''Get a field ID for a class
:param c: class (from :py:meth:`.find_class` or similar)
Expand All @@ -1083,7 +1083,10 @@ cdef class JB_Env:
jfieldID id
__JB_FieldID jbid

id = self.env[0].GetFieldID(self.env, c.c, name, sig)
utf8name = name.encode('utf-8')
utf8sig = sig.encode('utf-8')

id = self.env[0].GetFieldID(self.env, c.c, utf8name, utf8sig)
if id == NULL:
return None
jbid = __JB_FieldID()
Expand Down
5 changes: 3 additions & 2 deletions demo/demo_nogui.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@
"""

from __future__ import print_function
import os
import javabridge

javabridge.start_vm(run_headless=True)
try:
print javabridge.run_script('java.lang.String.format("Hello, %s!", greetee);',
dict(greetee='world'))
print(javabridge.run_script('java.lang.String.format("Hello, %s!", greetee);',
dict(greetee='world')))
finally:
javabridge.kill_vm()
4 changes: 2 additions & 2 deletions java/org_cellprofiler_javabridge_CPython.c
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ static int attach_env(JNIEnv *pEnv){
javabridge.jni_enter(env)
*/

pPyEnv = PyCObject_FromVoidPtr((void *)pEnv, NULL);
pPyEnv = PyCapsule_New((void *)pEnv, NULL, NULL);
if (PyErr_Occurred()) {
throwWrappedError(pEnv, __LINE__);
return -1;
Expand Down Expand Up @@ -272,7 +272,7 @@ static PyObject *wrapJObject(JNIEnv *pEnv, jobject j) {
Py_DECREF(pJavabridge);
return NULL;
}
pCapsule = PyCObject_FromVoidPtr((void *)j, NULL);
pCapsule = PyCapsule_New((void *)j, NULL, NULL);
if (! pCapsule) {
throwWrappedError(pEnv, __LINE__);
Py_DECREF(pTheEnv);
Expand Down
Binary file added javabridge/_javabridge.cpython-34m.so
Binary file not shown.
42 changes: 25 additions & 17 deletions javabridge/locate.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
is_mac = sys.platform == 'darwin'
is_win = sys.platform.startswith("win")
is_win64 = (is_win and (os.environ["PROCESSOR_ARCHITECTURE"] == "AMD64"))
is_msvc = (is_win and sys.version_info[0] >= 2 and sys.version_info[1] >= 6)
is_msvc = (is_win and
((sys.version_info.major == 2 and sys.version_info.minor >= 6) or
(sys.version_info.major == 3)))
is_mingw = (is_win and not is_msvc)

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -74,20 +76,23 @@ def get_out(cmd):
jdk_dir = os.path.abspath(jdk_dir)
return jdk_dir
elif is_win:
import _winreg
if sys.version_info.major == 2:
import _winreg as winreg
else:
import winreg
java_key_path = 'SOFTWARE\\JavaSoft\\Java Runtime Environment'
looking_for = java_key_path
try:
kjava = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, java_key_path)
kjava = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, java_key_path)
looking_for = java_key_path + "\\CurrentVersion"
kjava_values = dict([_winreg.EnumValue(kjava, i)[:2]
for i in range(_winreg.QueryInfoKey(kjava)[1])])
kjava_values = dict([winreg.EnumValue(kjava, i)[:2]
for i in range(winreg.QueryInfoKey(kjava)[1])])
current_version = kjava_values['CurrentVersion']
looking_for = java_key_path + '\\' + current_version
kjava_current = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,
kjava_current = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,
looking_for)
kjava_current_values = dict([_winreg.EnumValue(kjava_current, i)[:2]
for i in range(_winreg.QueryInfoKey(kjava_current)[1])])
kjava_current_values = dict([winreg.EnumValue(kjava_current, i)[:2]
for i in range(winreg.QueryInfoKey(kjava_current)[1])])
return kjava_current_values['JavaHome']
except:
logger.error("Failed to find registry entry: %s\n" %looking_for,
Expand All @@ -102,20 +107,23 @@ def find_jdk():
if is_mac:
return find_javahome()
if is_win:
import _winreg
import exceptions
if sys.version_info.major == 2:
import _winreg as winreg
from exceptions import WindowsError
else:
import winreg
try:
jdk_key_path = 'SOFTWARE\\JavaSoft\\Java Development Kit'
kjdk = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, jdk_key_path)
kjdk_values = dict([_winreg.EnumValue(kjdk, i)[:2]
for i in range(_winreg.QueryInfoKey(kjdk)[1])])
kjdk = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, jdk_key_path)
kjdk_values = dict([winreg.EnumValue(kjdk, i)[:2]
for i in range(winreg.QueryInfoKey(kjdk)[1])])
current_version = kjdk_values['CurrentVersion']
kjdk_current = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,
kjdk_current = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,
jdk_key_path + '\\' + current_version)
kjdk_current_values = dict([_winreg.EnumValue(kjdk_current, i)[:2]
for i in range(_winreg.QueryInfoKey(kjdk_current)[1])])
kjdk_current_values = dict([winreg.EnumValue(kjdk_current, i)[:2]
for i in range(winreg.QueryInfoKey(kjdk_current)[1])])
return kjdk_current_values['JavaHome']
except exceptions.WindowsError as e:
except WindowsError as e:
if e.errno == 2:
raise RuntimeError(
"Failed to find the Java Development Kit. Please download and install the Oracle JDK 1.6 or later")
Expand Down
6 changes: 3 additions & 3 deletions javabridge/tests/test_cpython.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def fn(numerator, denominator, answer):
jlocals.put("code", code)
jlocals.put("answer", jref.o)
self.cpython.execute(code, jlocals.o, None)
self.assertEqual(javabridge.to_string(jref.get(0)), "3")
self.assertEqual(float(javabridge.to_string(jref.get(0))), 3)

def test_01_03_globals(self):
jglobals = javabridge.JClassWrapper('java.util.HashMap')()
Expand All @@ -51,7 +51,7 @@ def fn():
javabridge.call(answer, "add", "(Ljava/lang/Object;)Z", str(result))
fn()
""", None, jglobals.o)
self.assertEqual(javabridge.to_string(jref.get(0)), "3")
self.assertEqual(float(javabridge.to_string(jref.get(0))), 3)

def test_01_04_globals_equals_locals(self):
jglobals = javabridge.JClassWrapper('java.util.HashMap')()
Expand All @@ -69,4 +69,4 @@ def fn():
javabridge.call(answer, "add", "(Ljava/lang/Object;)Z", str(result))
fn()
""", jglobals.o, jglobals.o)
self.assertEqual(javabridge.to_string(jref.get(0)), "3")
self.assertEqual(float(javabridge.to_string(jref.get(0))), 3)
9 changes: 5 additions & 4 deletions javabridge/tests/test_javabridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def test_01_03_00_new_string_utf(self):
def test_01_03_01_new_string_unicode(self):
s = u"Hola ni\u00F1os"
jstring = self.env.new_string(s)
self.assertEqual(self.env.get_string_utf(jstring).decode("utf-8"), s)
self.assertEqual(self.env.get_string_utf(jstring), s)

def test_01_03_02_new_string_string(self):
s = "Hello, world"
Expand Down Expand Up @@ -331,13 +331,14 @@ def test_03_08_call_method_double(self):
self.assertAlmostEqual(self.env.call_method(jdouble, method_id), -55.64)

def test_03_09_call_method_array(self):
jstring = self.env.new_string_utf("Hello, world")
s = "Hello, world"
jstring = self.env.new_string_utf(s)
klass = self.env.get_object_class(jstring)
method_id = self.env.get_method_id(klass, 'getBytes', '()[B')
result = self.env.call_method(jstring, method_id)
self.assertTrue(isinstance(result, jb.JB_Object))
a = self.env.get_byte_array_elements(result)
self.assertEqual("Hello, world", a.tostring())
self.assertEqual(np.array(s, "S%d" % len(s)).tostring(), a.tostring())

def test_03_10_call_method_object(self):
hello = self.env.new_string_utf("Hello, ")
Expand Down Expand Up @@ -445,7 +446,7 @@ def test_05_06_get_static_long_field(self):
klass = self.env.find_class("java/security/Key")
field_id = self.env.get_static_field_id(klass, "serialVersionUID", "J")
result = self.env.get_static_long_field(klass, field_id)
self.assertEqual(result, 6603384152749567654l) # see http://java.sun.com/j2se/1.4.2/docs/api/constant-values.html#java.security.Key.serialVersionUID
self.assertEqual(result, 6603384152749567654)

def test_05_07_get_static_float_field(self):
klass = self.env.find_class("java/lang/Float")
Expand Down
58 changes: 31 additions & 27 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def in_cwd(basename):

def build_cython():
"""Compile the pyx files if we have them.
The git repository has the .pyx files but not the .c files, and
the source distributions that are uploaded to PyPI have the .c
files and not the .pyx files. (The reason for the latter is that
Expand Down Expand Up @@ -76,17 +76,17 @@ def get_jvm_include_dirs():
elif is_mac:
where_jni_h_is_post_6 = os.path.join(java_home, 'include')
if os.path.isfile(os.path.join(where_jni_h_is_post_6, "jni.h")):

include_dirs += [where_jni_h_is_post_6,
os.path.join(java_home, 'include', 'darwin')]
else:
include_dirs += ["/System/Library/Frameworks/JavaVM.Framework/Headers"]
elif is_linux:
include_dirs += [os.path.join(java_home,'include'),
os.path.join(java_home,'include','linux')]

return include_dirs

def ext_modules():
extensions = []
extra_link_args = None
Expand All @@ -108,7 +108,7 @@ def ext_modules():
# Build libjvm from jvm.dll on Windows.
# This assumes that we're using mingw32 for build
#
cmd = ["dlltool", "--dllname",
cmd = ["dlltool", "--dllname",
os.path.join(jdk_home,"jre\\bin\\client\\jvm.dll"),
"--output-lib","libjvm.a",
"--input-def","jvm.def",
Expand Down Expand Up @@ -148,7 +148,7 @@ def ext_modules():

SO = ".dll" if sys.platform == 'win32' \
else ".jnilib" if sys.platform == 'darwin'\
else sysconfig.get_config_var("SO")
else ".so"

def needs_compilation(target, *sources):
try:
Expand Down Expand Up @@ -198,18 +198,19 @@ def run(self, *args, **kwargs):
def build_jar_from_sources(self, jar, sources):
if sys.platform == 'win32':
sources = [source.replace("/", os.path.sep) for source in sources]
jar = self.get_ext_fullpath(jar)
jar = os.path.splitext(jar)[0] + ".jar"
jar_filename = jar.rsplit(".", 1)[1] + ".jar"
jar_dir = os.path.dirname(self.get_ext_fullpath(jar))
jar = os.path.join(jar_dir, jar_filename)
jar_command = [find_jar_cmd(), 'cf', package_path(jar)]

javac_loc = find_javac_cmd()
dirty_jar = False
javac_command = [javac_loc, "-source", "6", "-target", "6"]
for source in sources:
javac_command.append(package_path(source))
if needs_compilation(jar, source):
dirty_jar = True

self.spawn(javac_command)
if dirty_jar:
if not os.path.exists(os.path.dirname(jar)):
Expand All @@ -219,11 +220,11 @@ def build_jar_from_sources(self, jar, sources):
java_klass_path = klass[klass.index(os.path.sep) + 1:].replace(os.path.sep, "/")
jar_command.extend(['-C', package_path('java'), java_klass_path])
self.spawn(jar_command)

def build_java2cpython(self):
sources = self.java2cpython_sources
distutils.log.info("building java2cpython library")


# First, compile the source code to object files in the library
# directory. (This should probably change to putting object
Expand All @@ -233,13 +234,15 @@ def build_java2cpython(self):
get_jvm_include_dirs()
python_lib_dir, lib_name = self.get_java2cpython_libdest()
library_dirs = [python_lib_dir]
output_dir = os.path.splitext(self.get_ext_fullpath("javabridge.jars"))[0]
export_symbols = ['Java_org_cellprofiler_javabridge_CPython_exec']
output_dir = os.path.join(os.path.dirname(
self.get_ext_fullpath("javabridge.jars")), "jars")
export_symbols = ['Java_org_cellprofiler_javabridge_CPython_exec']
objects = self.compiler.compile(sources,
output_dir=self.build_temp,
include_dirs=include_dirs,
debug=self.debug)
extra_postargs = ["/MANIFEST"] if sys.platform == 'win32' else None
needs_manifest = sys.platform == 'win32' and sys.version_info.major == 2
extra_postargs = ["/MANIFEST"] if needs_manifest else None
self.compiler.link(
CCompiler.SHARED_OBJECT,
objects, lib_name,
Expand All @@ -248,7 +251,7 @@ def build_java2cpython(self):
library_dirs=library_dirs,
export_symbols=export_symbols,
extra_postargs=extra_postargs)
if sys.platform == 'win32':
if needs_manifest:
temp_dir = os.path.dirname(objects[0])
manifest_name = lib_name +".manifest"
lib_path = os.path.join(output_dir, lib_name)
Expand All @@ -258,7 +261,7 @@ def build_java2cpython(self):
out_arg = '-outputresource:%s;2' % lib_path
try:
self.compiler.spawn([
'mt.exe', '-nologo', '-manifest', manifest_file,
'mt.exe', '-nologo', '-manifest', manifest_file,
out_arg])
except DistutilsExecError as msg:
raise LinkError(msg)
Expand All @@ -273,34 +276,34 @@ def get_java2cpython_libdest(self):
python_lib_dir = sysconfig.get_config_var('LIBDIR')
lib_name = "libjava2cpython" + SO
return python_lib_dir, lib_name


def build_jar_from_single_source(self, jar, source):
self.build_jar_from_sources(jar, [source])

def build_runnablequeue(self):
jar = 'javabridge.jars.runnablequeue'
source = 'java/org/cellprofiler/runnablequeue/RunnableQueue.java'
self.build_jar_from_single_source(jar, source)

def build_cpython(self):
jar = 'javabridge.jars.cpython'
sources = [
'java/org/cellprofiler/javabridge/CPython.java',
'java/org/cellprofiler/javabridge/CPythonInvocationHandler.java']
self.build_jar_from_sources(jar, sources)

def build_test(self):
jar = 'javabridge.jars.test'
source = 'java/org/cellprofiler/javabridge/test/RealRect.java'
self.build_jar_from_single_source(jar, source)

def build_java(self):
self.build_runnablequeue()
self.build_test()
self.build_cpython()


def get_version():
"""Get version from git or file system.
Expand All @@ -314,19 +317,19 @@ def get_version():
if os.path.exists(os.path.join(os.path.dirname(__file__), '.git')):
import subprocess
try:
git_version = subprocess.Popen(['git', 'describe'],
git_version = subprocess.Popen(['git', 'describe'],
stdout=subprocess.PIPE).communicate()[0].strip().decode('utf-8')
except:
pass

version_file = os.path.join(os.path.dirname(__file__), 'javabridge',
version_file = os.path.join(os.path.dirname(__file__), 'javabridge',
'_version.py')
if os.path.exists(version_file):
with open(version_file) as f:
cached_version_line = f.read().strip()
try:
# From http://stackoverflow.com/a/3619714/17498
cached_version = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]",
cached_version = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]",
cached_version_line, re.M).group(1)
except:
raise RuntimeError("Unable to find version in %s" % version_file)
Expand Down Expand Up @@ -357,7 +360,8 @@ def get_version():
classifiers=['Development Status :: 5 - Production/Stable',
'License :: OSI Approved :: BSD License',
'Programming Language :: Java',
'Programming Language :: Python :: 2 :: Only'
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 3'
],
license='BSD License',
install_requires=['numpy'],
Expand Down

0 comments on commit a7ed071

Please sign in to comment.