Skip to content

Commit f57d61f

Browse files
authored
Merge pull request #63 from scijava/jvm-config-shortcuts
Add JVM config shortcuts
2 parents ac62992 + 2bf3319 commit f57d61f

15 files changed

Lines changed: 346 additions & 67 deletions

.github/workflows/build.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ jobs:
2222
macos-latest
2323
]
2424
python-version: [
25-
'3.7',
2625
'3.8',
2726
'3.9',
2827
'3.10'

README.md

Lines changed: 75 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,15 @@ FUNCTIONS
158158
Add a converter to the list used by to_python.
159159
:param converter: A Converter from java to python
160160

161+
available_processors() -> int
162+
Get the number of processors available to the JVM.
163+
164+
This function is a shortcut for Java's
165+
Runtime.getRuntime().availableProcessors().
166+
167+
:return: The number of available processors.
168+
:raise RuntimeError: if the JVM has not yet been started.
169+
161170
enable_python_scripting(context)
162171
Adds a Python script runner object to the ObjectService of the given
163172
SciJava context. Intended for use in conjunction with
@@ -166,6 +175,13 @@ FUNCTIONS
166175
:param context: The org.scijava.Context containing the ObjectService
167176
where the PythonScriptRunner should be injected.
168177

178+
gc() -> None
179+
Do a round of Java garbage collection.
180+
181+
This function is a shortcut for Java's System.gc().
182+
183+
:raise RuntimeError: if the JVM has not yet been started.
184+
169185
get_version(java_class_or_python_package) -> str
170186
Return the version of a Java class or Python package.
171187

@@ -254,12 +270,21 @@ FUNCTIONS
254270
jclass(data)
255271
Obtain a Java class object.
256272

257-
:param data: The object from which to glean the class.
258273
Supported types include:
259-
A. Name of a class to look up, analogous to
260-
Class.forName("java.lang.String");
261-
B. A jpype.JClass object analogous to String.class;
262-
C. A jpype.JObject instance analogous to o.getClass().
274+
275+
A. Name of a class to look up -- e.g. "java.lang.String" --
276+
which returns the equivalent of Class.forName("java.lang.String").
277+
278+
B. A static-style class reference -- e.g. String --
279+
which returns the equivalent of String.class.
280+
281+
C. A Java object -- e.g. foo --
282+
which returns the equivalent of foo.getClass().
283+
284+
Note that if you pass a java.lang.Class object, you will get back Class.class,
285+
i.e. the Java class for the Class class. :-)
286+
287+
:param data: The object from which to glean the class.
263288
:returns: A java.lang.Class object, suitable for use with reflection.
264289
:raises TypeError: if the argument is not one of the aforementioned types.
265290

@@ -297,28 +322,63 @@ FUNCTIONS
297322
Return true iff a Java virtual machine (JVM) has been started.
298323

299324
jvm_version() -> str
300-
Gets the version of the JVM as a tuple,
301-
with each dot-separated digit as one element.
302-
Characters in the version string beyond only
303-
numbers and dots are ignored, in line
304-
with the java.version system property.
325+
Gets the version of the JVM as a tuple, with each dot-separated digit
326+
as one element. Characters in the version string beyond only numbers
327+
and dots are ignored, in line with the java.version system property.
305328

306329
Examples:
307330
* OpenJDK 17.0.1 -> [17, 0, 1]
308331
* OpenJDK 11.0.9.1-internal -> [11, 0, 9, 1]
309332
* OpenJDK 1.8.0_312 -> [1, 8, 0]
310333

311-
If the JVM is already started,
312-
this function should return the equivalent of:
334+
If the JVM is already started, this function returns the equivalent of:
313335
jimport('java.lang.System')
314336
.getProperty('java.version')
315337
.split('.')
316338

317-
In case the JVM is not started yet,a best effort is made to deduce
339+
In case the JVM is not started yet, a best effort is made to deduce
318340
the version from the environment without actually starting up the
319341
JVM in-process. If the version cannot be deduced, a RuntimeError
320342
with the cause is raised.
321343

344+
memory_max() -> int
345+
Get the maximum amount of memory that the JVM will attempt to use.
346+
347+
This number will always be greater than or equal to memory_total().
348+
349+
In case the JVM was configured with -Xmx flag upon startup (e.g. using
350+
the scyjava.config.set_heap_max function), the value will typically
351+
correspond approximately, but not exactly, to the configured value.
352+
353+
This function is a shortcut for Java's Runtime.getRuntime().maxMemory().
354+
355+
:return: The maximum memory in bytes.
356+
:raise RuntimeError: if the JVM has not yet been started.
357+
358+
memory_total() -> int
359+
Get the total amount of memory currently reserved by the JVM.
360+
361+
This number will always be less than or equal to memory_max().
362+
363+
In case the JVM was configured with -Xms flag upon startup (e.g. using
364+
the scyjava.config.set_heap_min function), the initial value will typically
365+
correspond approximately, but not exactly, to the configured value,
366+
although it is likely to grow over time as more Java objects are allocated.
367+
368+
This function is a shortcut for Java's Runtime.getRuntime().totalMemory().
369+
370+
:return: The total memory in bytes.
371+
:raise RuntimeError: if the JVM has not yet been started.
372+
373+
memory_used() -> int
374+
Get the amount of memory currently in use by the JVM.
375+
376+
This function is a shortcut for
377+
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory().
378+
379+
:return: The used memory in bytes.
380+
:raise RuntimeError: if the JVM has not yet been started.
381+
322382
shutdown_jvm() -> None
323383
Shutdown the JVM.
324384

@@ -338,6 +398,8 @@ FUNCTIONS
338398
Note that if the JVM is not already running, then this function does
339399
nothing! In particular, shutdown hooks are skipped in this situation.
340400

401+
:raises RuntimeError: if this method is called while in Jep mode.
402+
341403
start_jvm(options=None) -> None
342404
Explicitly connect to the Java virtual machine (JVM). Only one JVM can
343405
be active; does nothing if the JVM has already been started. Calling

dev-environment.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ channels:
1919
- conda-forge
2020
- defaults
2121
dependencies:
22-
- python >= 3.7
22+
- python >= 3.8
2323
# Project dependencies
2424
- jpype1 >= 1.3.0
2525
- jgo

environment.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ channels:
2020
- conda-forge
2121
- defaults
2222
dependencies:
23-
- python >= 3.7
23+
- python >= 3.8
2424
# Project dependencies
2525
- jpype1 >= 1.3.0
2626
- jgo

