Skip to content

Commit 50092cb

Browse files
authored
Merge pull request #92 from ncittm/fix-jvm-version
Fix jvm_version parsing of non-numeric version suffixes
2 parents 4f16009 + 9e20157 commit 50092cb

23 files changed

Lines changed: 147 additions & 135 deletions

src/scyjava/__init__.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,9 @@
8484
"""
8585

8686
import logging
87+
from collections.abc import Callable
8788
from functools import lru_cache
88-
from typing import Any, Callable, Dict
89+
from typing import Any
8990

9091
from . import config, inspect
9192
from ._arrays import is_arraylike, is_memoryarraylike, is_xarraylike
@@ -112,7 +113,7 @@
112113
jreflect,
113114
jsource,
114115
)
115-
from ._jvm import ( # noqa: F401
116+
from ._jvm import (
116117
available_processors,
117118
gc,
118119
is_awt_initialized,
@@ -161,7 +162,7 @@
161162
_logger = logging.getLogger(__name__)
162163

163164
# Set of module properties
164-
_CONSTANTS: Dict[str, Callable] = {}
165+
_CONSTANTS: dict[str, Callable] = {}
165166

166167

167168
def constant(func: Callable[[], Any], cache=True) -> Callable[[], Any]:

src/scyjava/_convert.py

Lines changed: 40 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@
77
import logging
88
import math
99
from bisect import insort
10+
from collections.abc import Callable
1011
from importlib.util import find_spec
1112
from pathlib import Path
12-
from typing import Any, Callable, Dict, List, NamedTuple
13+
from typing import Any, NamedTuple
1314

1415
from jpype import JBoolean, JByte, JChar, JDouble, JFloat, JInt, JLong, JShort
1516

@@ -52,14 +53,14 @@ class Converter(NamedTuple):
5253
priority: float = Priority.NORMAL
5354
name: str = "<unnamed>"
5455

55-
def supports(self, obj: Any, **hints: Dict) -> bool:
56+
def supports(self, obj: Any, **hints: dict) -> bool:
5657
return (
5758
self.predicate(obj, **hints)
5859
if _has_kwargs(self.predicate)
5960
else self.predicate(obj)
6061
)
6162

62-
def convert(self, obj: Any, **hints: Dict) -> Any:
63+
def convert(self, obj: Any, **hints: dict) -> Any:
6364
return (
6465
self.converter(obj, **hints)
6566
if _has_kwargs(self.converter)
@@ -82,7 +83,7 @@ def __str__(self):
8283
return self.name
8384

8485

85-
def _convert(obj: Any, converters: List[Converter], **hints: Dict) -> Any:
86+
def _convert(obj: Any, converters: list[Converter], **hints: dict) -> Any:
8687
# NB: The given converters are assumed to be sorted ascending by priority,
8788
# meaning lower-priority items appear earlier than higher-priority ones.
8889
# But we want to try the higher priority converters first, so we
@@ -132,7 +133,7 @@ def _convertIterable(obj: collections.abc.Iterable):
132133
return jlist
133134

134135

135-
java_converters: List[Converter] = []
136+
java_converters: list[Converter] = []
136137

137138

138139
def add_java_converter(converter: Converter) -> None:
@@ -143,7 +144,7 @@ def add_java_converter(converter: Converter) -> None:
143144
insort(java_converters, converter)
144145

145146

146-
def to_java(obj: Any, **hints: Dict) -> Any:
147+
def to_java(obj: Any, **hints: dict) -> Any:
147148
"""
148149
Recursively convert a Python object to a Java object.
149150
@@ -197,7 +198,7 @@ def to_java(obj: Any, **hints: Dict) -> Any:
197198
return _convert(obj, java_converters, **hints)
198199

199200

