15
15
# See NOTICE file for details.
16
16
#
17
17
# *****************************************************************************
18
- import sys
18
+ from __future__ import annotations
19
+
19
20
import atexit
21
+ import os
22
+ import sys
23
+ import typing
24
+
20
25
import _jpype
21
26
from . import types as _jtypes
22
27
from . import _classpath
@@ -53,6 +58,10 @@ class JVMNotRunning(RuntimeError):
53
58
pass
54
59
55
60
61
+ if typing .TYPE_CHECKING :
62
+ _PathOrStr = typing .Union [str , os .PathLike ]
63
+
64
+
56
65
# See http://scottlobdell.me/2015/04/decorators-arguments-python/
57
66
def deprecated (* args ):
58
67
""" Marks a function a deprecated when used as decorator.
@@ -96,23 +105,48 @@ def isJVMStarted():
96
105
return _jpype .isStarted ()
97
106
98
107
99
- def _hasClassPath (args ) :
108
+ def _hasClassPath (args : typing . Tuple [ _PathOrStr , ...]) -> bool :
100
109
for i in args :
101
- if i .startswith ('-Djava.class.path' ):
110
+ if isinstance ( i , str ) and i .startswith ('-Djava.class.path' ):
102
111
return True
103
112
return False
104
113
105
114
106
- def _handleClassPath (clsList ):
115
+ def _handleClassPath (
116
+ classpath : typing .Union [
117
+ _PathOrStr ,
118
+ typing .Tuple [_PathOrStr , ...]
119
+ ],
120
+ ) -> str :
121
+ """
122
+ Return a classpath which represents the given tuple of classpath specifications
123
+ """
107
124
out = []
108
- for s in clsList :
109
- if not isinstance (s , str ):
110
- raise TypeError ("Classpath elements must be strings" )
111
- if s .endswith ('*' ):
125
+
126
+ if isinstance (classpath , (str , os .PathLike )):
127
+ classpath = (classpath ,)
128
+ try :
129
+ # Convert anything iterable into a tuple.
130
+ classpath = tuple (classpath )
131
+ except TypeError :
132
+ raise TypeError ("Unknown class path element" )
133
+
134
+ for element in classpath :
135
+ try :
136
+ pth = os .fspath (element )
137
+ except TypeError as err :
138
+ raise TypeError ("Classpath elements must be strings or Path-like" ) from err
139
+
140
+ if isinstance (pth , bytes ):
141
+ # In the future we may allow this to support Paths which are undecodable.
142
+ # https://docs.python.org/3/howto/unicode.html#unicode-filenames.
143
+ raise TypeError ("Classpath elements must be strings or Path-like" )
144
+
145
+ if pth .endswith ('*' ):
112
146
import glob
113
- out .extend (glob .glob (s + '.jar' ))
147
+ out .extend (glob .glob (pth + '.jar' ))
114
148
else :
115
- out .append (s )
149
+ out .append (pth )
116
150
return _classpath ._SEP .join (out )
117
151
118
152
@@ -123,7 +157,14 @@ def interactive():
123
157
return bool (getattr (sys , 'ps1' , sys .flags .interactive ))
124
158
125
159
126
- def startJVM (* args , ** kwargs ):
160
+ def startJVM (
161
+ * jvmargs : str ,
162
+ jvmpath : typing .Optional [_PathOrStr ] = None ,
163
+ classpath : typing .Optional [typing .Sequence [_PathOrStr ], _PathOrStr ] = None ,
164
+ ignoreUnrecognized : bool = False ,
165
+ convertStrings : bool = False ,
166
+ interrupt : bool = not interactive (),
167
+ ) -> None :
127
168
"""
128
169
Starts a Java Virtual Machine. Without options it will start
129
170
the JVM with the default classpath and jvmpath.
@@ -132,14 +173,14 @@ def startJVM(*args, **kwargs):
132
173
The default jvmpath is determined by ``jpype.getDefaultJVMPath()``.
133
174
134
175
Parameters:
135
- *args (Optional, str[]): Arguments to give to the JVM.
136
- The first argument may be the path the JVM.
176
+ *jvmargs (Optional, str[]): Arguments to give to the JVM.
177
+ The first argument may be the path to the JVM.
137
178
138
179
Keyword Arguments:
139
- jvmpath (str): Path to the jvm library file,
180
+ jvmpath (str, PathLike ): Path to the jvm library file,
140
181
Typically one of (``libjvm.so``, ``jvm.dll``, ...)
141
182
Using None will apply the default jvmpath.
142
- classpath (str,[str]): Set the classpath for the JVM.
183
+ classpath (str, PathLike, [str, PathLike ]): Set the classpath for the JVM.
143
184
This will override any classpath supplied in the arguments
144
185
list. A value of None will give no classpath to JVM.
145
186
ignoreUnrecognized (bool): Option to ignore
@@ -158,8 +199,7 @@ def startJVM(*args, **kwargs):
158
199
159
200
Raises:
160
201
OSError: if the JVM cannot be started or is already running.
161
- TypeError: if an invalid keyword argument is supplied
162
- or a keyword argument conflicts with the arguments.
202
+ TypeError: if a keyword argument conflicts with the positional arguments.
163
203
164
204
"""
165
205
if _jpype .isStarted ():
@@ -168,51 +208,36 @@ def startJVM(*args, **kwargs):
168
208
if _JVM_started :
169
209
raise OSError ('JVM cannot be restarted' )
170
210
171
- args = list (args )
172
-
173
211
# JVM path
174
- jvmpath = None
175
- if args :
212
+ if jvmargs :
176
213
# jvm is the first argument the first argument is a path or None
177
- if not args [0 ] or not args [0 ].startswith ('-' ):
178
- jvmpath = args . pop ( 0 )
179
- if 'jvmpath' in kwargs :
180
- if jvmpath :
181
- raise TypeError ( 'jvmpath specified twice' )
182
- jvmpath = kwargs . pop ( 'jvmpath' )
214
+ if jvmargs [0 ] is None or ( isinstance ( jvmargs [ 0 ], str ) and not jvmargs [0 ].startswith ('-' ) ):
215
+ if jvmpath :
216
+ raise TypeError ( 'jvmpath specified twice' )
217
+ jvmpath = jvmargs [ 0 ]
218
+ jvmargs = jvmargs [ 1 :]
219
+
183
220
if not jvmpath :
184
221
jvmpath = getDefaultJVMPath ()
222
+ else :
223
+ # Allow the path to be a PathLike.
224
+ jvmpath = os .fspath (jvmpath )
225
+
226
+ extra_jvm_args : typing .Tuple [str , ...] = tuple ()
185
227
186
228
# Classpath handling
187
- if _hasClassPath (args ):
229
+ if _hasClassPath (jvmargs ):
188
230
# Old style, specified in the arguments
189
- if ' classpath' in kwargs :
231
+ if classpath is not None :
190
232
# Cannot apply both styles, conflict
191
233
raise TypeError ('classpath specified twice' )
192
- classpath = None
193
- elif 'classpath' in kwargs :
194
- # New style, as a keywork
195
- classpath = kwargs .pop ('classpath' )
196
- else :
197
- # Not speficied at all, use the default classpath
234
+ elif classpath is None :
235
+ # Not specified at all, use the default classpath.
198
236
classpath = _classpath .getClassPath ()
199
237
200
238
# Handle strings and list of strings.
201
239
if classpath :
202
- if isinstance (classpath , str ):
203
- args .append ('-Djava.class.path=%s' % _handleClassPath ([classpath ]))
204
- elif hasattr (classpath , '__iter__' ):
205
- args .append ('-Djava.class.path=%s' % _handleClassPath (classpath ))
206
- else :
207
- raise TypeError ("Unknown class path element" )
208
-
209
- ignoreUnrecognized = kwargs .pop ('ignoreUnrecognized' , False )
210
- convertStrings = kwargs .pop ('convertStrings' , False )
211
- interrupt = kwargs .pop ('interrupt' , not interactive ())
212
-
213
- if kwargs :
214
- raise TypeError ("startJVM() got an unexpected keyword argument '%s'"
215
- % (',' .join ([str (i ) for i in kwargs ])))
240
+ extra_jvm_args += (f'-Djava.class.path={ _handleClassPath (classpath )} ' , )
216
241
217
242
try :
218
243
import locale
@@ -221,7 +246,7 @@ def startJVM(*args, **kwargs):
221
246
# Keep the current locale settings, else Java will replace them.
222
247
prior = [locale .getlocale (i ) for i in categories ]
223
248
# Start the JVM
224
- _jpype .startup (jvmpath , tuple ( args ) ,
249
+ _jpype .startup (jvmpath , jvmargs + extra_jvm_args ,
225
250
ignoreUnrecognized , convertStrings , interrupt )
226
251
# Collect required resources for operation
227
252
initializeResources ()
@@ -238,8 +263,7 @@ def startJVM(*args, **kwargs):
238
263
match = re .search (r"([0-9]+)\.[0-9]+" , source )
239
264
if match :
240
265
version = int (match .group (1 )) - 44
241
- raise RuntimeError ("%s is older than required Java version %d" % (
242
- jvmpath , version )) from ex
266
+ raise RuntimeError (f"{ jvmpath } is older than required Java version{ version } " ) from ex
243
267
raise
244
268
245
269
0 commit comments