1from collections import namedtuple 2from collections.abc import Iterable 3from types import MappingProxyType 4 5from .abstract import ( 6 ConstSized, 7 Container, 8 Hashable, 9 MutableSequence, 10 Sequence, 11 Type, 12 TypeRef, 13 Literal, 14 InitialValue, 15 Poison, 16) 17from .common import ( 18 Buffer, 19 IterableType, 20 SimpleIterableType, 21 SimpleIteratorType, 22) 23from .misc import Undefined, unliteral, Optional, NoneType 24from ..typeconv import Conversion 25from ..errors import TypingError 26from .. import utils 27 28 29class Pair(Type): 30 """ 31 A heterogeneous pair. 32 """ 33 34 def __init__(self, first_type, second_type): 35 self.first_type = first_type 36 self.second_type = second_type 37 name = "pair<%s, %s>" % (first_type, second_type) 38 super(Pair, self).__init__(name=name) 39 40 @property 41 def key(self): 42 return self.first_type, self.second_type 43 44 def unify(self, typingctx, other): 45 if isinstance(other, Pair): 46 first = typingctx.unify_pairs(self.first_type, other.first_type) 47 second = typingctx.unify_pairs(self.second_type, other.second_type) 48 if first is not None and second is not None: 49 return Pair(first, second) 50 51 52class BaseContainerIterator(SimpleIteratorType): 53 """ 54 Convenience base class for some container iterators. 55 56 Derived classes must implement the *container_class* attribute. 57 """ 58 59 def __init__(self, container): 60 assert isinstance(container, self.container_class), container 61 self.container = container 62 yield_type = container.dtype 63 name = "iter(%s)" % container 64 super(BaseContainerIterator, self).__init__(name, yield_type) 65 66 def unify(self, typingctx, other): 67 cls = type(self) 68 if isinstance(other, cls): 69 container = typingctx.unify_pairs(self.container, other.container) 70 if container is not None: 71 return cls(container) 72 73 @property 74 def key(self): 75 return self.container 76 77 78class BaseContainerPayload(Type): 79 """ 80 Convenience base class for some container payloads. 81 82 Derived classes must implement the *container_class* attribute. 83 """ 84 85 def __init__(self, container): 86 assert isinstance(container, self.container_class) 87 self.container = container 88 name = "payload(%s)" % container 89 super(BaseContainerPayload, self).__init__(name) 90 91 @property 92 def key(self): 93 return self.container 94 95 96class Bytes(Buffer): 97 """ 98 Type class for Python 3.x bytes objects. 99 """ 100 101 mutable = False 102 # Actually true but doesn't matter since bytes is immutable 103 slice_is_copy = False 104 105 106class ByteArray(Buffer): 107 """ 108 Type class for bytearray objects. 109 """ 110 111 slice_is_copy = True 112 113 114class PyArray(Buffer): 115 """ 116 Type class for array.array objects. 117 """ 118 119 slice_is_copy = True 120 121 122class MemoryView(Buffer): 123 """ 124 Type class for memoryview objects. 125 """ 126 127 128def is_homogeneous(*tys): 129 """Are the types homogeneous? 130 """ 131 if tys: 132 first, tys = tys[0], tys[1:] 133 return not any(t != first for t in tys) 134 else: 135 # *tys* is empty. 136 return False 137 138 139class BaseTuple(ConstSized, Hashable): 140 """ 141 The base class for all tuple types (with a known size). 142 """ 143 144 @classmethod 145 def from_types(cls, tys, pyclass=None): 146 """ 147 Instantiate the right tuple type for the given element types. 148 """ 149 if pyclass is not None and pyclass is not tuple: 150 # A subclass => is it a namedtuple? 151 assert issubclass(pyclass, tuple) 152 if hasattr(pyclass, "_asdict"): 153 tys = tuple(map(unliteral, tys)) 154 homogeneous = is_homogeneous(*tys) 155 if homogeneous: 156 return NamedUniTuple(tys[0], len(tys), pyclass) 157 else: 158 return NamedTuple(tys, pyclass) 159 else: 160 dtype = utils.unified_function_type(tys) 161 if dtype is not None: 162 return UniTuple(dtype, len(tys)) 163 # non-named tuple 164 homogeneous = is_homogeneous(*tys) 165 if homogeneous: 166 return cls._make_homogeneous_tuple(tys[0], len(tys)) 167 else: 168 return cls._make_heterogeneous_tuple(tys) 169 170 @classmethod 171 def _make_homogeneous_tuple(cls, dtype, count): 172 return UniTuple(dtype, count) 173 174 @classmethod 175 def _make_heterogeneous_tuple(cls, tys): 176 return Tuple(tys) 177 178 179class BaseAnonymousTuple(BaseTuple): 180 """ 181 Mixin for non-named tuples. 182 """ 183 184 def can_convert_to(self, typingctx, other): 185 """ 186 Convert this tuple to another one. Note named tuples are rejected. 187 """ 188 if not isinstance(other, BaseAnonymousTuple): 189 return 190 if len(self) != len(other): 191 return 192 if len(self) == 0: 193 return Conversion.safe 194 if isinstance(other, BaseTuple): 195 kinds = [ 196 typingctx.can_convert(ta, tb) for ta, tb in zip(self, other) 197 ] 198 if any(kind is None for kind in kinds): 199 return 200 return max(kinds) 201 202 def __unliteral__(self): 203 return type(self).from_types([unliteral(t) for t in self]) 204 205 206class _HomogeneousTuple(Sequence, BaseTuple): 207 @property 208 def iterator_type(self): 209 return UniTupleIter(self) 210 211 def __getitem__(self, i): 212 """ 213 Return element at position i 214 """ 215 return self.dtype 216 217 def __iter__(self): 218 return iter([self.dtype] * self.count) 219 220 def __len__(self): 221 return self.count 222 223 @property 224 def types(self): 225 return (self.dtype,) * self.count 226 227 228class UniTuple(BaseAnonymousTuple, _HomogeneousTuple, Sequence): 229 """ 230 Type class for homogeneous tuples. 231 """ 232 233 def __init__(self, dtype, count): 234 self.dtype = dtype 235 self.count = count 236 name = "%s(%s x %d)" % (self.__class__.__name__, dtype, count,) 237 super(UniTuple, self).__init__(name) 238 239 @property 240 def mangling_args(self): 241 return self.__class__.__name__, (self.dtype, self.count) 242 243 @property 244 def key(self): 245 return self.dtype, self.count 246 247 def unify(self, typingctx, other): 248 """ 249 Unify UniTuples with their dtype 250 """ 251 if isinstance(other, UniTuple) and len(self) == len(other): 252 dtype = typingctx.unify_pairs(self.dtype, other.dtype) 253 if dtype is not None: 254 return UniTuple(dtype=dtype, count=self.count) 255 256 def __unliteral__(self): 257 return type(self)(dtype=unliteral(self.dtype), count=self.count) 258 259 260class UniTupleIter(BaseContainerIterator): 261 """ 262 Type class for homogeneous tuple iterators. 263 """ 264 265 container_class = _HomogeneousTuple 266 267 268class _HeterogeneousTuple(BaseTuple): 269 def __getitem__(self, i): 270 """ 271 Return element at position i 272 """ 273 return self.types[i] 274 275 def __len__(self): 276 # Beware: this makes Tuple(()) false-ish 277 return len(self.types) 278 279 def __iter__(self): 280 return iter(self.types) 281 282 @staticmethod 283 def is_types_iterable(types): 284 # issue 4463 - check if argument 'types' is iterable 285 if not isinstance(types, Iterable): 286 raise TypingError("Argument 'types' is not iterable") 287 288 289class UnionType(Type): 290 def __init__(self, types): 291 self.types = tuple(sorted(set(types), key=lambda x: x.name)) 292 name = "Union[{}]".format(",".join(map(str, self.types))) 293 super(UnionType, self).__init__(name=name) 294 295 def get_type_tag(self, typ): 296 return self.types.index(typ) 297 298 299class Tuple(BaseAnonymousTuple, _HeterogeneousTuple): 300 def __new__(cls, types): 301 302 t = utils.unified_function_type(types, require_precise=True) 303 if t is not None: 304 return UniTuple(dtype=t, count=len(types)) 305 306 _HeterogeneousTuple.is_types_iterable(types) 307 308 if types and all(t == types[0] for t in types[1:]): 309 return UniTuple(dtype=types[0], count=len(types)) 310 else: 311 return object.__new__(Tuple) 312 313 def __init__(self, types): 314 self.types = tuple(types) 315 self.count = len(self.types) 316 self.dtype = UnionType(types) 317 name = "%s(%s)" % ( 318 self.__class__.__name__, 319 ", ".join(str(i) for i in self.types), 320 ) 321 super(Tuple, self).__init__(name) 322 323 @property 324 def mangling_args(self): 325 return self.__class__.__name__, tuple(t for t in self.types) 326 327 @property 328 def key(self): 329 return self.types 330 331 def unify(self, typingctx, other): 332 """ 333 Unify elements of Tuples/UniTuples 334 """ 335 # Other is UniTuple or Tuple 336 if isinstance(other, BaseTuple) and len(self) == len(other): 337 unified = [ 338 typingctx.unify_pairs(ta, tb) for ta, tb in zip(self, other) 339 ] 340 341 if all(t is not None for t in unified): 342 return Tuple(unified) 343 344 345class _StarArgTupleMixin: 346 @classmethod 347 def _make_homogeneous_tuple(cls, dtype, count): 348 return StarArgUniTuple(dtype, count) 349 350 @classmethod 351 def _make_heterogeneous_tuple(cls, tys): 352 return StarArgTuple(tys) 353 354 355class StarArgTuple(_StarArgTupleMixin, Tuple): 356 """To distinguish from Tuple() used as argument to a `*args`. 357 """ 358 359 def __new__(cls, types): 360 _HeterogeneousTuple.is_types_iterable(types) 361 362 if types and all(t == types[0] for t in types[1:]): 363 return StarArgUniTuple(dtype=types[0], count=len(types)) 364 else: 365 return object.__new__(StarArgTuple) 366 367 368class StarArgUniTuple(_StarArgTupleMixin, UniTuple): 369 """To distinguish from UniTuple() used as argument to a `*args`. 370 """ 371 372 373class BaseNamedTuple(BaseTuple): 374 pass 375 376 377class NamedUniTuple(_HomogeneousTuple, BaseNamedTuple): 378 def __init__(self, dtype, count, cls): 379 self.dtype = dtype 380 self.count = count 381 self.fields = tuple(cls._fields) 382 self.instance_class = cls 383 name = "%s(%s x %d)" % (cls.__name__, dtype, count) 384 super(NamedUniTuple, self).__init__(name) 385 386 @property 387 def iterator_type(self): 388 return UniTupleIter(self) 389 390 @property 391 def key(self): 392 return self.instance_class, self.dtype, self.count 393 394 395class NamedTuple(_HeterogeneousTuple, BaseNamedTuple): 396 def __init__(self, types, cls): 397 _HeterogeneousTuple.is_types_iterable(types) 398 399 self.types = tuple(types) 400 self.count = len(self.types) 401 self.fields = tuple(cls._fields) 402 self.instance_class = cls 403 name = "%s(%s)" % (cls.__name__, ", ".join(str(i) for i in self.types)) 404 super(NamedTuple, self).__init__(name) 405 406 @property 407 def key(self): 408 return self.instance_class, self.types 409 410 411class List(MutableSequence, InitialValue): 412 """ 413 Type class for (arbitrary-sized) homogeneous lists. 414 """ 415 416 mutable = True 417 418 def __init__(self, dtype, reflected=False, initial_value=None): 419 dtype = unliteral(dtype) 420 self.dtype = dtype 421 self.reflected = reflected 422 cls_name = "reflected list" if reflected else "list" 423 name = "%s(%s)<iv=%s>" % (cls_name, self.dtype, initial_value) 424 super(List, self).__init__(name=name) 425 InitialValue.__init__(self, initial_value) 426 427 def copy(self, dtype=None, reflected=None): 428 if dtype is None: 429 dtype = self.dtype 430 if reflected is None: 431 reflected = self.reflected 432 return List(dtype, reflected, self.initial_value) 433 434 def unify(self, typingctx, other): 435 if isinstance(other, List): 436 dtype = typingctx.unify_pairs(self.dtype, other.dtype) 437 reflected = self.reflected or other.reflected 438 if dtype is not None: 439 siv = self.initial_value 440 oiv = other.initial_value 441 if siv is not None and oiv is not None: 442 use = siv 443 if siv is None: 444 use = oiv 445 return List(dtype, reflected, use) 446 else: 447 return List(dtype, reflected) 448 449 @property 450 def key(self): 451 return self.dtype, self.reflected, str(self.initial_value) 452 453 @property 454 def iterator_type(self): 455 return ListIter(self) 456 457 def is_precise(self): 458 return self.dtype.is_precise() 459 460 def __getitem__(self, args): 461 """ 462 Overrides the default __getitem__ from Type. 463 """ 464 return self.dtype 465 466 def __unliteral__(self): 467 return List(self.dtype, reflected=self.reflected, 468 initial_value=None) 469 470 471class LiteralList(Literal, ConstSized, Hashable): 472 """A heterogeneous immutable list (basically a tuple with list semantics). 473 """ 474 475 mutable = False 476 477 def __init__(self, literal_value): 478 self.is_types_iterable(literal_value) 479 self._literal_init(list(literal_value)) 480 self.types = tuple(literal_value) 481 self.count = len(self.types) 482 self.name = "LiteralList({})".format(literal_value) 483 484 def __getitem__(self, i): 485 """ 486 Return element at position i 487 """ 488 return self.types[i] 489 490 def __len__(self): 491 return len(self.types) 492 493 def __iter__(self): 494 return iter(self.types) 495 496 @classmethod 497 def from_types(cls, tys): 498 return LiteralList(tys) 499 500 @staticmethod 501 def is_types_iterable(types): 502 if not isinstance(types, Iterable): 503 raise TypingError("Argument 'types' is not iterable") 504 505 @property 506 def iterator_type(self): 507 return ListIter(self) 508 509 def __unliteral__(self): 510 return Poison(self) 511 512 def unify(self, typingctx, other): 513 """ 514 Unify this with the *other* one. 515 """ 516 if isinstance(other, LiteralList) and self.count == other.count: 517 tys = [] 518 for i1, i2 in zip(self.types, other.types): 519 tys.append(typingctx.unify_pairs(i1, i2)) 520 if all(tys): 521 return LiteralList(tys) 522 523 524class ListIter(BaseContainerIterator): 525 """ 526 Type class for list iterators. 527 """ 528 529 container_class = List 530 531 532class ListPayload(BaseContainerPayload): 533 """ 534 Internal type class for the dynamically-allocated payload of a list. 535 """ 536 537 container_class = List 538 539 540class Set(Container): 541 """ 542 Type class for homogeneous sets. 543 """ 544 545 mutable = True 546 547 def __init__(self, dtype, reflected=False): 548 assert isinstance(dtype, (Hashable, Undefined)) 549 self.dtype = dtype 550 self.reflected = reflected 551 cls_name = "reflected set" if reflected else "set" 552 name = "%s(%s)" % (cls_name, self.dtype) 553 super(Set, self).__init__(name=name) 554 555 @property 556 def key(self): 557 return self.dtype, self.reflected 558 559 @property 560 def iterator_type(self): 561 return SetIter(self) 562 563 def is_precise(self): 564 return self.dtype.is_precise() 565 566 def copy(self, dtype=None, reflected=None): 567 if dtype is None: 568 dtype = self.dtype 569 if reflected is None: 570 reflected = self.reflected 571 return Set(dtype, reflected) 572 573 def unify(self, typingctx, other): 574 if isinstance(other, Set): 575 dtype = typingctx.unify_pairs(self.dtype, other.dtype) 576 reflected = self.reflected or other.reflected 577 if dtype is not None: 578 return Set(dtype, reflected) 579 580 581class SetIter(BaseContainerIterator): 582 """ 583 Type class for set iterators. 584 """ 585 586 container_class = Set 587 588 589class SetPayload(BaseContainerPayload): 590 """ 591 Internal type class for the dynamically-allocated payload of a set. 592 """ 593 594 container_class = Set 595 596 597class SetEntry(Type): 598 """ 599 Internal type class for the entries of a Set's hash table. 600 """ 601 602 def __init__(self, set_type): 603 self.set_type = set_type 604 name = "entry(%s)" % set_type 605 super(SetEntry, self).__init__(name) 606 607 @property 608 def key(self): 609 return self.set_type 610 611 612class ListType(IterableType): 613 """List type 614 """ 615 616 mutable = True 617 618 def __init__(self, itemty): 619 assert not isinstance(itemty, TypeRef) 620 itemty = unliteral(itemty) 621 if isinstance(itemty, Optional): 622 fmt = "List.item_type cannot be of type {}" 623 raise TypingError(fmt.format(itemty)) 624 # FIXME: _sentry_forbidden_types(itemty) 625 self.item_type = itemty 626 self.dtype = itemty 627 name = "{}[{}]".format(self.__class__.__name__, itemty,) 628 super(ListType, self).__init__(name) 629 630 def is_precise(self): 631 return not isinstance(self.item_type, Undefined) 632 633 @property 634 def iterator_type(self): 635 return ListTypeIterableType(self).iterator_type 636 637 @classmethod 638 def refine(cls, itemty): 639 """Refine to a precise list type 640 """ 641 res = cls(itemty) 642 assert res.is_precise() 643 return res 644 645 def unify(self, typingctx, other): 646 """ 647 Unify this with the *other* list. 648 """ 649 # If other is list 650 if isinstance(other, ListType): 651 if not other.is_precise(): 652 return self 653 654 655class ListTypeIterableType(SimpleIterableType): 656 """List iterable type 657 """ 658 659 def __init__(self, parent): 660 assert isinstance(parent, ListType) 661 self.parent = parent 662 self.yield_type = self.parent.item_type 663 name = "list[{}]".format(self.parent.name) 664 iterator_type = ListTypeIteratorType(self) 665 super(ListTypeIterableType, self).__init__(name, iterator_type) 666 667 668class ListTypeIteratorType(SimpleIteratorType): 669 def __init__(self, iterable): 670 self.parent = iterable.parent 671 self.iterable = iterable 672 yield_type = iterable.yield_type 673 name = "iter[{}->{}]".format(iterable.parent, yield_type) 674 super(ListTypeIteratorType, self).__init__(name, yield_type) 675 676 677def _sentry_forbidden_types(key, value): 678 # Forbids List and Set for now 679 if isinstance(key, (Set, List)): 680 raise TypingError("{} as key is forbidden".format(key)) 681 if isinstance(value, (Set, List)): 682 raise TypingError("{} as value is forbidden".format(value)) 683 684 685class DictType(IterableType, InitialValue): 686 """Dictionary type 687 """ 688 689 def __init__(self, keyty, valty, initial_value=None): 690 assert not isinstance(keyty, TypeRef) 691 assert not isinstance(valty, TypeRef) 692 keyty = unliteral(keyty) 693 valty = unliteral(valty) 694 if isinstance(keyty, (Optional, NoneType)): 695 fmt = "Dict.key_type cannot be of type {}" 696 raise TypingError(fmt.format(keyty)) 697 if isinstance(valty, (Optional, NoneType)): 698 fmt = "Dict.value_type cannot be of type {}" 699 raise TypingError(fmt.format(valty)) 700 _sentry_forbidden_types(keyty, valty) 701 self.key_type = keyty 702 self.value_type = valty 703 self.keyvalue_type = Tuple([keyty, valty]) 704 name = "{}[{},{}]<iv={}>".format( 705 self.__class__.__name__, keyty, valty, initial_value 706 ) 707 super(DictType, self).__init__(name) 708 InitialValue.__init__(self, initial_value) 709 710 def is_precise(self): 711 return not any( 712 ( 713 isinstance(self.key_type, Undefined), 714 isinstance(self.value_type, Undefined), 715 ) 716 ) 717 718 @property 719 def iterator_type(self): 720 return DictKeysIterableType(self).iterator_type 721 722 @classmethod 723 def refine(cls, keyty, valty): 724 """Refine to a precise dictionary type 725 """ 726 res = cls(keyty, valty) 727 assert res.is_precise() 728 return res 729 730 def unify(self, typingctx, other): 731 """ 732 Unify this with the *other* dictionary. 733 """ 734 # If other is dict 735 if isinstance(other, DictType): 736 if not other.is_precise(): 737 return self 738 else: 739 ukey_type = self.key_type == other.key_type 740 uvalue_type = self.value_type == other.value_type 741 if ukey_type and uvalue_type: 742 siv = self.initial_value 743 oiv = other.initial_value 744 siv_none = siv is None 745 oiv_none = oiv is None 746 if not siv_none and not oiv_none: 747 if siv == oiv: 748 return DictType(self.key_type, other.value_type, 749 siv) 750 return DictType(self.key_type, other.value_type) 751 752 @property 753 def key(self): 754 return self.key_type, self.value_type, str(self.initial_value) 755 756 def __unliteral__(self): 757 return DictType(self.key_type, self.value_type) 758 759 760class LiteralStrKeyDict(Literal, ConstSized, Hashable): 761 """A Dictionary of string keys to heterogeneous values (basically a 762 namedtuple with dict semantics). 763 """ 764 765 mutable = False 766 767 def __init__(self, literal_value, value_index=None): 768 self._literal_init(literal_value) 769 self.value_index = value_index 770 strkeys = [x.literal_value for x in literal_value.keys()] 771 self.tuple_ty = namedtuple("_ntclazz", " ".join(strkeys)) 772 tys = [x for x in literal_value.values()] 773 self.types = tuple(tys) 774 self.count = len(self.types) 775 self.fields = tuple(self.tuple_ty._fields) 776 self.instance_class = self.tuple_ty 777 self.name = "LiteralStrKey[Dict]({})".format(literal_value) 778 779 def __unliteral__(self): 780 return Poison(self) 781 782 def unify(self, typingctx, other): 783 """ 784 Unify this with the *other* one. 785 """ 786 if isinstance(other, LiteralStrKeyDict): 787 tys = [] 788 for (k1, v1), (k2, v2) in zip( 789 self.literal_value.items(), other.literal_value.items() 790 ): 791 if k1 != k2: # keys must be same 792 break 793 tys.append(typingctx.unify_pairs(v1, v2)) 794 else: 795 if all(tys): 796 d = {k: v for k, v in zip(self.literal_value.keys(), tys)} 797 return LiteralStrKeyDict(d) 798 799 def __len__(self): 800 return len(self.types) 801 802 def __iter__(self): 803 return iter(self.types) 804 805 @property 806 def key(self): 807 # use the namedtuple fields not the namedtuple itself as it's created 808 # locally in the ctor and comparison would always be False. 809 return self.tuple_ty._fields, self.types, str(self.literal_value) 810 811 812class DictItemsIterableType(SimpleIterableType): 813 """Dictionary iterable type for .items() 814 """ 815 816 def __init__(self, parent): 817 assert isinstance(parent, DictType) 818 self.parent = parent 819 self.yield_type = self.parent.keyvalue_type 820 name = "items[{}]".format(self.parent.name) 821 self.name = name 822 iterator_type = DictIteratorType(self) 823 super(DictItemsIterableType, self).__init__(name, iterator_type) 824 825 826class DictKeysIterableType(SimpleIterableType): 827 """Dictionary iterable type for .keys() 828 """ 829 830 def __init__(self, parent): 831 assert isinstance(parent, DictType) 832 self.parent = parent 833 self.yield_type = self.parent.key_type 834 name = "keys[{}]".format(self.parent.name) 835 self.name = name 836 iterator_type = DictIteratorType(self) 837 super(DictKeysIterableType, self).__init__(name, iterator_type) 838 839 840class DictValuesIterableType(SimpleIterableType): 841 """Dictionary iterable type for .values() 842 """ 843 844 def __init__(self, parent): 845 assert isinstance(parent, DictType) 846 self.parent = parent 847 self.yield_type = self.parent.value_type 848 name = "values[{}]".format(self.parent.name) 849 self.name = name 850 iterator_type = DictIteratorType(self) 851 super(DictValuesIterableType, self).__init__(name, iterator_type) 852 853 854class DictIteratorType(SimpleIteratorType): 855 def __init__(self, iterable): 856 self.parent = iterable.parent 857 self.iterable = iterable 858 yield_type = iterable.yield_type 859 name = "iter[{}->{}],{}".format( 860 iterable.parent, yield_type, iterable.name 861 ) 862 super(DictIteratorType, self).__init__(name, yield_type) 863 864 865class StructRef(Type): 866 """A mutable struct. 867 """ 868 869 def __init__(self, fields): 870 """ 871 Parameters 872 ---------- 873 fields : Sequence 874 A sequence of field descriptions, which is a 2-tuple-like object 875 containing `(name, type)`, where `name` is a `str` for the field 876 name, and `type` is a numba type for the field type. 877 """ 878 879 def check_field_pair(fieldpair): 880 name, typ = fieldpair 881 if not isinstance(name, str): 882 msg = "expecting a str for field name" 883 raise ValueError(msg) 884 if not isinstance(typ, Type): 885 msg = "expecting a Numba Type for field type" 886 raise ValueError(msg) 887 return name, typ 888 889 fields = tuple(map(check_field_pair, fields)) 890 self._fields = tuple(map(check_field_pair, 891 self.preprocess_fields(fields))) 892 self._typename = self.__class__.__qualname__ 893 name = f"numba.{self._typename}{self._fields}" 894 super().__init__(name=name) 895 896 def preprocess_fields(self, fields): 897 """Subclasses can override this to do additional clean up on fields. 898 899 The default is an identity function. 900 901 Parameters: 902 ----------- 903 fields : Sequence[Tuple[str, Type]] 904 """ 905 return fields 906 907 @property 908 def field_dict(self): 909 """Return an immutable mapping for the field names and their 910 corresponding types. 911 """ 912 return MappingProxyType(dict(self._fields)) 913 914 def get_data_type(self): 915 """Get the payload type for the actual underlying structure referred 916 to by this struct reference. 917 918 See also: `ClassInstanceType.get_data_type` 919 """ 920 return StructRefPayload( 921 typename=self.__class__.__name__, fields=self._fields, 922 ) 923 924 925class StructRefPayload(Type): 926 """The type of the payload of a mutable struct. 927 """ 928 929 mutable = True 930 931 def __init__(self, typename, fields): 932 self._typename = typename 933 self._fields = tuple(fields) 934 super().__init__(name=f"numba.{typename}{self._fields}.payload") 935 936 @property 937 def field_dict(self): 938 return MappingProxyType(dict(self._fields)) 939