200-
def _stock_java_converters() -> List[Converter]:
201+
def _stock_java_converters() -> list[Converter]:
201202
"""
202203
Construct the Python-to-Java converters supported out of the box.
203204
:return: A list of Converters
@@ -366,7 +367,7 @@ def _jstr(data):
366367
if isinstance(data, JavaObject):
367368
return str(data)
368369
# NB: We want Python strings to render in single quotes.
369-
return "{!r}".format(data)
370+
return f"{data!r}"
370371

371372

372373
class JavaObject:
@@ -526,7 +527,7 @@ def __str__(self):
526527
return "{" + ", ".join(_jstr(v) for v in self) + "}"
527528

528529

529-
py_converters: List[Converter] = []
530+
py_converters: list[Converter] = []
530531

531532

532533
def add_py_converter(converter: Converter) -> None:
@@ -566,13 +567,13 @@ def to_python(data: Any, gentle: bool = False) -> Any:
566567
start_jvm()
567568
try:
568569
return _convert(data, py_converters)
569-
except TypeError as exc:
570+
except TypeError:
570571
if gentle:
571572
return data
572-
raise exc
573+
raise
573574

574575

575-
def _stock_py_converters() -> List:
576+
def _stock_py_converters() -> list:
576577
"""
577578
Construct the Java-to-Python converters supported out of the box.
578579
:return: A list of Converters
@@ -842,7 +843,7 @@ def _is_table(obj: Any) -> bool:
842843
"""Check if obj is a table."""
843844
try:
844845
return jinstance(obj, "org.scijava.table.Table")
845-
except BaseException:
846+
except BaseException: # noqa: BLE001
846847
# No worries if scijava-table is not available.
847848
return False
848849

@@ -851,7 +852,7 @@ def _convert_table(obj: Any):
851852
"""Convert obj to a table."""
852853
try:
853854
return _table_to_pandas(obj)
854-
except BaseException:
855+
except BaseException: # noqa: BLE001
855856
# No worries if scijava-table is not available.
856857
return None
857858

@@ -894,8 +895,8 @@ def _pandas_to_table(df):
894895
elif table_type.name.startswith("bool"):
895896
TableClass = jimport("org.scijava.table.DefaultBoolTable")
896897
else:
897-
msg = "The type '{}' is not supported.".format(table_type.name)
898-
raise Exception(msg)
898+
msg = f"The type '{table_type.name}' is not supported."
899+
raise ValueError(msg)
899900

900901
table = TableClass(*df.shape[::-1])
901902

