Skip to content

Commit

Permalink
Fix exception caused by non-ascii paths
Browse files Browse the repository at this point in the history
  • Loading branch information
tachyonicClock committed Jun 17, 2024
1 parent 653ccff commit f4b6d88
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 1 deletion.
3 changes: 3 additions & 0 deletions doc/CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ Latest Changes:
For editable installs, ``python setup.py --enable-tracing develop``
must now be done with ``python setup.py develop --enable-tracing``.

- Fixed a problem that caused ``dir(jpype.JPackage("mypackage"))`` to fail if
the class path contained non-ascii characters. See issue #1194.

- **1.5.0 - 2023-04-03**

- Support for Python 3.12
Expand Down
16 changes: 15 additions & 1 deletion native/java/org/jpype/pkg/JPypePackageManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,21 @@ private static URI toURI(Path path)
URI uri = path.toUri();
if (uri.getScheme().equals("jar") && uri.toString().contains("%2520"))
uri = URI.create("jar:" + uri.getRawSchemeSpecificPart().replaceAll("%25", "%"));
return uri;

// `toASCIIString` ensures the URI is URL encoded with only ascii
// characters. This avoids issues in `sun.nio.fs.UnixUriUtils.fromUri` that
// naively uses `uri.getRawPath()` despite the possibility that it contains
// non-ascii characters that will cause errors. By using `toASCIIString` and
// re-wrapping it in a URI object we ensure that the URI is properly
// encoded. See: https://github.com/jpype-project/jpype/issues/1194
try {
return new URI(uri.toASCIIString());
} catch (Exception e) {
// This exception *should* never occur as we are re-encoding a valid URI.
// Throwing a runtime exception avoids java exception handling boilerplate
// for a situation that *should* never occur.
throw new RuntimeException("Failed to encode URI: " + uri, e);
}
}
//</editor-fold>
}
45 changes: 45 additions & 0 deletions test/jpypetest/test_non_ascii_paths.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"""
Regression test for https://github.com/jpype-project/jpype/issues/1194
"""

import unittest
import jpype
from pathlib import Path
import shutil
from multiprocessing import get_context, Queue, Process


class TestNonAsciiPaths(unittest.TestCase):

def setUp(self):
# Create a copy of the mrjar.jar file with a non-ASCII path.
mrjar_path = Path("test/jar/mrjar.jar")
self.mrjar_non_ascii = Path("test/jar/à_non_ascii/mrjar.jar")
self.mrjar_non_ascii.parent.mkdir(exist_ok=True)
shutil.copy(mrjar_path, self.mrjar_non_ascii)

def tearDown(self):
# Clean up the non-ASCII path.
self.mrjar_non_ascii.unlink()
self.mrjar_non_ascii.parent.rmdir()

@staticmethod
def _subproc_test_non_ascii_paths(path: str, queue: Queue):
# Can `dir` be called on a package with a non-ASCII path?
jpype.addClassPath(path)
jpype.startJVM(jpype.getDefaultJVMPath())
queue.put(dir(jpype.JPackage("org.jpype.mrjar")))
jpype.shutdownJVM()

def test_non_ascii_paths(self):
# Uses a separate process to avoid polluting the main process with
# a JVM instance, since restarting the JVM is not supported.
ctx = get_context("spawn")
queue: Queue = ctx.Queue()
path = self.mrjar_non_ascii.absolute().as_posix()
proc: Process = ctx.Process(
target=self._subproc_test_non_ascii_paths, args=(path, queue)
)
proc.start()
proc.join(5)
assert queue.get() == ["A", "B", "sub"]

0 comments on commit f4b6d88

Please sign in to comment.