3
3
"""
4
4
5
5
import collections
6
- import typing
6
+ import inspect
7
+ import math
7
8
from pathlib import Path
8
- from typing import Any , Callable , NamedTuple
9
+ from typing import Any , Callable , List , NamedTuple
9
10
10
11
from jpype import JArray , JBoolean , JByte , JChar , JDouble , JFloat , JInt , JLong , JShort
11
12
12
- from ._java import JavaClasses , isjava , jclass , jimport , start_jvm
13
+ from ._java import JavaClasses , isjava , jclass , jimport , jinstance , start_jvm
13
14
14
15
15
16
# NB: We cannot use org.scijava.priority.Priority or other Java-side class
@@ -27,16 +28,37 @@ class Priority:
27
28
LAST = - 1e300
28
29
29
30
31
+ def _has_kwargs (f ):
32
+ return not isjava (f ) and any (
33
+ p .kind == inspect .Parameter .VAR_KEYWORD
34
+ for p in inspect .signature (f ).parameters .values ()
35
+ )
36
+
37
+
30
38
class Converter (NamedTuple ):
31
39
predicate : Callable [[Any ], bool ]
32
40
converter : Callable [[Any ], Any ]
33
41
priority : float = Priority .NORMAL
34
42
43
+ def supports (self , obj : Any , ** hints : dict ) -> bool :
44
+ return (
45
+ self .predicate (obj , ** hints )
46
+ if _has_kwargs (self .predicate )
47
+ else self .predicate (obj )
48
+ )
49
+
50
+ def convert (self , obj : Any , ** hints : dict ) -> Any :
51
+ return (
52
+ self .converter (obj , ** hints )
53
+ if _has_kwargs (self .converter )
54
+ else self .converter (obj )
55
+ )
56
+
35
57
36
- def _convert (obj : Any , converters : typing . List [Converter ]) -> Any :
37
- suitable_converters = filter ( lambda c : c . predicate (obj ), converters )
58
+ def _convert (obj : Any , converters : List [Converter ], ** hints : dict ) -> Any :
59
+ suitable_converters = [ c for c in converters if c . supports (obj , ** hints )]
38
60
prioritized = max (suitable_converters , key = lambda c : c .priority )
39
- return prioritized .converter (obj )
61
+ return prioritized .convert (obj , ** hints )
40
62
41
63
42
64
# -- Python to Java --
@@ -74,18 +96,18 @@ def _convertIterable(obj: collections.abc.Iterable):
74
96
return jlist
75
97
76
98
77
- java_converters : typing . List [Converter ] = []
99
+ java_converters : List [Converter ] = []
78
100
79
101
80
102
def add_java_converter (converter : Converter ):
81
103
"""
82
- Adds a converter to the list used by to_java
104
+ Add a converter to the list used by to_java.
83
105
:param converter: A Converter going from python to java
84
106
"""
85
107
java_converters .append (converter )
86
108
87
109
88
- def to_java (obj : Any ) -> Any :
110
+ def to_java (obj : Any , ** hints : dict ) -> Any :
89
111
"""
90
112
Recursively convert a Python object to a Java object.
91
113
@@ -103,15 +125,15 @@ def to_java(obj: Any) -> Any:
103
125
:raises TypeError: if the argument is not one of the aforementioned types.
104
126
"""
105
127
start_jvm ()
106
- return _convert (obj , java_converters )
128
+ return _convert (obj , java_converters , ** hints )
107
129
108
130
109
- def _stock_java_converters () -> typing . List [Converter ]:
131
+ def _stock_java_converters () -> List [Converter ]:
110
132
"""
111
- Returns all python-to-java converters supported out of the box!
112
- This should only be called after the JVM has been started!
133
+ Construct the Python-to-Java converters supported out of the box.
113
134
:returns: A list of Converters
114
135
"""
136
+ start_jvm ()
115
137
return [
116
138
# Other (Exceptional) converter
117
139
Converter (
@@ -141,15 +163,33 @@ def _stock_java_converters() -> typing.List[Converter]:
141
163
predicate = lambda obj : isinstance (obj , bool ),
142
164
converter = _jc .Boolean ,
143
165
),
166
+ # int -> java.lang.Byte
167
+ Converter (
168
+ predicate = lambda obj , ** hints : isinstance (obj , int )
169
+ and ("type" in hints and hints ["type" ] in ("b" , "byte" , "Byte" ))
170
+ and _jc .Byte .MIN_VALUE <= obj <= _jc .Byte .MAX_VALUE ,
171
+ converter = _jc .Byte ,
172
+ priority = Priority .HIGH ,
173
+ ),
174
+ # int -> java.lang.Short
175
+ Converter (
176
+ predicate = lambda obj , ** hints : isinstance (obj , int )
177
+ and ("type" in hints and hints ["type" ] in ("s" , "short" , "Short" ))
178
+ and _jc .Short .MIN_VALUE <= obj <= _jc .Short .MAX_VALUE ,
179
+ converter = _jc .Short ,
180
+ priority = Priority .HIGH ,
181
+ ),
144
182
# int -> java.lang.Integer
145
183
Converter (
146
- predicate = lambda obj : isinstance (obj , int )
184
+ predicate = lambda obj , ** hints : isinstance (obj , int )
185
+ and ("type" not in hints or hints ["type" ] in ("i" , "int" , "Integer" ))
147
186
and _jc .Integer .MIN_VALUE <= obj <= _jc .Integer .MAX_VALUE ,
148
187
converter = _jc .Integer ,
149
188
),
150
189
# int -> java.lang.Long
151
190
Converter (
152
- predicate = lambda obj : isinstance (obj , int )
191
+ predicate = lambda obj , ** hints : isinstance (obj , int )
192
+ and ("type" not in hints or hints ["type" ] in ("j" , "l" , "long" , "Long" ))
153
193
and _jc .Long .MIN_VALUE <= obj <= _jc .Long .MAX_VALUE ,
154
194
converter = _jc .Long ,
155
195
priority = Priority .NORMAL - 1 ,
@@ -162,14 +202,24 @@ def _stock_java_converters() -> typing.List[Converter]:
162
202
),
163
203
# float -> java.lang.Float
164
204
Converter (
165
- predicate = lambda obj : isinstance (obj , float )
166
- and _jc .Float .MIN_VALUE <= obj <= _jc .Float .MAX_VALUE ,
205
+ predicate = lambda obj , ** hints : isinstance (obj , float )
206
+ and ("type" not in hints or hints ["type" ] in ("f" , "float" , "Float" ))
207
+ and (
208
+ math .isinf (obj )
209
+ or math .isnan (obj )
210
+ or - _jc .Float .MAX_VALUE <= obj <= _jc .Float .MAX_VALUE
211
+ ),
167
212
converter = _jc .Float ,
168
213
),
169
214
# float -> java.lang.Double
170
215
Converter (
171
- predicate = lambda obj : isinstance (obj , float )
172
- and _jc .Double .MAX_VALUE <= obj <= _jc .Double .MAX_VALUE ,
216
+ predicate = lambda obj , ** hints : isinstance (obj , float )
217
+ and ("type" not in hints or hints ["type" ] in ("d" , "double" , "Double" ))
218
+ and (
219
+ math .isinf (obj )
220
+ or math .isnan (obj )
221
+ or - _jc .Double .MAX_VALUE <= obj <= _jc .Double .MAX_VALUE
222
+ ),
173
223
converter = _jc .Double ,
174
224
priority = Priority .NORMAL - 1 ,
175
225
),
@@ -377,12 +427,12 @@ def __str__(self):
377
427
return "{" + ", " .join (_jstr (v ) for v in self ) + "}"
378
428
379
429
380
- py_converters : typing . List [Converter ] = []
430
+ py_converters : List [Converter ] = []
381
431
382
432
383
433
def add_py_converter (converter : Converter ):
384
434
"""
385
- Adds a converter to the list used by to_python
435
+ Add a converter to the list used by to_python.
386
436
:param converter: A Converter from java to python
387
437
"""
388
438
py_converters .append (converter )
@@ -420,12 +470,12 @@ def to_python(data: Any, gentle: bool = False) -> Any:
420
470
raise exc
421
471
422
472
423
- def _stock_py_converters () -> typing . List :
473
+ def _stock_py_converters () -> List :
424
474
"""
425
- Returns all java-to-python converters supported out of the box!
426
- This should only be called after the JVM has been started!
475
+ Construct the Java-to-Python converters supported out of the box.
427
476
:returns: A list of Converters
428
477
"""
478
+ start_jvm ()
429
479
430
480
converters = [
431
481
# Other (Exceptional) converter
@@ -682,7 +732,7 @@ def _import_numpy(required=True):
682
732
def _is_table (obj : Any ) -> bool :
683
733
"""Check if obj is a table."""
684
734
try :
685
- return isinstance (obj , jimport ( "org.scijava.table.Table" ) )
735
+ return jinstance (obj , "org.scijava.table.Table" )
686
736
except BaseException :
687
737
# No worries if scijava-table is not available.
688
738
pass
0 commit comments