GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing github.com — scyjava/src/scyjava/_convert.py at main · scijava/scyjava Expand file tree/ Copy path_convert.pyMore file actions 965 lines (825 loc) · 31.2 KB / Copy path_convert.pyFile metadata and controls965 lines (825 loc) · 31.2 KB 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 """ The scyjava conversion subsystem, and built-in conversion functions. """ import collections import inspect import logging import math from bisect import insort from collections.abc import Callable from importlib.util import find_spec from pathlib import Path from typing import Any, NamedTuple from jpype import JBoolean, JByte, JChar, JDouble, JFloat, JInt, JLong, JShort from scyjava._jvm import jimport, start_jvm from scyjava._types import JavaClasses, is_jarray, isjava, jarray, jclass, jinstance from scyjava.config import Mode, mode _logger = logging.getLogger(__name__) # NB: We cannot use org.scijava.priority.Priority or other Java-side class # here because we don't want to impose Java-side dependencies, and we don't # want to require Java to be started before reasoning about priorities. class Priority: FIRST = 1e300 EXTREMELY_HIGH = 1e6 VERY_HIGH = 1e4 HIGH = 1e2 NORMAL = 0 LOW = -1e2 VERY_LOW = -1e4 EXTREMELY_LOW = -1e6 LAST = -1e300 def _priority(thing): return getattr(thing, "priority", Priority.NORMAL) def _has_kwargs(f): return not isjava(f) and any( p.kind == inspect.Parameter.VAR_KEYWORD for p in inspect.signature(f).parameters.values() ) class Converter(NamedTuple): predicate: Callable[[Any], bool] converter: Callable[[Any], Any] priority: float = Priority.NORMAL name: str = "<unnamed>" def supports(self, obj: Any, **hints: dict) -> bool: return ( self.predicate(obj, **hints) if _has_kwargs(self.predicate) else self.predicate(obj) ) def convert(self, obj: Any, **hints: dict) -> Any: return ( self.converter(obj, **hints) if _has_kwargs(self.converter) else self.converter(obj) ) def __lt__(self, other): return self.priority < _priority(other) def __le__(self, other): return self.priority <= _priority(other) def __gt__(self, other): return self.priority > _priority(other) def __ge__(self, other): return self.priority >= _priority(other) def __str__(self): return self.name def _convert(obj: Any, converters: list[Converter], **hints: dict) -> Any: # NB: The given converters are assumed to be sorted ascending by priority, # meaning lower-priority items appear earlier than higher-priority ones. # But we want to try the higher priority converters first, so we # need to iterate the given converters list starting at the end. debug = hints.get("debug", False) log = _logger.info if debug else _logger.debug log(f"Converting object of type {type(obj)} with hints {hints}") for converter in reversed(converters): if converter.supports(obj, **hints): log(f"- {converter} supports") return converter.convert(obj, **hints) log(f"- {converter} does not support") # -- Python to Java -- # Adapted from code posted by vslotman on GitHub: # https://github.com/kivy/pyjnius/issues/217#issue-145981070 def _raise_type_exception(obj: Any): raise TypeError("Unsupported type: " + str(type(obj))) def _convertMap(obj: collections.abc.Mapping): jmap = _jc.LinkedHashMap() for k, v in obj.items(): jk = to_java(k) jv = to_java(v) jmap.put(jk, jv) return jmap def _convertSet(obj: collections.abc.Set): jset = _jc.LinkedHashSet() for item in obj: jitem = to_java(item) jset.add(jitem) return jset def _convertIterable(obj: collections.abc.Iterable): jlist = _jc.ArrayList() for item in obj: jitem = to_java(item) jlist.add(jitem) return jlist java_converters: list[Converter] = [] def add_java_converter(converter: Converter) -> None: """ Add a converter to the list used by to_java. :param converter: A Converter going from python to java """ insort(java_converters, converter) def to_java(obj: Any, **hints: dict) -> Any: """ Recursively convert a Python object to a Java object. Supported types include: * str -> String * bool -> Boolean * int -> Integer, Long or BigInteger as appropriate * float -> Float, Double or BigDecimal as appropriate * dict -> LinkedHashMap * set -> LinkedHashSet * list -> ArrayList There is typically one single destination conversion type and value that makes sense. For example, Python str always converts to java.lang.String. But in some cases, there are multiple options that can be controlled by passing key/value pairs as hints. The base scyjava library includes: * int + type='byte' -> Byte * int + type='short' -> Short * int + type='int' -> Integer * int + type='long' -> Long * int + type='bigint' -> BigInteger * float + type='float' -> Float * float + type='double' -> Double * float + type='bigdec' -> BigDecimal But the scyjava conversion framework is extensible and other packages may introduce converters supporting additional hints. In the absence of a hint, scyjava makes a best effort to use a sensible destination type and value: * int values in [-2**31, 2**31-1] convert to Integer * int values in [-2**63, 2**63-1] but outside int range convert to Long * int values outside Java long range convert to BigInteger * conversion of int to Byte or Short must be requested via a hint * float values in Float range convert to Float * float inf, -inf, and nan convert to Float * float values in Double range but outside float range convert to Double * float values outside double range convert to BigDecimal :param obj: The Python object to convert. :param hints: An optional dictionary of hints, to help scyjava make decisions about how to do the conversion. :return: A corresponding Java object with the same contents. :raise TypeError: if the argument is not one of the aforementioned types. """ start_jvm() return _convert(obj, java_converters, **hints) def _stock_java_converters() -> list[Converter]: """ Construct the Python-to-Java converters supported out of the box. :return: A list of Converters """ start_jvm() return [ Converter( name="Other (Exceptional) converter", predicate=lambda obj: True, converter=_raise_type_exception, priority=Priority.EXTREMELY_LOW - 1, ), Converter( name="None -> None", predicate=lambda obj: obj is None, converter=lambda obj: None, priority=Priority.EXTREMELY_HIGH + 1, ), Converter( name="Java object identity", predicate=isjava, converter=lambda obj: obj, priority=Priority.EXTREMELY_HIGH, ), Converter( name="str -> java.lang.String", predicate=lambda obj: isinstance(obj, str), converter=lambda obj: _jc.String(obj.encode("utf-8"), "utf-8"), ), Converter( name="bool -> java.lang.Boolean", predicate=lambda obj: isinstance(obj, bool), converter=_jc.Boolean, # NB: Must be higher priority than the int converters, # because the bool type extends the int type! priority=Priority.NORMAL + 1, ), Converter( name="int -> java.lang.Byte", predicate=lambda obj, **hints: ( isinstance(obj, int) and ("type" in hints and hints["type"] in ("b", "byte", "Byte")) and _jc.Byte.MIN_VALUE <= obj <= _jc.Byte.MAX_VALUE ), converter=_jc.Byte, priority=Priority.HIGH, ), Converter( name="int -> java.lang.Short", predicate=lambda obj, **hints: ( isinstance(obj, int) and ("type" in hints and hints["type"] in ("s", "short", "Short")) and _jc.Short.MIN_VALUE <= obj <= _jc.Short.MAX_VALUE ), converter=_jc.Short, priority=Priority.HIGH, ), Converter( name="int -> java.lang.Integer", predicate=lambda obj, **hints: ( isinstance(obj, int) and ("type" not in hints or hints["type"] in ("i", "int", "Integer")) and _jc.Integer.MIN_VALUE <= obj <= _jc.Integer.MAX_VALUE ), converter=_jc.Integer, ), Converter( name="int -> java.lang.Long", predicate=lambda obj, **hints: ( isinstance(obj, int) and ("type" not in hints or hints["type"] in ("j", "l", "long", "Long")) and _jc.Long.MIN_VALUE <= obj <= _jc.Long.MAX_VALUE ), converter=_jc.Long, priority=Priority.NORMAL - 1, ), Converter( name="int -> java.math.BigInteger", predicate=lambda obj, **hints: ( isinstance(obj, int) and ( "type" not in hints or hints["type"] in ("bi", "bigint", "BigInteger") ) ), converter=lambda obj: _jc.BigInteger(str(obj)), priority=Priority.NORMAL - 2, ), Converter( name="float -> java.lang.Float", predicate=lambda obj, **hints: ( isinstance(obj, float) and ("type" not in hints or hints["type"] in ("f", "float", "Float")) and ( math.isinf(obj) or math.isnan(obj) or -_jc.Float.MAX_VALUE <= obj <= _jc.Float.MAX_VALUE ) ), converter=_jc.Float, ), Converter( name="float -> java.lang.Double", predicate=lambda obj, **hints: ( isinstance(obj, float) and ("type" not in hints or hints["type"] in ("d", "double", "Double")) and ( math.isinf(obj) or math.isnan(obj) or -_jc.Double.MAX_VALUE <= obj <= _jc.Double.MAX_VALUE ) ), converter=_jc.Double, priority=Priority.NORMAL - 1, ), Converter( name="float -> java.math.BigDecimal", predicate=lambda obj, **hints: ( isinstance(obj, float) and ( "type" not in hints or hints["type"] in ("bd", "bigdec", "BigDecimal") ) ), converter=lambda obj: _jc.BigDecimal(str(obj)), priority=Priority.NORMAL - 2, ), Converter( name="pathlib.Path -> java.nio.file.Path", predicate=lambda obj: isinstance(obj, Path), # Pass an empty String array in addition to our path # To make it clear to jep that we want the string-args version # JPype is smart enough to know that, but it doesn't mind the extra args converter=lambda obj: _jc.Paths.get(str(obj), jarray(_jc.String, [0])), priority=Priority.NORMAL + 1, ), Converter( name="pandas.DataFrame -> org.scijava.table.Table", predicate=lambda obj: type(obj).__name__ == "DataFrame", converter=_pandas_to_table, priority=Priority.NORMAL + 1, ), Converter( name="collections.abc.Mapping -> java.util.Map", predicate=lambda obj: isinstance(obj, collections.abc.Mapping), converter=_convertMap, ), Converter( name="collections.abc.Set -> java.util.Set", predicate=lambda obj: isinstance(obj, collections.abc.Set), converter=_convertSet, ), Converter( name="collections.abc.Iterable -> java.util.Iterable", predicate=lambda obj: isinstance(obj, collections.abc.Iterable), converter=_convertIterable, priority=Priority.NORMAL - 1, ), ] # -- Java to Python -- def _jstr(data): if isinstance(data, JavaObject): return str(data) # NB: We want Python strings to render in single quotes. return f"{data!r}" class JavaObject: def __init__(self, jobj, intended_class=None): if intended_class is None: intended_class = _jc.Object if not jinstance(jobj, intended_class): raise TypeError( f"Not a {intended_class.getName()}: {jclass(jobj).getName()}" ) self.jobj = jobj def __str__(self): return _jstr(self.jobj) class JavaIterable(JavaObject, collections.abc.Iterable): def __init__(self, jobj): JavaObject.__init__(self, jobj, _jc.Iterable) def __iter__(self): return to_python(self.jobj.iterator()) def __str__(self): return "[" + ", ".join(_jstr(v) for v in self) + "]" class JavaCollection(JavaIterable, collections.abc.Collection): def __init__(self, jobj): JavaObject.__init__(self, jobj, _jc.Collection) def __contains__(self, item): # NB: Collection.contains returns boolean, so no need for gentleness. return to_python(self.jobj.contains(to_java(item))) def __len__(self): return to_python(self.jobj.size()) def __eq__(self, other): try: if len(self) != len(other): return False for e1, e2 in zip(self, other): if e1 != e2: return False return True except TypeError: return False class JavaIterator(JavaObject, collections.abc.Iterator): def __init__(self, jobj): JavaObject.__init__(self, jobj, _jc.Iterator) def __next__(self): if self.jobj.hasNext(): # NB: Even if an element cannot be converted, # we still want to support Pythonic iteration. return to_python(self.jobj.next(), gentle=True) raise StopIteration class JavaList(JavaCollection, collections.abc.MutableSequence): def __init__(self, jobj): JavaObject.__init__(self, jobj, _jc.List) def __getitem__(self, key): # NB: Even if an element cannot be converted, # we still want Pythonic access to elements. return to_python(self.jobj.get(key), gentle=True) def __setitem__(self, key, value): # NB: List.set(int, Object) returns inserted element; be gentle here. return to_python(self.jobj.set(key, to_java(value)), gentle=True) def __delitem__(self, key): # NB: List.remove(Object) returns boolean, so no need for gentleness. return to_python(self.jobj.remove(to_java(key))) def insert(self, index, object): # NB: List.set(int, Object) returns inserted element; be gentle here. return to_python(self.jobj.set(index, to_java(object)), gentle=True) class JavaMap(JavaObject, collections.abc.MutableMapping): def __init__(self, jobj): JavaObject.__init__(self, jobj, _jc.Map) def __getitem__(self, key): # NB: Even if an element cannot be converted, # we still want Pythonic access to elements. return to_python(self.jobj.get(to_java(key)), gentle=True) def __setitem__(self, key, value): # NB: Map.put(Object, Object) returns inserted value; be gentle here. put_return: bool = self.jobj.put(to_java(key), to_java(value)) return to_python(put_return, gentle=True) def __delitem__(self, key): # NB: Map.remove(Object) returns the removed key; be gentle here. return to_python(self.jobj.remove(to_java(key)), gentle=True) def keys(self): return to_python(self.jobj.keySet()) def __iter__(self): return self.keys().__iter__() def __len__(self): return to_python(self.jobj.size()) def __eq__(self, other): try: if len(self) != len(other): return False for k in self: if k not in other or self[k] != other[k]: return False return True except TypeError: return False def __str__(self): def item_str(k, v): return _jstr(k) + ": " + _jstr(v) return "{" + ", ".join(item_str(k, v) for k, v in self.items()) + "}" class JavaSet(JavaCollection, collections.abc.MutableSet): def __init__(self, jobj): JavaObject.__init__(self, jobj, _jc.Set) def add(self, item): # NB: Set.add returns boolean, so no need for gentleness. return to_python(self.jobj.add(to_java(item))) def discard(self, item): # NB: Set.remove returns boolean, so no need for gentleness. return to_python(self.jobj.remove(to_java(item))) def __iter__(self): return to_python(self.jobj.iterator()) def __eq__(self, other): try: if len(self) != len(other): return False for k in self: if k not in other: return False return True except TypeError: return False def __str__(self): return "{" + ", ".join(_jstr(v) for v in self) + "}" py_converters: list[Converter] = [] def add_py_converter(converter: Converter) -> None: """ Add a converter to the list used by to_python. :param converter: A Converter from java to python """ insort(py_converters, converter) def to_python(data: Any, gentle: bool = False) -> Any: """ Recursively convert a Java object to a Python object. Supported types include: * String, Character -> str * Boolean -> bool * Byte, Short, Integer, Long, BigInteger -> int * Float, Double, BigDecimal -> float * Map -> collections.abc.MutableMapping (dict-like) * Set -> collections.abc.MutableSet (set-like) * List -> collections.abc.MutableSequence (list-like) * Collection -> collections.abc.Collection * Iterable -> collections.abc.Iterable * Iterator -> collections.abc.Iterator :param data: The Java object to convert. :param gentle: If set, and the type cannot be converted, leaves the data alone rather than raising a TypeError. :return: A corresponding Python object with the same contents. :raise TypeError: if the argument is not one of the aforementioned types, and the gentle flag is not set. """ start_jvm() try: return _convert(data, py_converters) except TypeError: if gentle: return data raise def _stock_py_converters() -> list: """ Construct the Java-to-Python converters supported out of the box. :return: A list of Converters """ start_jvm() converters = [ Converter( name="Other (Exceptional) converter", predicate=lambda obj: True, converter=_raise_type_exception, priority=Priority.EXTREMELY_LOW - 1, ), Converter( name="Python object identity", predicate=lambda obj: not isjava(obj), converter=lambda obj: obj, priority=Priority.EXTREMELY_HIGH, ), Converter( name="java.lang.Boolean -> bool", predicate=lambda obj: jinstance(obj, _jc.Boolean), converter=lambda obj: obj.booleanValue(), ), Converter( name="java.lang.Byte -> int", predicate=lambda obj: jinstance(obj, _jc.Byte), converter=lambda obj: int(obj.byteValue()), ), Converter( name="java.lang.Character -> str", predicate=lambda obj: jinstance(obj, _jc.Character), converter=lambda obj: str, ), Converter( name="java.lang.Double -> float", predicate=lambda obj: jinstance(obj, _jc.Double), converter=lambda obj: float(obj.doubleValue()), ), Converter( name="java.lang.Float -> float", predicate=lambda obj: jinstance(obj, _jc.Float), converter=lambda obj: float(obj.floatValue()), ), Converter( name="java.lang.Integer -> int", predicate=lambda obj: jinstance(obj, _jc.Integer), converter=lambda obj: int(obj.intValue()), ), Converter( name="java.lang.Long -> int", predicate=lambda obj: jinstance(obj, _jc.Long), converter=lambda obj: int(obj.longValue()), ), Converter( name="java.lang.Short -> int", predicate=lambda obj: jinstance(obj, _jc.Short), converter=lambda obj: int(obj.shortValue()), ), Converter( name="java.lang.String -> str", predicate=lambda obj: jinstance(obj, _jc.String), converter=lambda obj: str(obj), ), Converter( name="java.math.BigInteger -> int", predicate=lambda obj: jinstance(obj, _jc.BigInteger), converter=lambda obj: int(str(obj)), ), Converter( name="java.math.BigDecimal -> float", predicate=lambda obj: jinstance(obj, _jc.BigDecimal), converter=lambda obj: float(str(obj)), ), Converter( name="java.util.List -> scyjava.JavaList (list-like)", predicate=lambda obj: jinstance(obj, _jc.List), converter=JavaList, ), Converter( name="java.util.Map -> scyjava.JavaMap (dict-like)", predicate=lambda obj: jinstance(obj, _jc.Map), converter=JavaMap, ), Converter( name="java.util.Set -> scyjava.JavaSet (set-like)", predicate=lambda obj: jinstance(obj, _jc.Set), converter=JavaSet, ), Converter( name="java.util.Collection -> " "scyjava.JavaCollection (collections.abc.Collection)", predicate=lambda obj: jinstance(obj, _jc.Collection), converter=JavaCollection, priority=Priority.NORMAL - 1, ), Converter( name="java.lang.Iterable -> " "scyjava.JavaIterable (collections.abc.Iterable)", predicate=lambda obj: jinstance(obj, _jc.Iterable), converter=JavaIterable, priority=Priority.NORMAL - 1, ), Converter( name="java.util.Iterator -> " "scyjava.JavaIterator (collections.abc.Iterator)", predicate=lambda obj: jinstance(obj, _jc.Iterator), converter=JavaIterator, priority=Priority.NORMAL - 1, ), Converter( name="java.nio.file.Path -> pathlib.Path", predicate=lambda obj: jinstance(obj, _jc.Path), converter=lambda obj: Path(str(obj)), priority=Priority.NORMAL + 1, ), Converter( name="jarray -> list", predicate=lambda obj: is_jarray(obj), converter=lambda obj: [to_python(o) for o in obj], priority=Priority.VERY_LOW, ), ] if find_spec("pandas"): converters.append( Converter( name="org.scijava.table.Table -> pandas.DataFrame", predicate=_is_table, converter=_convert_table, priority=Priority.HIGH, ) ) if mode == Mode.JPYPE: converters.extend( [ Converter( name="JBoolean -> bool", predicate=lambda obj: isinstance(obj, JBoolean), converter=bool, priority=Priority.NORMAL + 1, ), Converter( name="JByte/JInt/JLong/JShort -> int", predicate=lambda obj: isinstance(obj, (JByte, JInt, JLong, JShort)), converter=int, priority=Priority.NORMAL + 1, ), Converter( name="JDouble/JFloat -> float", predicate=lambda obj: isinstance(obj, (JDouble, JFloat)), converter=float, priority=Priority.NORMAL + 1, ), Converter( name="JChar -> str", predicate=lambda obj: isinstance(obj, JChar), converter=str, priority=Priority.NORMAL + 1, ), ] ) if find_spec("numpy"): converters.append( Converter( name="primitive array -> numpy.ndarray", predicate=_supports_jarray_to_ndarray, converter=_jarray_to_ndarray, ) ) return converters ############################### # Java array -> NumPy ndarray # ############################### def _jarray_to_ndarray(jarr): """ Convert the given Java primitive array into a NumPy ndarray. :param jarr: The Java primitive array :return: The converted NumPy ndarray """ np = _import_numpy() assert _supports_jarray_to_ndarray(jarr) element_type = _jarray_element_type(jarr) # fmt: off jarraytype_map = { JBoolean: np.bool_, JByte: np.int8, # JChar: np.???, JDouble: np.float64, JFloat: np.float32, JInt: np.int32, JLong: np.int64, JShort: np.int16, } # fmt: on dtype = jarraytype_map[element_type] # Use a bytearray instead of memoryview for np.frombuffer. # Casting memoryview() on a Java array copies the array's content # into a buffer which does not get released when a new view is # requested. If the Java array's data changes a new memoryview will # contain the old buffer data. The view and any object created with # it must be deleted (del or =None) to release the buffer before # requesting a new view. Instead of utilizing the buffer # memoryview creates of the Java array, we obtain the buffer ouselves # as a mutable bytearray. bb = bytearray(jarr) ndarray = np.frombuffer(bb, dtype=dtype) del bb # release the buffer return ndarray.reshape(_jarray_shape(jarr)) def _supports_jarray_to_ndarray(obj): """ Return True iff the given object is convertible to a NumPy ndarray via the _jarray_to_ndarray function. :param obj: The object to check for convertibility :return: True iff conversion to a NumPy ndarray is possible """ element_type = _jarray_element_type(obj) return element_type in (JBoolean, JByte, JDouble, JFloat, JInt, JLong, JShort) def _jarray_element_type(jarr): if not is_jarray(jarr): return None element = jarr while is_jarray(element): element = element[0] return type(element) def _jarray_shape(jarr): if not is_jarray(jarr): return None shape = [] element = jarr while is_jarray(element): shape.append(len(element)) element = element[0] return shape def _import_numpy(): try: import numpy as np return np except ImportError as e: msg = "The NumPy library is missing (https://numpy.org/). " msg += "Please install it before using this function." raise RuntimeError(msg) from e ###################################### # SciJava table <-> pandas DataFrame # ###################################### def _is_table(obj: Any) -> bool: """Check if obj is a table.""" try: return jinstance(obj, "org.scijava.table.Table") except BaseException: # noqa: BLE001 # No worries if scijava-table is not available. return False def _convert_table(obj: Any): """Convert obj to a table.""" try: return _table_to_pandas(obj) except BaseException: # noqa: BLE001 # No worries if scijava-table is not available. return None def _import_pandas(): try: import pandas as pd return pd except ImportError as e: msg = "The Pandas library is missing (http://pandas.pydata.org/). " msg += "Please install it before using this function." raise RuntimeError(msg) from e def _table_to_pandas(table): pd = _import_pandas() data = [] headers = [] for i, column in enumerate(table.toArray()): data.append(column.toArray()) headers.append(str(table.getColumnHeader(i))) for j in range(len(data)): data[j] = to_python(data[j]) df = pd.DataFrame(data).T df.columns = headers return df def _pandas_to_table(df): if len(df.dtypes.unique()) > 1: TableClass = jimport("org.scijava.table.DefaultGenericTable") else: table_type = df.dtypes.unique()[0] if table_type.name.startswith("float"): TableClass = jimport("org.scijava.table.DefaultFloatTable") elif table_type.name.startswith("int"): TableClass = jimport("org.scijava.table.DefaultIntTable") elif table_type.name.startswith("bool"): TableClass = jimport("org.scijava.table.DefaultBoolTable") else: msg = f"The type '{table_type.name}' is not supported." raise ValueError(msg) table = TableClass(*df.shape[::-1]) for c, column_name in enumerate(df.columns): table.setColumnHeader(c, column_name) for i, (_, row) in enumerate(df.iterrows()): for c, value in enumerate(row): header = df.columns[c] table.set(header, i, to_java(value)) return table # fmt: off class _JavaClasses(JavaClasses): @JavaClasses.java_import def Boolean(self): return "java.lang.Boolean" @JavaClasses.java_import def Byte(self): return "java.lang.Byte" @JavaClasses.java_import def Character(self): return "java.lang.Character" @JavaClasses.java_import def Double(self): return "java.lang.Double" @JavaClasses.java_import def Float(self): return "java.lang.Float" @JavaClasses.java_import def Integer(self): return "java.lang.Integer" @JavaClasses.java_import def Iterable(self): return "java.lang.Iterable" @JavaClasses.java_import def Long(self): return "java.lang.Long" @JavaClasses.java_import def Object(self): return "java.lang.Object" @JavaClasses.java_import def Short(self): return "java.lang.Short" @JavaClasses.java_import def String(self): return "java.lang.String" @JavaClasses.java_import def BigDecimal(self): return "java.math.BigDecimal" @JavaClasses.java_import def BigInteger(self): return "java.math.BigInteger" @JavaClasses.java_import def Path(self): return "java.nio.file.Path" @JavaClasses.java_import def Paths(self): return "java.nio.file.Paths" @JavaClasses.java_import def ArrayList(self): return "java.util.ArrayList" @JavaClasses.java_import def Collection(self): return "java.util.Collection" @JavaClasses.java_import def Iterator(self): return "java.util.Iterator" @JavaClasses.java_import def LinkedHashMap(self): return "java.util.LinkedHashMap" @JavaClasses.java_import def LinkedHashSet(self): return "java.util.LinkedHashSet" @JavaClasses.java_import def List(self): return "java.util.List" @JavaClasses.java_import def Map(self): return "java.util.Map" @JavaClasses.java_import def Set(self): return "java.util.Set" # fmt: on _jc = _JavaClasses() |
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |