1__all__ = ('object_property', 'bool_property', 2 'array_property', 'set_property', 'dict_property') 3 4from objc import ivar, selector, _C_ID, _C_NSBOOL, _C_BOOL, NULL, _C_NSUInteger 5from objc import lookUpClass 6import collections 7from copy import copy as copy_func 8import sys 9 10NSSet = lookUpClass('NSSet') 11NSObject = lookUpClass('NSObject') 12 13if sys.version_info[0] == 2: 14 range = xrange 15 16 def _str(value): 17 return value 18 19else: # pragma: no cover (py3k) 20 long = int 21 22 def _str(value): 23 return value.decode('ascii') 24 25 26 27def attrsetter(prop, name, copy): 28 if copy: 29 def func(self, value): 30 if isinstance(value, NSObject): 31 setattr(self, name, value.copy()) 32 else: 33 setattr(self, name, copy_func(value)) 34 else: 35 def func(self, value): 36 setattr(self, name, value) 37 return func 38 39def attrgetter(name): 40 def func(self): 41 return getattr(self, name) 42 return func 43 44def _return_value(value): 45 def func(self): 46 return value 47 48 return func 49 50def _dynamic_getter(name): 51 def getter(object): 52 m = getattr(object.pyobjc_instanceMethods, name) 53 return m() 54 getter.__name__ = name 55 return getter 56 57def _dynamic_setter(name): 58 def setter(object, value): 59 m = getattr(object.pyobjc_instanceMethods, name) 60 return m(value) 61 setter.__name__ = name 62 return setter 63 64class object_property (object): 65 def __init__(self, name=None, 66 read_only=False, copy=False, dynamic=False, 67 ivar=None, typestr=_C_ID, depends_on=None): 68 self.__created = False 69 self.__inherit = False 70 self._name = name 71 self._typestr = typestr 72 self._ro = read_only 73 self._copy = copy 74 self._dynamic = dynamic 75 self._ivar = ivar 76 self._getter = None 77 self._setter = None 78 self._validate = None 79 if depends_on is None: 80 self._depends_on = None 81 else: 82 self._depends_on = set(depends_on) 83 84 self.__getprop = None 85 self.__setprop = None 86 self.__parent = None 87 88 def _clone(self): 89 if self._depends_on is None: 90 depends = None 91 else: 92 depends = self._depends_on.copy() 93 94 v = type(self)(name=self._name, 95 read_only=self._ro, copy=self._copy, dynamic=self._dynamic, 96 ivar=self._ivar, typestr=self._typestr, depends_on=depends) 97 v.__inherit = True 98 99 v.__getprop = self.__getprop 100 v.__setprop = self.__setprop 101 v.__parent = self 102 103 return v 104 105 def __pyobjc_class_setup__(self, name, class_dict, instance_methods, class_methods): 106 self.__created = True 107 if self._name is None: 108 self._name = name 109 110 if self._ivar is not NULL: 111 if self._ivar is None: 112 ivname = '_' + self._name 113 else: 114 ivname = self._ivar 115 116 if self.__parent is None: 117 ivar_ref = ivar(name=ivname, type=self._typestr) 118 class_dict[ivname] = ivar_ref 119 120 if self._ro: 121 self._setter = None 122 123 else: 124 setterName = b'set' + name[0].upper().encode('latin1') + name[1:].encode('latin1') + b':' 125 signature = b'v@:' + self._typestr 126 if self._setter is None: 127 if self.__inherit: 128 pass 129 130 elif self._dynamic: 131 dynSetterName = 'set' + name[0].upper() + name[1:] + '_' 132 self.__setprop = _dynamic_setter(dynSetterName) 133 instance_methods.add(setterName) 134 135 else: 136 137 if self._ivar is NULL: 138 raise ValueError( 139 "Cannot create default setter for property " 140 "without ivar") 141 142 setprop = selector( 143 attrsetter(self._name, ivname, self._copy), 144 selector=setterName, 145 signature=signature 146 ) 147 setprop.isHidden = True 148 instance_methods.add(setprop) 149 150 # Use dynamic setter to avoid problems when subclassing 151 self.__setprop = _dynamic_setter(_str(setterName)) 152 else: 153 setprop = selector( 154 self._setter, 155 selector=setterName, 156 signature=signature 157 ) 158 setprop.isHidden = True 159 instance_methods.add(setprop) 160 161 # Use dynamic setter to avoid problems when subclassing 162 self.__setprop = _dynamic_setter(_str(setterName)) 163 164 if self._typestr in (_C_NSBOOL, _C_BOOL): 165 getterName = b'is' + name[0].upper().encode('latin1') + name[1:].encode('latin1') 166 else: 167 getterName = self._name.encode('latin1') 168 169 if self._getter is None: 170 if self.__inherit: 171 pass 172 173 elif self._dynamic: 174 if self._typestr in (_C_NSBOOL, _C_BOOL): 175 dynGetterName = 'is' + name[0].upper() + name[1:] 176 else: 177 dynGetterName = self._name 178 179 self.__getprop = _dynamic_getter(dynGetterName) 180 instance_methods.add(getterName) 181 182 else: 183 if self._ivar is NULL: 184 raise ValueError( 185 "Cannot create default getter for property without ivar") 186 187 self.__getprop = selector( 188 attrgetter(ivname), 189 selector=getterName, 190 signature=self._typestr + b'@:') 191 self.__getprop.isHidden=True 192 instance_methods.add(self.__getprop) 193 194 else: 195 self.__getprop = getprop = selector( 196 self._getter, 197 selector=getterName, 198 signature=self._typestr + b'@:') 199 getprop.isHidden=True 200 instance_methods.add(getprop) 201 #self.__getprop = _dynamic_getter(getterName) 202 203 if self._validate is not None: 204 selName = b'validate' + self._name[0].upper().encode('latin') + self._name[1:].encode('latin') + b':error:' 205 signature = _C_NSBOOL + b'@:N^@o^@' 206 validate = selector( 207 self._validate, 208 selector=selName, 209 signature=signature) 210 class_dict[validate.selector] = validate 211 instance_methods.add(validate) 212 213 if self._depends_on: 214 if self.__parent is not None: 215 if self.__parent._depends_on: 216 self._depends_on.update(self.__parent._depends_on.copy()) 217 218 self._depends_on = self._depends_on 219 220 affecting = selector( 221 _return_value(NSSet.setWithArray_(list(self._depends_on))), 222 selector = b'keyPathsForValuesAffecting' + self._name[0].upper().encode('latin1') + self._name[1:].encode('latin1'), 223 signature = b'@@:', 224 isClassMethod=True) 225 class_dict[affecting.selector] = affecting 226 class_methods.add(affecting) 227 228 def __get__(self, object, owner): 229 if object is None: 230 return self 231 return self.__getprop(object) 232 233 def __set__(self, object, value): 234 if self.__setprop is None: 235 raise ValueError("setting read-only property " + self._name) 236 237 return self.__setprop(object, value) 238 239 def __delete__(self, object): 240 raise TypeError("cannot delete property " + self._name) 241 242 def depends_on(self, keypath): 243 if self._depends_on is None: 244 self._depends_on = set() 245 self._depends_on.add(keypath) 246 247 def getter(self, function): 248 if self.__created: 249 v = self._clone() 250 v._getter = function 251 return v 252 253 self._getter = function 254 return self 255 256 def setter(self, function): 257 258 if self.__created: 259 v = self._clone() 260 v._ro = False 261 v._setter = function 262 return v 263 264 if self._ro: 265 raise ValueError("Defining settter for read-only property") 266 267 self._setter = function 268 return self 269 270 def validate(self, function): 271 if self._ro: 272 raise ValueError("Defining validator for read-only property") 273 274 if self.__created: 275 v = self._clone() 276 v._validate = function 277 return v 278 279 self._validate = function 280 return self 281 282class bool_property (object_property): 283 def __init__(self, name=None, 284 read_only=False, copy=False, dynamic=False, 285 ivar=None, typestr=_C_NSBOOL): 286 super(bool_property, self).__init__( 287 name, read_only, copy, dynamic, ivar, typestr) 288 289 290 291 292NSIndexSet = lookUpClass('NSIndexSet') 293NSMutableIndexSet = lookUpClass('NSMutableIndexSet') 294NSKeyValueChangeSetting = 1 295NSKeyValueChangeInsertion = 2 296NSKeyValueChangeRemoval = 3 297NSKeyValueChangeReplacement = 4 298 299 300# Helper function for (not) pickling array_proxy instances 301# NOTE: Don't remove this function, it can be referenced from 302# pickle files. 303def _id(value): 304 return value 305 306# FIXME: split into two: array_proxy and mutable_array_proxy 307class array_proxy (collections.MutableSequence): 308 # XXX: The implemenation should be complete, but is currently not 309 # tested. 310 __slots__ = ('_name', '_parent', '__wrapped', '_ro') 311 312 def __init__(self, name, parent, wrapped, read_only): 313 self._name = name 314 self._parent = parent 315 self._ro = read_only 316 self.__wrapped = wrapped 317 318 @property 319 def _wrapped(self): 320 return self.__wrapped.__getvalue__(self._parent) 321 322 def __indexSetForIndex(self, index): 323 if isinstance(index, slice): 324 result = NSMutableIndexSet.alloc().init() 325 start, stop, step = index.indices(len(self._wrapped)) 326 for i in range(start, stop, step): 327 result.addIndex_(i) 328 329 return result 330 331 elif isinstance(index, (int, long)): 332 if index < 0: 333 v = len(self) + index 334 if v < 0: 335 raise IndexError(index) 336 return NSIndexSet.alloc().initWithIndex_(v) 337 338 else: 339 return NSIndexSet.alloc().initWithIndex_(index) 340 341 else: 342 raise TypeError(index) 343 344 345 346 def __repr__(self): 347 return '<array proxy for property ' + self._name + ' ' + repr(self._wrapped) + '>' 348 349 def __reduce__(self): 350 # Ensure that the proxy itself doesn't get stored 351 # in pickles. 352 return _id, (self._wrapped,) 353 354 def __getattr__(self, name): 355 # Default: just defer to wrapped list 356 return getattr(self._wrapped, name) 357 358 def __len__(self): 359 return self._wrapped.__len__() 360 361 def __getitem__(self, index): 362 return self._wrapped[index] 363 364 def __setitem__(self, index, value): 365 if self._ro: 366 raise ValueError("Property '%s' is read-only"%(self._name,)) 367 368 indexes = self.__indexSetForIndex(index) 369 self._parent.willChange_valuesAtIndexes_forKey_( 370 NSKeyValueChangeSetting, 371 indexes, self._name) 372 try: 373 self._wrapped[index] = value 374 finally: 375 self._parent.didChange_valuesAtIndexes_forKey_( 376 NSKeyValueChangeReplacement, 377 indexes, self._name) 378 379 def __delitem__(self, index): 380 if self._ro: 381 raise ValueError("Property '%s' is read-only"%(self._name,)) 382 383 indexes = self.__indexSetForIndex(index) 384 self._parent.willChange_valuesAtIndexes_forKey_( 385 NSKeyValueChangeRemoval, 386 indexes, self._name) 387 try: 388 del self._wrapped[index] 389 finally: 390 self._parent.didChange_valuesAtIndexes_forKey_( 391 NSKeyValueChangeRemoval, 392 indexes, self._name) 393 394 def append(self, value): 395 if self._ro: 396 raise ValueError("Property '%s' is read-only"%(self._name,)) 397 398 index = len(self) 399 indexes = NSIndexSet.alloc().initWithIndex_(index) 400 self._parent.willChange_valuesAtIndexes_forKey_( 401 NSKeyValueChangeInsertion, 402 indexes, self._name) 403 try: 404 self._wrapped.append(value) 405 finally: 406 self._parent.didChange_valuesAtIndexes_forKey_( 407 NSKeyValueChangeInsertion, 408 indexes, self._name) 409 410 def insert(self, index, value): 411 if self._ro: 412 raise ValueError("Property '%s' is read-only"%(self._name,)) 413 414 if isinstance(index, slice): 415 raise TypeError("insert argument 1 is a slice") 416 417 indexes = self.__indexSetForIndex(index) 418 self._parent.willChange_valuesAtIndexes_forKey_( 419 NSKeyValueChangeInsertion, 420 indexes, self._name) 421 try: 422 self._wrapped.insert(index, value) 423 finally: 424 self._parent.didChange_valuesAtIndexes_forKey_( 425 NSKeyValueChangeInsertion, 426 indexes, self._name) 427 428 def pop(self, index=-1): 429 if self._ro: 430 raise ValueError("Property '%s' is read-only"%(self._name,)) 431 432 if isinstance(index, slice): 433 raise TypeError("insert argument 1 is a slice") 434 435 indexes = self.__indexSetForIndex(index) 436 self._parent.willChange_valuesAtIndexes_forKey_( 437 NSKeyValueChangeRemoval, 438 indexes, self._name) 439 try: 440 return self._wrapped.pop(index) 441 finally: 442 self._parent.didChange_valuesAtIndexes_forKey_( 443 NSKeyValueChangeRemoval, 444 indexes, self._name) 445 446 def extend(self, values): 447 # XXX: This is suboptimal but correct 448 if self._ro: 449 raise ValueError("Property '%s' is read-only"%(self._name,)) 450 451 values = list(values) 452 453 indexes = NSIndexSet.alloc().initWithIndexesInRange_((len(self), len(values))) 454 455 self._parent.willChange_valuesAtIndexes_forKey_( 456 NSKeyValueChangeInsertion, 457 indexes, self._name) 458 try: 459 self._wrapped.extend(values) 460 461 finally: 462 self._parent.didChange_valuesAtIndexes_forKey_( 463 NSKeyValueChangeInsertion, 464 indexes, self._name) 465 466 def __iadd__(self, values): 467 self.extend(values) 468 return self 469 470 def __add__(self, values): 471 return self._wrapped + values 472 473 def __mul__(self, count): 474 return self._wrapped * count 475 476 def __imul__(self, count): 477 if self._ro: 478 raise ValueError("Property '%s' is read-only"%(self._name,)) 479 if not isinstance(count, (int, long)): 480 raise TypeError(count) 481 482 indexes = NSIndexSet.alloc().initWithIndexesInRange_((len(self), len(self)*(count-1))) 483 self._parent.willChange_valuesAtIndexes_forKey_( 484 NSKeyValueChangeInsertion, 485 indexes, self._name) 486 try: 487 self._wrapped.__imul__(count) 488 finally: 489 self._parent.didChange_valuesAtIndexes_forKey_( 490 NSKeyValueChangeInsertion, 491 indexes, self._name) 492 493 return self 494 495 496 def __eq__(self, other): 497 if isinstance(other, array_proxy): 498 return self._wrapped == other._wrapped 499 500 else: 501 return self._wrapped == other 502 503 def __ne__(self, other): 504 if isinstance(other, array_proxy): 505 return self._wrapped != other._wrapped 506 507 else: 508 return self._wrapped != other 509 510 def __lt__(self, other): 511 if isinstance(other, array_proxy): 512 return self._wrapped < other._wrapped 513 514 else: 515 return self._wrapped < other 516 517 def __le__(self, other): 518 if isinstance(other, array_proxy): 519 return self._wrapped <= other._wrapped 520 521 else: 522 return self._wrapped <= other 523 524 def __gt__(self, other): 525 if isinstance(other, array_proxy): 526 return self._wrapped > other._wrapped 527 528 else: 529 return self._wrapped > other 530 531 def __ge__(self, other): 532 if isinstance(other, array_proxy): 533 return self._wrapped >= other._wrapped 534 535 else: 536 return self._wrapped >= other 537 538 539 if sys.version_info[0] == 2: 540 def __cmp__(self, other): 541 if isinstance(other, array_proxy): 542 return cmp(self._wrapped, other._wrapped) 543 544 else: 545 return cmp(self._wrapped, other) 546 547 if sys.version_info[0] == 2: 548 def sort(self, cmp=None, key=None, reverse=False): 549 if self._ro: 550 raise ValueError("Property '%s' is read-only"%(self._name,)) 551 552 indexes = NSIndexSet.alloc().initWithIndexesInRange_( 553 (0, len(self._wrapped))) 554 self._parent.willChange_valuesAtIndexes_forKey_( 555 NSKeyValueChangeReplacement, 556 indexes, self._name) 557 try: 558 self._wrapped.sort(cmp=cmp, key=key, reverse=reverse) 559 finally: 560 self._parent.didChange_valuesAtIndexes_forKey_( 561 NSKeyValueChangeReplacement, 562 indexes, self._name) 563 564 else: # pragma: no cover (py3k) 565 def sort(self, key=None, reverse=False): 566 if self._ro: 567 raise ValueError("Property '%s' is read-only"%(self._name,)) 568 569 indexes = NSIndexSet.alloc().initWithIndexesInRange_( 570 (0, len(self._wrapped))) 571 self._parent.willChange_valuesAtIndexes_forKey_( 572 NSKeyValueChangeReplacement, 573 indexes, self._name) 574 try: 575 self._wrapped.sort(key=key, reverse=reverse) 576 finally: 577 self._parent.didChange_valuesAtIndexes_forKey_( 578 NSKeyValueChangeReplacement, 579 indexes, self._name) 580 581 def reverse(self): 582 if self._ro: 583 raise ValueError("Property '%s' is read-only"%(self._name,)) 584 585 indexes = NSIndexSet.alloc().initWithIndexesInRange_( 586 (0, len(self._wrapped))) 587 self._parent.willChange_valuesAtIndexes_forKey_( 588 NSKeyValueChangeReplacement, 589 indexes, self._name) 590 try: 591 self._wrapped.reverse() 592 finally: 593 self._parent.didChange_valuesAtIndexes_forKey_( 594 NSKeyValueChangeReplacement, 595 indexes, self._name) 596 597def makeArrayAccessors(name): 598 599 def countOf(self): 600 return len(getattr(self, name)) 601 602 def objectIn(self, idx): 603 return getattr(self, name)[idx] 604 605 def insert(self, value, idx): 606 getattr(self, name).insert(idx, value) 607 608 def replace(self, idx, value): 609 getattr(self, name)[idx] = value 610 611 def remove(self, idx): 612 del getattr(self, name)[idx] 613 614 return countOf, objectIn, insert, remove, replace 615 616class array_property (object_property): 617 def __init__(self, name=None, 618 read_only=False, copy=True, dynamic=False, 619 ivar=None, depends_on=None): 620 super(array_property, self).__init__(name, 621 read_only=read_only, 622 copy=copy, dynamic=dynamic, 623 ivar=ivar, depends_on=depends_on) 624 625 def __pyobjc_class_setup__(self, name, class_dict, instance_methods, class_methods): 626 super(array_property, self).__pyobjc_class_setup__(name, class_dict, instance_methods, class_methods) 627 628 629 # Insert (Mutable) Indexed Accessors 630 # FIXME: should only do the mutable bits when we're actually a mutable property 631 632 name = self._name 633 Name = name[0].upper() + name[1:] 634 635 countOf, objectIn, insert, remove, replace = makeArrayAccessors(self._name) 636 637 countOf = selector(countOf, 638 selector = ('countOf%s'%(Name,)).encode('latin1'), 639 signature = _C_NSUInteger + b'@:', 640 ) 641 countOf.isHidden = True 642 instance_methods.add(countOf) 643 644 objectIn = selector(objectIn, 645 selector = ('objectIn%sAtIndex:'%(Name,)).encode('latin1'), 646 signature = b'@@:' + _C_NSUInteger, 647 ) 648 objectIn.isHidden = True 649 instance_methods.add(objectIn) 650 651 insert = selector(insert, 652 selector = ('insertObject:in%sAtIndex:'%(Name,)).encode('latin1'), 653 signature = b'v@:@' + _C_NSUInteger, 654 ) 655 insert.isHidden = True 656 instance_methods.add(insert) 657 658 remove = selector(remove, 659 selector = ('removeObjectFrom%sAtIndex:'%(Name,)).encode('latin1'), 660 signature = b'v@:' + _C_NSUInteger, 661 ) 662 remove.isHidden = True 663 instance_methods.add(remove) 664 665 replace = selector(replace, 666 selector = ('replaceObjectIn%sAtIndex:withObject:'%(Name,)).encode('latin1'), 667 signature = b'v@:' + _C_NSUInteger + b'@', 668 ) 669 replace.isHidden = True 670 instance_methods.add(replace) 671 672 673 def __set__(self, object, value): 674 if isinstance(value, array_proxy): 675 if value._name == self._name and value._parent is object: 676 # attr.prop = attr.prop 677 return 678 679 if isinstance(value, array_proxy): 680 value = list(value) 681 682 super(array_property, self).__set__(object, value) 683 684 def __get__(self, object, owner): 685 v = object_property.__get__(self, object, owner) 686 if v is None: 687 v = list() 688 object_property.__set__(self, object, v) 689 return array_proxy(self._name, object, self, self._ro) 690 691 def __getvalue__(self, object): 692 v = object_property.__get__(self, object, None) 693 if v is None: 694 v = list() 695 object_property.__set__(self, object, v) 696 return v 697 698 699NSKeyValueUnionSetMutation = 1 700NSKeyValueMinusSetMutation = 2 701NSKeyValueIntersectSetMutation = 3 702NSKeyValueSetSetMutation = 4 703 704 705class set_proxy (collections.MutableSet): 706 __slots__ = ('_name', '__wrapped', '_parent', '_ro') 707 708 def __init__(self, name, parent, wrapped, read_only): 709 self._name = name 710 self._parent = parent 711 self._ro = read_only 712 self.__wrapped = wrapped 713 714 def __repr__(self): 715 return '<set proxy for property ' + self._name + ' ' + repr(self._wrapped) + '>' 716 717 @property 718 def _wrapped(self): 719 return self.__wrapped.__getvalue__(self._parent) 720 721 def __reduce__(self): 722 # Ensure that the proxy itself doesn't get stored 723 # in pickles. 724 return _id, (self._wrapped,) 725 726 def __getattr__(self, attr): 727 return getattr(self._wrapped, attr) 728 729 def __contains__(self, value): 730 return self._wrapped.__contains__(value) 731 732 def __iter__(self): 733 return self._wrapped.__iter__() 734 735 def __len__(self): 736 return self._wrapped.__len__() 737 738 739 def __eq__(self, other): 740 if isinstance(other, set_proxy): 741 return self._wrapped == other._wrapped 742 743 else: 744 return self._wrapped == other 745 746 def __ne__(self, other): 747 if isinstance(other, set_proxy): 748 return self._wrapped != other._wrapped 749 750 else: 751 return self._wrapped != other 752 753 def __lt__(self, other): 754 if isinstance(other, set_proxy): 755 return self._wrapped < other._wrapped 756 757 else: 758 return self._wrapped < other 759 760 def __le__(self, other): 761 if isinstance(other, set_proxy): 762 return self._wrapped <= other._wrapped 763 764 else: 765 return self._wrapped <= other 766 767 def __gt__(self, other): 768 if isinstance(other, set_proxy): 769 return self._wrapped > other._wrapped 770 771 else: 772 return self._wrapped > other 773 774 def __ge__(self, other): 775 if isinstance(other, set_proxy): 776 return self._wrapped >= other._wrapped 777 778 else: 779 return self._wrapped >= other 780 781 782 if sys.version_info[0] == 2: 783 def __cmp__(self, other): 784 raise TypeError('cannot compare sets using cmp()') 785 786 def add(self, item): 787 if self._ro: 788 raise ValueError("Property '%s' is read-only"%(self._name,)) 789 790 self._parent.willChangeValueForKey_withSetMutation_usingObjects_( 791 self._name, 792 NSKeyValueUnionSetMutation, 793 set([item]), 794 ) 795 try: 796 self._wrapped.add(item) 797 finally: 798 self._parent.didChangeValueForKey_withSetMutation_usingObjects_( 799 self._name, 800 NSKeyValueUnionSetMutation, 801 set([item]), 802 ) 803 804 def clear(self): 805 if self._ro: 806 raise ValueError("Property '%s' is read-only"%(self._name,)) 807 808 object = set(self._wrapped) 809 self._parent.willChangeValueForKey_withSetMutation_usingObjects_( 810 self._name, 811 NSKeyValueMinusSetMutation, 812 object 813 ) 814 try: 815 self._wrapped.clear() 816 finally: 817 self._parent.didChangeValueForKey_withSetMutation_usingObjects_( 818 self._name, 819 NSKeyValueMinusSetMutation, 820 object 821 ) 822 823 def difference_update(self, *others): 824 if self._ro: 825 raise ValueError("Property '%s' is read-only"%(self._name,)) 826 827 s = set() 828 s.update(*others) 829 self._parent.willChangeValueForKey_withSetMutation_usingObjects_( 830 self._name, 831 NSKeyValueMinusSetMutation, 832 s 833 ) 834 try: 835 self._wrapped.difference_update(s) 836 837 finally: 838 self._parent.didChangeValueForKey_withSetMutation_usingObjects_( 839 self._name, 840 NSKeyValueMinusSetMutation, 841 s 842 ) 843 844 845 def discard(self, item): 846 if self._ro: 847 raise ValueError("Property '%s' is read-only"%(self._name,)) 848 849 self._parent.willChangeValueForKey_withSetMutation_usingObjects_( 850 self._name, 851 NSKeyValueMinusSetMutation, 852 {item} 853 ) 854 try: 855 self._wrapped.discard(item) 856 857 finally: 858 self._parent.didChangeValueForKey_withSetMutation_usingObjects_( 859 self._name, 860 NSKeyValueMinusSetMutation, 861 {item} 862 ) 863 864 def intersection_update(self, other): 865 if self._ro: 866 raise ValueError("Property '%s' is read-only"%(self._name,)) 867 868 other = set(other) 869 870 self._parent.willChangeValueForKey_withSetMutation_usingObjects_( 871 self._name, 872 NSKeyValueIntersectSetMutation, 873 other 874 ) 875 try: 876 self._wrapped.intersection_update(other) 877 878 finally: 879 self._parent.didChangeValueForKey_withSetMutation_usingObjects_( 880 self._name, 881 NSKeyValueIntersectSetMutation, 882 other 883 ) 884 885 def pop(self): 886 if self._ro: 887 raise ValueError("Property '%s' is read-only"%(self._name,)) 888 889 try: 890 v = next(iter(self)) 891 except StopIteration: 892 raise KeyError("Empty set") 893 894 self.remove(v) 895 return v 896 897 def remove(self, item): 898 if self._ro: 899 raise ValueError("Property '%s' is read-only"%(self._name,)) 900 901 self._parent.willChangeValueForKey_withSetMutation_usingObjects_( 902 self._name, 903 NSKeyValueMinusSetMutation, 904 set([item]) 905 ) 906 try: 907 self._wrapped.remove(item) 908 909 finally: 910 self._parent.didChangeValueForKey_withSetMutation_usingObjects_( 911 self._name, 912 NSKeyValueMinusSetMutation, 913 set([item]) 914 ) 915 916 def symmetric_difference_update(self, other): 917 # NOTE: This method does not call the corresponding method 918 # of the wrapped set to ensure that we generate the right 919 # notifications. 920 if self._ro: 921 raise ValueError("Property '%s' is read-only"%(self._name,)) 922 923 other = set(other) 924 925 to_add = set() 926 to_remove = set() 927 for o in other: 928 if o in self: 929 to_remove.add(o) 930 else: 931 to_add.add(o) 932 933 self._parent.willChangeValueForKey_withSetMutation_usingObjects_( 934 self._name, 935 NSKeyValueMinusSetMutation, 936 to_remove 937 ) 938 try: 939 self._wrapped.difference_update(to_remove) 940 941 finally: 942 self._parent.didChangeValueForKey_withSetMutation_usingObjects_( 943 self._name, 944 NSKeyValueMinusSetMutation, 945 to_remove 946 ) 947 948 self._parent.willChangeValueForKey_withSetMutation_usingObjects_( 949 self._name, 950 NSKeyValueUnionSetMutation, 951 to_add 952 ) 953 try: 954 self._wrapped.update(to_add) 955 956 finally: 957 self._parent.didChangeValueForKey_withSetMutation_usingObjects_( 958 self._name, 959 NSKeyValueUnionSetMutation, 960 to_add 961 ) 962 963 def update(self, *others): 964 if self._ro: 965 raise ValueError("Property '%s' is read-only"%(self._name,)) 966 967 s = set() 968 s.update(*others) 969 970 self._parent.willChangeValueForKey_withSetMutation_usingObjects_( 971 self._name, 972 NSKeyValueUnionSetMutation, 973 s 974 ) 975 try: 976 self._wrapped.update(s) 977 978 finally: 979 self._parent.didChangeValueForKey_withSetMutation_usingObjects_( 980 self._name, 981 NSKeyValueUnionSetMutation, 982 s 983 ) 984 985 def __or__(self, other): 986 return self._wrapped | other 987 988 def __and__(self, other): 989 return self._wrapped & other 990 991 def __xor__(self, other): 992 return self._wrapped ^ other 993 994 def __sub__(self, other): 995 return self._wrapped - other 996 997 def __ior__(self, other): 998 if self._ro: 999 raise ValueError("Property '%s' is read-only"%(self._name,)) 1000 1001 self.update(other) 1002 return self 1003 1004 def __isub__(self, other): 1005 if self._ro: 1006 raise ValueError("Property '%s' is read-only"%(self._name,)) 1007 1008 self.difference_update(other) 1009 return self 1010 1011 def __ixor__(self, other): 1012 if self._ro: 1013 raise ValueError("Property '%s' is read-only"%(self._name,)) 1014 1015 self.symmetric_difference_update(other) 1016 return self 1017 1018 def __iand__(self, other): 1019 if self._ro: 1020 raise ValueError("Property '%s' is read-only"%(self._name,)) 1021 1022 self.intersection_update(other) 1023 return self 1024 1025 1026def makeSetAccessors(name): 1027 def countOf(self): 1028 return len(getattr(self, name)) 1029 1030 def enumeratorOf(self): 1031 return iter(getattr(self, name)) 1032 1033 def memberOf(self, value): 1034 collection = getattr(self, name) 1035 if value not in collection: 1036 return None 1037 1038 for item in collection: 1039 if item == value: 1040 return item 1041 1042 def add(self, value): 1043 getattr(self, name).add(value) 1044 1045 def remove(self, value): 1046 getattr(self, name).discard(value) 1047 1048 return countOf, enumeratorOf, memberOf, add, remove 1049 1050 1051class set_property (object_property): 1052 def __init__(self, name=None, 1053 read_only=False, copy=True, dynamic=False, 1054 ivar=None, depends_on=None): 1055 super(set_property, self).__init__(name, 1056 read_only=read_only, 1057 copy=copy, dynamic=dynamic, 1058 ivar=ivar, depends_on=depends_on) 1059 1060 def __get__(self, object, owner): 1061 v = object_property.__get__(self, object, owner) 1062 if v is None: 1063 v = set() 1064 object_property.__set__(self, object, v) 1065 return set_proxy(self._name, object, self, self._ro) 1066 1067 def __set__(self, object, value): 1068 if isinstance(value, set_proxy): 1069 if value._name == self._name and value._parent is object: 1070 # attr.prop = attr.prop 1071 return 1072 1073 if isinstance(value, set_proxy): 1074 value = list(value) 1075 1076 super(set_property, self).__set__(object, value) 1077 1078 def __getvalue__(self, object): 1079 v = object_property.__get__(self, object, None) 1080 if v is None: 1081 v = set() 1082 object_property.__set__(self, object, v) 1083 return v 1084 1085 def __pyobjc_class_setup__(self, name, class_dict, instance_methods, class_methods): 1086 super(set_property, self).__pyobjc_class_setup__(name, class_dict, instance_methods, class_methods) 1087 1088 # (Mutable) Unordered Accessors 1089 # FIXME: should only do the mutable bits when we're actually a mutable property 1090 1091 name = self._name 1092 Name = name[0].upper() + name[1:] 1093 1094 countOf, enumeratorOf, memberOf, add, remove = makeSetAccessors(self._name) 1095 1096 countOf = selector(countOf, 1097 selector = ('countOf%s'%(Name,)).encode('latin1'), 1098 signature = _C_NSUInteger + b'@:', 1099 ) 1100 countOf.isHidden = True 1101 instance_methods.add(countOf) 1102 1103 enumeratorOf = selector(enumeratorOf, 1104 selector = ('enumeratorOf%s'%(Name,)).encode('latin1'), 1105 signature = b'@@:', 1106 ) 1107 enumeratorOf.isHidden = True 1108 instance_methods.add(enumeratorOf) 1109 1110 memberOf = selector(memberOf, 1111 selector = ('memberOf%s:'%(Name,)).encode('latin'), 1112 signature = b'@@:@', 1113 ) 1114 memberOf.isHidden = True 1115 instance_methods.add(memberOf) 1116 1117 add1 = selector(add, 1118 selector = ('add%s:'%(Name,)).encode('latin'), 1119 signature = b'v@:@', 1120 ) 1121 add1.isHidden = True 1122 instance_methods.add(add1) 1123 1124 add2 = selector(add, 1125 selector = ('add%sObject:'%(Name,)).encode('latin1'), 1126 signature = b'v@:@', 1127 ) 1128 add2.isHidden = True 1129 instance_methods.add(add2) 1130 1131 remove1 = selector(remove, 1132 selector = ('remove%s:'%(Name,)).encode('latin1'), 1133 signature = b'v@:@', 1134 ) 1135 remove1.isHidden = True 1136 instance_methods.add(remove1) 1137 1138 remove2 = selector(remove, 1139 selector = ('remove%sObject:'%(Name,)).encode('latin'), 1140 signature = b'v@:@', 1141 ) 1142 remove2.isHidden = True 1143 instance_methods.add(remove2) 1144 1145 1146NSMutableDictionary = lookUpClass('NSMutableDictionary') 1147 1148class dict_property (object_property): 1149 def __get__(self, object, owner): 1150 v = object_property.__get__(self, object, owner) 1151 if v is None: 1152 v = NSMutableDictionary.alloc().init() 1153 object_property.__set__(self, object, v) 1154 return object_property.__get__(self, object, owner) 1155