Skip to content

Commit

Permalink
fixed stupid mistake causing failure when specifying -Djava.class.path
Browse files Browse the repository at this point in the history
  • Loading branch information
astrelsky committed Oct 25, 2024
1 parent 4201de6 commit 8516121
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 17 deletions.
29 changes: 14 additions & 15 deletions jpype/_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,20 +109,14 @@ def isJVMStarted():
return _jpype.isStarted()


def _hasClassPath(args) -> bool:
def _getOldClassPath(args) -> list[str]:
for i in args:
if isinstance(i, str) and i.startswith('-Djava.class.path'):
if not i.isascii():
# This passes it to the system classloader.
# Unfortunately the string encoding gets butchered and the original
# isn't recoverable from System.getProperty("java.class.path").
# If it was, then I'd have our system classloader take care of
# it in the constructor, but for now it is impossible.
# raise a ValueError to tell the user they can't do this.
# https://bugs.openjdk.org/browse/JDK-8079633?jql=text%20~%20%22ParseUtil%22
raise ValueError("-Djava.class.path contains non ascii characters")
return True
return False
if not isinstance(i, str):
continue
_, _, classpath = i.partition('-Djava.class.path=')
if classpath:
return classpath.split(_classpath._SEP)
return []


def _hasSystemClassLoader(args) -> bool:
Expand Down Expand Up @@ -247,11 +241,13 @@ def startJVM(
jvmpath = os.fspath(jvmpath)

# Classpath handling
if _hasClassPath(jvmargs):
old_classpath = _getOldClassPath(jvmargs)
if old_classpath:
# Old style, specified in the arguments
if classpath is not None:
# Cannot apply both styles, conflict
raise TypeError('classpath specified twice')
classpath = old_classpath
elif classpath is None:
# Not specified at all, use the default classpath.
classpath = _classpath.getClassPath()
Expand All @@ -263,11 +259,14 @@ def startJVM(
if not os.path.exists(supportLib):
raise RuntimeError("Unable to find org.jpype.jar support library at " + supportLib)

late_load = not has_classloader
if classpath:
cp = _classpath._SEP.join(_handleClassPath(classpath))
if cp.isascii():
# no problems
extra_jvm_args += ['-Djava.class.path=%s'%cp ]
jvmargs = _removeClassPath(jvmargs)
late_load = False
elif has_classloader:
# https://bugs.openjdk.org/browse/JDK-8079633?jql=text%20~%20%22ParseUtil%22
raise ValueError("system classloader cannot be specified with non ascii characters in the classpath")
Expand Down Expand Up @@ -327,7 +326,7 @@ def startJVM(
To resolve this issue, we add the classpath after initialization since Java has
had the utilities to correctly encode it since 1.0
"""
if not has_classloader and classpath:
if late_load and classpath:
# now we can add to the system classpath
cl = _jpype.JClass("java.lang.ClassLoader").getSystemClassLoader()
for cp in _handleClassPath(classpath):
Expand Down
26 changes: 26 additions & 0 deletions test/jpypetest/test_startup.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,15 @@ def testNonASCIIPath(self):
assert dir(jpype.JPackage('org.jpype.sample_package')) == ['A', 'B']


def testOldStyleNonASCIIPath(self):
"""Test that paths with non-ASCII characters are handled correctly.
Regression test for https://github.com/jpype-project/jpype/issues/1194
"""
jpype.startJVM("-Djava.class.path=test/jar/unicode_à😎/sample_package.jar", jvmpath=Path(self.jvmpath))
cl = jpype.JClass("java.lang.ClassLoader").getSystemClassLoader()
self.assertEqual(type(cl), jpype.JClass("org.jpype.classloader.JpypeSystemClassLoader"))
assert dir(jpype.JPackage('org.jpype.sample_package')) == ['A', 'B']

def testNonASCIIPathWithSystemClassLoader(self):
with self.assertRaises(ValueError):
jpype.startJVM(
Expand All @@ -164,6 +173,13 @@ def testNonASCIIPathWithSystemClassLoader(self):
classpath="test/jar/unicode_à😎/sample_package.jar"
)

def testOldStyleNonASCIIPathWithSystemClassLoader(self):
with self.assertRaises(ValueError):
jpype.startJVM(
self.jvmpath,
"-Djava.system.class.loader=jpype.startup.TestSystemClassLoader",
"-Djava.class.path=test/jar/unicode_à😎/sample_package.jar"
)

def testASCIIPathWithSystemClassLoader(self):
jpype.startJVM(
Expand All @@ -175,6 +191,16 @@ def testASCIIPathWithSystemClassLoader(self):
self.assertEqual(type(cl), jpype.JClass("jpype.startup.TestSystemClassLoader"))
assert dir(jpype.JPackage('org.jpype.late')) == ['Test']

def testOldStyleASCIIPathWithSystemClassLoader(self):
jpype.startJVM(
self.jvmpath,
"-Djava.system.class.loader=jpype.startup.TestSystemClassLoader",
"-Djava.class.path=test/classes;test/jar/late/late.jar"
)
cl = jpype.JClass("java.lang.ClassLoader").getSystemClassLoader()
self.assertEqual(type(cl), jpype.JClass("jpype.startup.TestSystemClassLoader"))
assert dir(jpype.JPackage('org.jpype.late')) == ['Test']

def testDefaultSystemClassLoader(self):
# we introduce no behavior change unless absolutely necessary
jpype.startJVM(jvmpath=Path(self.jvmpath))
Expand Down
4 changes: 2 additions & 2 deletions test/jpypetest/test_thread.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@
#
# *****************************************************************************
import jpype
import sys
import time
import common
import pytest

from jpype.imports import *


class ThreadTestCase(common.JPypeTestCase):
def setUp(self):
Expand Down

0 comments on commit 8516121

Please sign in to comment.