pyproject.toml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ classifiers = [
1616
"Intended Audience :: Education",
1717
"Intended Audience :: Science/Research",
1818
"Programming Language :: Python :: 3 :: Only",
19-
"Programming Language :: Python :: 3.7",
2019
"Programming Language :: Python :: 3.8",
2120
"Programming Language :: Python :: 3.9",
2221
"Programming Language :: Python :: 3.10",
@@ -31,7 +30,7 @@ classifiers = [
3130
]
3231

3332
# NB: Keep this in sync with environment.yml AND dev-environment.yml!
34-
requires-python = ">=3.7"
33+
requires-python = ">=3.8"
3534
dependencies = [
3635
"jpype1 >= 1.3.0",
3736
"jgo",
@@ -77,7 +76,7 @@ exclude = ["bin", "build", "dist"]
7776
extend-ignore = ["E203"]
7877
# See https://black.readthedocs.io/en/stable/guides/using_black_with_other_tools.html#flake8
7978
max-line-length = 88
80-
min_python_version = "3.7"
79+
min_python_version = "3.8"
8180

8281
[tool.isort]
8382
profile = "black"

src/scyjava/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@
9696
)
9797
from scyjava._java import ( # noqa: F401
9898
JavaClasses,
99+
available_processors,
100+
gc,
99101
is_awt_initialized,
100102
is_jarray,
101103
is_jvm_headless,
@@ -107,6 +109,9 @@
107109
jstacktrace,
108110
jvm_started,
109111
jvm_version,
112+
memory_max,
113+
memory_total,
114+
memory_used,
110115
shutdown_jvm,
111116
start_jvm,
112117
when_jvm_starts,

src/scyjava/_java.py

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def java_import(func: Callable[[], str]) -> Callable[[], jpype.JClass]:
6666
@property
6767
def inner(self):
6868
if not jvm_started():
69-
raise Exception()
69+
raise RuntimeError("JVM has not started yet!")
7070
try:
7171
return jimport(func(self))
7272
except TypeError:
@@ -302,6 +302,80 @@ def jvm_started() -> bool:
302302
return jpype.isJVMStarted()
303303

304304

305+
def gc() -> None:
306+
"""
307+
Do a round of Java garbage collection.
308+
309+
This function is a shortcut for Java's System.gc().
310+
311+
:raise RuntimeError: if the JVM has not yet been started.
312+
"""
313+
_jc.System.gc()
314+
315+
316+
def memory_total() -> int:
317+
"""
318+
Get the total amount of memory currently reserved by the JVM.
319+
320+
This number will always be less than or equal to memory_max().
321+
322+
In case the JVM was configured with -Xms flag upon startup (e.g. using
323+
the scyjava.config.set_heap_min function), the initial value will typically
324+
correspond approximately, but not exactly, to the configured value,
325+
although it is likely to grow over time as more Java objects are allocated.
326+
327+
This function is a shortcut for Java's Runtime.getRuntime().totalMemory().
328+
329+
:return: The total memory in bytes.
330+
:raise RuntimeError: if the JVM has not yet been started.
331+
"""
332+
return int(_jc.Runtime.getRuntime().totalMemory())
333+
334+
335+
def memory_max() -> int:
336+
"""
337+
Get the maximum amount of memory that the JVM will attempt to use.
338+
339+
This number will always be greater than or equal to memory_total().
340+
341+
In case the JVM was configured with -Xmx flag upon startup (e.g. using
342+
the scyjava.config.set_heap_max function), the value will typically
343+
correspond approximately, but not exactly, to the configured value.
344+
345+
This function is a shortcut for Java's Runtime.getRuntime().maxMemory().
346+
347+
:return: The maximum memory in bytes.
348+
:raise RuntimeError: if the JVM has not yet been started.
349+
"""
350+
return int(_jc.Runtime.getRuntime().maxMemory())
351+
352+
353+
def memory_used() -> int:
354+
"""
355+
Get the amount of memory currently in use by the JVM.
356+
357+
This function is a shortcut for
358+
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory().
359+
360+
:return: The used memory in bytes.
361+
:raise RuntimeError: if the JVM has not yet been started.
362+
"""
363+
return memory_total() - int(_jc.Runtime.getRuntime().freeMemory())
364+
365+
366+
def available_processors() -> int:
367+
"""
368+
Get the number of processors available to the JVM.
369+
370+
This function is a shortcut for Java's
371+
Runtime.getRuntime().availableProcessors().
372+
373+
:return: The number of available processors.
374+
:raise RuntimeError: if the JVM has not yet been started.
375+
"""
376+
return int(_jc.Runtime.getRuntime().availableProcessors())
377+
378+
305379
def is_jvm_headless() -> bool:
306380
"""
307381
Return true iff Java is running in headless mode.
@@ -575,3 +649,15 @@ def jarray(kind, lengths: Sequence):
575649
for i in range(len(arr)):
576650
arr[i] = jarray(kind, lengths[1:])
577651
return arr
652+
653+
654+
# fmt: off
655+
class _JavaClasses(JavaClasses):
656+
@JavaClasses.java_import
657+
def Runtime(self): return "java.lang.Runtime" # noqa: E272
658+
@JavaClasses.java_import
659+
def System(self): return "java.lang.System" # noqa: E272
660+
# fmt: on
661+
662+
663+
_jc = _JavaClasses()

src/scyjava/_versions.py

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"""
44

55
import logging
6-
from importlib.util import find_spec
6+
from importlib.metadata import version
77

88
from ._java import isjava, jimport
99

@@ -32,20 +32,7 @@ def get_version(java_class_or_python_package) -> str:
3232
return str(VersionUtils.getVersion(java_class_or_python_package))
3333

3434
# Assume we were given a Python package name.
35-
36-
if find_spec("importlib.metadata"):
37-
# Fastest, but requires Python 3.8+.
38-
from importlib.metadata import version
39-
40-
return version(java_class_or_python_package)
41-
42-
if find_spec("pkg_resources"):
43-
# Slower, but works on Python 3.7.
44-
from pkg_resources import get_distribution
45-
46-
return get_distribution(java_class_or_python_package).version
47-
48-
raise RuntimeError("Cannot determine version! Is pkg_resources installed?")
35+
return version(java_class_or_python_package)
4936

5037

5138
def is_version_at_least(actual_version: str, minimum_version: str) -> bool:

0 commit comments

Comments
 (0)