@@ -913,51 +914,51 @@ def _pandas_to_table(df):
913914
# fmt: off
914915
class _JavaClasses(JavaClasses):
915916
@JavaClasses.java_import
916-
def Boolean(self): return "java.lang.Boolean" # noqa: E272
917+
def Boolean(self): return "java.lang.Boolean"
917918
@JavaClasses.java_import
918-
def Byte(self): return "java.lang.Byte" # noqa: E272
919+
def Byte(self): return "java.lang.Byte"
919920
@JavaClasses.java_import
920-
def Character(self): return "java.lang.Character" # noqa: E272
921+
def Character(self): return "java.lang.Character"
921922
@JavaClasses.java_import
922-
def Double(self): return "java.lang.Double" # noqa: E272
923+
def Double(self): return "java.lang.Double"
923924
@JavaClasses.java_import
924-
def Float(self): return "java.lang.Float" # noqa: E272
925+
def Float(self): return "java.lang.Float"
925926
@JavaClasses.java_import
926-
def Integer(self): return "java.lang.Integer" # noqa: E272
927+
def Integer(self): return "java.lang.Integer"
927928
@JavaClasses.java_import
928-
def Iterable(self): return "java.lang.Iterable" # noqa: E272
929+
def Iterable(self): return "java.lang.Iterable"
929930
@JavaClasses.java_import
930-
def Long(self): return "java.lang.Long" # noqa: E272
931+
def Long(self): return "java.lang.Long"
931932
@JavaClasses.java_import
932-
def Object(self): return "java.lang.Object" # noqa: E272
933+
def Object(self): return "java.lang.Object"
933934
@JavaClasses.java_import
934-
def Short(self): return "java.lang.Short" # noqa: E272
935+
def Short(self): return "java.lang.Short"
935936
@JavaClasses.java_import
936-
def String(self): return "java.lang.String" # noqa: E272
937+
def String(self): return "java.lang.String"
937938
@JavaClasses.java_import
938-
def BigDecimal(self): return "java.math.BigDecimal" # noqa: E272
939+
def BigDecimal(self): return "java.math.BigDecimal"
939940
@JavaClasses.java_import
940-
def BigInteger(self): return "java.math.BigInteger" # noqa: E272
941+
def BigInteger(self): return "java.math.BigInteger"
941942
@JavaClasses.java_import
942-
def Path(self): return "java.nio.file.Path" # noqa: E272
943+
def Path(self): return "java.nio.file.Path"
943944
@JavaClasses.java_import
944-
def Paths(self): return "java.nio.file.Paths" # noqa: E272
945+
def Paths(self): return "java.nio.file.Paths"
945946
@JavaClasses.java_import
946-
def ArrayList(self): return "java.util.ArrayList" # noqa: E272
947+
def ArrayList(self): return "java.util.ArrayList"
947948
@JavaClasses.java_import
948-
def Collection(self): return "java.util.Collection" # noqa: E272
949+
def Collection(self): return "java.util.Collection"
949950
@JavaClasses.java_import
950-
def Iterator(self): return "java.util.Iterator" # noqa: E272
951+
def Iterator(self): return "java.util.Iterator"
951952
@JavaClasses.java_import
952-
def LinkedHashMap(self): return "java.util.LinkedHashMap" # noqa: E272
953+
def LinkedHashMap(self): return "java.util.LinkedHashMap"
953954
@JavaClasses.java_import
954-
def LinkedHashSet(self): return "java.util.LinkedHashSet" # noqa: E272
955+
def LinkedHashSet(self): return "java.util.LinkedHashSet"
955956
@JavaClasses.java_import
956-
def List(self): return "java.util.List" # noqa: E272
957+
def List(self): return "java.util.List"
957958
@JavaClasses.java_import
958-
def Map(self): return "java.util.Map" # noqa: E272
959+
def Map(self): return "java.util.Map"
959960
@JavaClasses.java_import
960-
def Set(self): return "java.util.Set" # noqa: E272
961+
def Set(self): return "java.util.Set"
961962
# fmt: on
962963

963964

src/scyjava/_introspect.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
class methods, fields, and source code URL.
44
"""
55

6-
from typing import Any, Dict, List
6+
from typing import Any
77

88
from scyjava._jvm import jimport, jvm_version
9-
from scyjava._types import isjava, jinstance, jclass
9+
from scyjava._types import isjava, jclass, jinstance
1010

1111

12-
def jreflect(data, aspect: str = "all") -> List[Dict[str, Any]]:
12+
def jreflect(data, aspect: str = "all") -> list[dict[str, Any]]:
1313
"""
1414
Use Java reflection to introspect the given Java object,
1515
returning a table of its available methods or fields.
@@ -91,7 +91,7 @@ def jsource(data) -> str:
9191
try:
9292
data = jimport(data) # check if data can be imported
9393
except Exception as err:
94-
raise ValueError(f"Not a Java object {err}")
94+
raise ValueError(f"Not a Java object {err}") from err
9595
jcls = data if jinstance(data, "java.lang.Class") else jclass(data)
9696

9797
if jcls.getClassLoader() is None:

src/scyjava/_jdk_fetch.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import logging
88
import os
9-
from typing import TYPE_CHECKING, Union
9+
from typing import TYPE_CHECKING
1010

1111
from jgo.exec import JavaLocator, JavaSource
1212

