Skip to content

Commit e614bb9

Browse files
ctruedengselzer
andcommitted
Avoid converting objects when not necessary
This is accomplished by keeping the converters sorted by priority, then invoking them one by one in order until we find a working one. Co-authored-by: Gabriel Selzer <gjselzer@wisc.edu>
1 parent 9bab39d commit e614bb9

2 files changed

Lines changed: 36 additions & 5 deletions

File tree

src/scyjava/_convert.py

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import collections
66
import inspect
77
import math
8+
from bisect import insort
89
from pathlib import Path
910
from typing import Any, Callable, Dict, List, NamedTuple
1011

@@ -39,6 +40,10 @@ class Priority:
3940
LAST = -1e300
4041

4142

43+
def _priority(thing):
44+
return getattr(thing, "priority", Priority.NORMAL)
45+
46+
4247
def _has_kwargs(f):
4348
return not isjava(f) and any(
4449
p.kind == inspect.Parameter.VAR_KEYWORD
@@ -65,11 +70,27 @@ def convert(self, obj: Any, **hints: Dict) -> Any:
6570
else self.converter(obj)
6671
)
6772

73+
def __lt__(self, other):
74+
return self.priority < _priority(other)
75+
76+
def __le__(self, other):
77+
return self.priority <= _priority(other)
78+
79+
def __gt__(self, other):
80+
return self.priority > _priority(other)
81+
82+
def __ge__(self, other):
83+
return self.priority >= _priority(other)
84+
6885

6986
def _convert(obj: Any, converters: List[Converter], **hints: Dict) -> Any:
70-
suitable_converters = [c for c in converters if c.supports(obj, **hints)]
71-
prioritized = max(suitable_converters, key=lambda c: c.priority)
72-
return prioritized.convert(obj, **hints)
87+
# NB: The given converters are assumed to be sorted ascending by priority,
88+
# meaning lower-priority items appear earlier than higher-priority ones.
89+
# But we want to try the higher priority converters first, so we
90+
# need to iterate the given converters list starting at the end.
91+
for converter in reversed(converters):
92+
if converter.supports(obj, **hints):
93+
return converter.convert(obj, **hints)
7394

7495

7596
# -- Python to Java --
@@ -115,7 +136,7 @@ def add_java_converter(converter: Converter) -> None:
115136
Add a converter to the list used by to_java.
116137
:param converter: A Converter going from python to java
117138
"""
118-
java_converters.append(converter)
139+
insort(java_converters, converter)
119140

120141

121142
def to_java(obj: Any, **hints: Dict) -> Any:
@@ -204,6 +225,9 @@ def _stock_java_converters() -> List[Converter]:
204225
Converter(
205226
predicate=lambda obj: isinstance(obj, bool),
206227
converter=_jc.Boolean,
228+
# NB: Must be higher priority than the int converters,
229+
# because the bool type extends the int type!
230+
priority=Priority.NORMAL + 1,
207231
),
208232
# int -> java.lang.Byte
209233
Converter(
@@ -486,7 +510,7 @@ def add_py_converter(converter: Converter) -> None:
486510
Add a converter to the list used by to_python.
487511
:param converter: A Converter from java to python
488512
"""
489-
py_converters.append(converter)
513+
insort(py_converters, converter)
490514

491515

492516
def to_python(data: Any, gentle: bool = False) -> Any:

tests/test_convert.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
jclass,
1515
jimport,
1616
jinstance,
17+
py_converters,
1718
to_java,
1819
to_python,
1920
)
@@ -355,3 +356,9 @@ def test_conversion_priority(self):
355356
assert e == a
356357

357358
java_converters.remove(bad_converter)
359+
360+
def test_converter_priority(self):
361+
assert len(java_converters) > 0
362+
assert sorted(java_converters) == java_converters
363+
assert len(py_converters) > 0
364+
assert sorted(py_converters) == py_converters

0 commit comments

Comments
 (0)