Skip to content

Commit 4f1aa5b

Browse files
ctruedenclaude
andcommitted
Fail fast with clear error for Java < 11
JPype 1.6+ dropped Java 8 support, so surface a clear RuntimeError in start_jvm() rather than a cryptic JPype failure. Also improve macOS JVM path resolution to handle Java 8's different dylib layout (jre/lib/jli/libjli.dylib), with a further fallback to $JAVA_HOME/bin/java. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 50006f0 commit 4f1aa5b

File tree

1 file changed

+52
-16
lines changed

1 file changed

+52
-16
lines changed

src/scyjava/_jvm.py

Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -78,26 +78,49 @@ def jvm_version() -> tuple[int, ...]:
7878
)
7979

8080
p = Path(jvm_path)
81+
java = None
82+
8183
if not p.exists():
82-
raise RuntimeError(f"Invalid default JVM path: {p}")
84+
# Try Java 8 macOS dylib path (jre/lib/jli/libjli.dylib vs lib/libjli.dylib).
85+
p8 = Path(
86+
default_jvm_path.replace(
87+
"/Contents/MacOS/libjli.dylib",
88+
"/Contents/Home/jre/lib/jli/libjli.dylib",
89+
)
90+
)
91+
if p8.exists():
92+
p = p8
8393

84-
java = None
85-
for _ in range(3): # The bin folder is always <=3 levels up from libjvm.
86-
p = p.parent
87-
if p.name == "lib":
88-
java = p.parent / "bin" / "java"
89-
elif p.name == "bin":
90-
java = p / "java"
91-
92-
if java is not None:
94+
if not p.exists():
95+
# Fall back to JAVA_HOME if the dylib path resolution failed.
96+
java_home = os.environ.get("JAVA_HOME")
97+
if java_home:
98+
candidate = Path(java_home) / "bin" / "java"
9399
if os.name == "nt":
94-
# Good ol' Windows! Nothing beats Windows.
95-
java = java.with_suffix(".exe")
96-
if not java.is_file():
97-
raise RuntimeError(f"No ../bin/java found at: {p}")
98-
break
100+
candidate = candidate.with_suffix(".exe")
101+
if candidate.is_file():
102+
java = candidate
103+
104+
if java is None:
105+
raise RuntimeError(f"Invalid default JVM path: {p}")
106+
99107
if java is None:
100-
raise RuntimeError(f"No java executable found inside: {p}")
108+
for _ in range(3): # The bin folder is always <=3 levels up from libjvm.
109+
p = p.parent
110+
if p.name == "lib":
111+
java = p.parent / "bin" / "java"
112+
elif p.name == "bin":
113+
java = p / "java"
114+
115+
if java is not None:
116+
if os.name == "nt":
117+
# Good ol' Windows! Nothing beats Windows.
118+
java = java.with_suffix(".exe")
119+
if not java.is_file():
120+
raise RuntimeError(f"No ../bin/java found at: {p}")
121+
break
122+
if java is None:
123+
raise RuntimeError(f"No java executable found inside: {p}")
101124

102125
_logger.debug(f"Invoking `{java} -version`...")
103126
try:
@@ -154,6 +177,19 @@ def start_jvm(options: Sequence[str] = None) -> None:
154177
# download Java as appropriate
155178
ensure_jvm_available()
156179

180+
# Fail fast if Java version is too old. JPype 1.6+ dropped Java 8 support.
181+
try:
182+
ver = jvm_version()
183+
if ver < (11,):
184+
raise RuntimeError(
185+
f"Java {'.'.join(str(v) for v in ver)} is not supported. "
186+
"scyjava requires Java 11 or later."
187+
)
188+
except RuntimeError as e:
189+
if "not supported" in str(e):
190+
raise
191+
_logger.debug(f"Could not determine JVM version before start: {e}")
192+
157193
# get endpoints and add to JPype class path
158194
if len(endpoints) > 0:
159195
# sort endpoints list, except for the first one

0 commit comments

Comments
 (0)