@@ -64,7 +64,7 @@ def resolve_java(vendor: str | None = None, version: str | None = None) -> None:
6464
os.environ["JAVA_HOME"] = str(java_home)
6565

6666

67-
def _add_to_path(path: Union[Path, str], front: bool = False) -> None:
67+
def _add_to_path(path: Path | str, front: bool = False) -> None:
6868
"""Add a path to the PATH environment variable.
6969
7070
If front is True, the path is added to the front of the PATH.

src/scyjava/_jvm.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,18 @@
88
import re
99
import subprocess
1010
import sys
11-
from functools import lru_cache
11+
from collections.abc import Sequence
12+
from functools import cache
1213
from importlib import import_module
1314
from pathlib import Path
14-
from typing import Sequence
1515

16+
import jgo
1617
import jpype
1718
import jpype.config
18-
import jgo
1919

2020
import scyjava.config
21-
from scyjava.config import Mode, mode
2221
from scyjava._jdk_fetch import resolve_java
22+
from scyjava.config import Mode, mode
2323

2424
_logger = logging.getLogger(__name__)
2525

@@ -130,11 +130,15 @@ def jvm_version() -> tuple[int, ...]:
130130
except subprocess.CalledProcessError as e:
131131
raise RuntimeError("System call to java failed") from e
132132

133-
output = output.replace("\n", " ").replace("\r", "")
134-
m = re.match('.* version "([^"]*)"', output)
133+
return _jvm_version_str_to_tuple(output, java)
134+
135+
136+
def _jvm_version_str_to_tuple(java_version_output: str, java: str) -> tuple[int, ...]:
137+
java_version_output = java_version_output.replace("\n", " ").replace("\r", "")
138+
m = re.match(r'.*version "(\d+(?:\.\d+)*)', java_version_output)
135139
if not m:
136140
raise RuntimeError(
137-
f"Inscrutable java command output:\n$ {java} -version\n{output}"
141+
f"Inscrutable java command output:\n$ {java} -version\n{java_version_output}"
138142
)
139143

140144
v = m.group(1)
@@ -172,7 +176,7 @@ def start_jvm(options: Sequence[str] | None = None) -> None:
172176
repositories = scyjava.config.get_repositories()
173177

174178
# use the logger to notify user that endpoints are being added
175-
_logger.debug("Adding jars from endpoints {0}".format(endpoints))
179+
_logger.debug(f"Adding jars from endpoints {endpoints}")
176180

177181
# download Java as appropriate
178182
resolve_java()
@@ -293,7 +297,7 @@ def shutdown_jvm() -> None:
293297
for callback in _shutdown_callbacks:
294298
try:
295299
callback()
296-
except Exception as e:
300+
except Exception as e: # noqa: BLE001
297301
_logger.error(f"Exception during shutdown callback: {e}")
298302

299303
# dispose AWT resources if applicable
@@ -305,7 +309,7 @@ def shutdown_jvm() -> None:
305309
# okay to shutdown JVM
306310
try:
307311
jpype.shutdownJVM()
308-
except Exception as e:
312+
except Exception as e: # noqa: BLE001
309313
_logger.error(f"Exception during JVM shutdown: {e}")
310314

311315

@@ -440,7 +444,6 @@ def when_jvm_starts(f) -> None:
440444
f()
441445
else:
442446
# Add function to the list of callbacks to invoke upon start_jvm().
443-
global _startup_callbacks
444447
_startup_callbacks.append(f)
445448

446449

@@ -454,11 +457,10 @@ def when_jvm_stops(f) -> None:
454457
455458
:param f: Function to invoke when scyjava.shutdown_jvm() is called.
456459
"""
457-
global _shutdown_callbacks
458460
_shutdown_callbacks.append(f)
459461

460462

461-
@lru_cache(maxsize=None)
463+
@cache
462464
def jimport(class_name: str):
463465
"""
464466
Import a class from Java to Python.

0 commit comments

Comments
 (0)