1""" 2This module implements a callback function that is used by the C code to 3add Python special methods to Objective-C classes with a suitable interface. 4 5This module contains no user callable code. 6 7TODO: 8- Add external interface: Framework specific modules may want to add to this. 9 10- These are candidates for implementation: 11 12 >>> from Foundation import * 13 >>> set(dir(list)) - set(dir(NSMutableArray)) 14 set(['__delslice__', '__imul__', '__getslice__', '__setslice__', 15 '__iadd__', '__mul__', '__add__', '__rmul__']) 16 >>> set(dir(dict)) - set(dir(NSMutableDictionary)) 17 set(['__cmp__']) 18 19""" 20from objc._objc import _setClassExtender, selector, lookUpClass, currentBundle, repythonify, splitSignature, _block_call 21from objc._objc import registerMetaDataForSelector 22from itertools import imap 23import sys 24 25__all__ = ( 'addConvenienceForSelector', 'addConvenienceForClass' ) 26 27 28# 12041508: NSDictionary now defines both objectForKey: and containsObject:, 29# so that the defining of __contains__ is order dependent (and often wrong). 30# So we define an OVERRIDE dict, whose keys are pipe-separated, triple strings: 31# 32# 1) the python method in question 33# 2) the first selector 34# 3) the overriding selector 35# 36# So "__contains__|containsObject:|objectForKey:" means that if containsObject: 37# was first, and has already defined __contains__, the objectForKey: can 38# later override the definition of __contains__ (but not in the reverse order). 39OVERRIDE = {'__contains__|containsObject:|objectForKey:': 1} 40def _canOverride(meth, owner, sel): 41 if not meth in owner: 42 return False 43 k = meth + '|' + owner[meth] + '|' + sel 44 return k in OVERRIDE 45 46CONVENIENCE_METHODS = {} 47CLASS_METHODS = {} 48 49def addConvenienceForSelector(selector, methods): 50 """ 51 Add the list with methods to every class that has a selector with the 52 given name. 53 """ 54 CONVENIENCE_METHODS[selector] = methods 55 56def addConvenienceForClass(classname, methods): 57 """ 58 Add the list with methods to the class with the specified name 59 """ 60 CLASS_METHODS[classname] = methods 61 62NSObject = lookUpClass('NSObject') 63 64def add_convenience_methods(super_class, name, type_dict): 65 try: 66 return _add_convenience_methods(super_class, name, type_dict) 67 except: 68 import traceback 69 traceback.print_exc() 70 raise 71 72def _add_convenience_methods(super_class, name, type_dict): 73 """ 74 Add additional methods to the type-dict of subclass 'name' of 75 'super_class'. 76 77 CONVENIENCE_METHODS is a global variable containing a mapping from 78 an Objective-C selector to a Python method name and implementation. 79 80 CLASS_METHODS is a global variable containing a mapping from 81 class name to a list of Python method names and implementation. 82 83 Matching entries from both mappings are added to the 'type_dict'. 84 """ 85 if type_dict.get('__objc_python_subclass__'): 86 if 'bundleForClass' not in type_dict: 87 cb = currentBundle() 88 def bundleForClass(cls): 89 return cb 90 type_dict['bundleForClass'] = selector(bundleForClass, isClassMethod=True) 91 if '__bundle_hack__' in type_dict: 92 import warnings 93 warnings.warn( 94 "__bundle_hack__ is not necessary in PyObjC 1.3+ / py2app 0.1.8+", 95 DeprecationWarning) 96 97 look_at_super = (super_class is not None and super_class.__name__ != 'Object') 98 99 owner = {} 100 for k, sel in type_dict.items(): 101 if not isinstance(sel, selector): 102 continue 103 104 # 105 # Handle some common exceptions to the usual rules: 106 # 107 108 sel = sel.selector 109 110 if sel in CONVENIENCE_METHODS: 111 v = CONVENIENCE_METHODS[sel] 112 for nm, value in v: 113 if nm in type_dict and isinstance(type_dict[nm], selector): 114 115 # Clone attributes of already existing version 116 117 t = type_dict[nm] 118 v = selector(value, selector=t.selector, 119 signature=t.signature, isClassMethod=t.isClassMethod) 120 121 type_dict[nm] = v 122 123 elif look_at_super and hasattr(super_class, nm): 124 # Skip, inherit the implementation from a super_class 125 pass 126 127 elif nm not in type_dict or _canOverride(nm, owner, sel): 128 type_dict[nm] = value 129 owner[nm] = sel 130 131 if name in CLASS_METHODS: 132 for nm, value in CLASS_METHODS[name]: 133 type_dict[nm] = value 134 135 136 if name == 'NSObject': 137 class kvc (object): 138 """ 139 Key-Value-Coding accessor for Cocoa objects. 140 141 Both attribute access and dict-like indexing will attempt to 142 access the requested item through Key-Value-Coding. 143 """ 144 __slots__ = ('__object',) 145 def __init__(self, value): 146 self.__object = value 147 148 def __repr__(self): 149 return "<KVC accessor for %r>"%(self.__object,) 150 151 def __getattr__(self, key): 152 try: 153 return self.__object.valueForKey_(key) 154 except KeyError, msg: 155 if hasattr(msg, '_pyobjc_info_') and msg._pyobjc_info_['name'] == 'NSUnknownKeyException': 156 raise AttributeError(key) 157 158 raise 159 def __setattr__(self, key, value): 160 if not key.startswith('_'): 161 return self.__object.setValue_forKey_(value, key) 162 else: 163 super(kvc, self).__setattr__(key, value) 164 165 def __getitem__(self, key): 166 if not isinstance(key, (str, unicode)): 167 raise TypeError("Key must be string") 168 return self.__object.valueForKey_(key) 169 170 def __setitem__(self, key, value): 171 if not isinstance(key, (str, unicode)): 172 raise TypeError("Key must be string") 173 return self.__object.setValue_forKey_(value, key) 174 175 type_dict['_'] = property(kvc) 176 177_setClassExtender(add_convenience_methods) 178 179 180# 181# The following conveniences should strictly speaking be in 182# in pyobjc-framework-Foundation, but as they are very fundamental 183# we're keeping them here. 184# 185 186def __getitem__objectForKey_(self, key): 187 res = self.objectForKey_(container_wrap(key)) 188 return container_unwrap(res, KeyError, key) 189 190def has_key_objectForKey_(self, key): 191 res = self.objectForKey_(container_wrap(key)) 192 return res is not None 193 194def get_objectForKey_(self, key, dflt=None): 195 res = self.objectForKey_(container_wrap(key)) 196 if res is None: 197 res = dflt 198 return res 199 200CONVENIENCE_METHODS[b'objectForKey:'] = ( 201 ('__getitem__', __getitem__objectForKey_), 202 ('has_key', has_key_objectForKey_), 203 ('get', get_objectForKey_), 204 ('__contains__', has_key_objectForKey_), 205) 206 207def __delitem__removeObjectForKey_(self, key): 208 self.removeObjectForKey_(container_wrap(key)) 209 210CONVENIENCE_METHODS[b'removeObjectForKey:'] = ( 211 ('__delitem__', __delitem__removeObjectForKey_), 212) 213 214def update_setObject_forKey_(self, *args, **kwds): 215 # XXX - should this be more flexible? 216 if len(args) == 0: 217 pass 218 elif len(args) != 1: 219 raise TypeError("update expected at most 1 arguments, got {0}".format( 220 len(args))) 221 222 223 else: 224 other = args[0] 225 if hasattr(other, 'keys'): 226 # This mirrors the implementation of dict.update, but seems 227 # wrong for Python3 (with collectons.Dict) 228 for key in other.keys(): 229 self[key] = other[key] 230 231 else: 232 for key, value in other: 233 self[key] = value 234 235 for k, v in kwds.iteritems(): 236 self[k] = v 237 238def setdefault_setObject_forKey_(self, key, dflt=None): 239 try: 240 return self[key] 241 except KeyError: 242 self[key] = dflt 243 return dflt 244 245def __setitem__setObject_forKey_(self, key, value): 246 self.setObject_forKey_(container_wrap(value), container_wrap(key)) 247 248pop_setObject_dflt=object() 249def pop_setObject_forKey_(self, key, dflt=pop_setObject_dflt): 250 try: 251 res = self[key] 252 except KeyError: 253 if dflt == pop_setObject_dflt: 254 raise KeyError(key) 255 res = dflt 256 else: 257 del self[key] 258 return res 259 260NSAutoreleasePool = lookUpClass('NSAutoreleasePool') 261 262def popitem_setObject_forKey_(self): 263 try: 264 it = self.keyEnumerator() 265 k = container_unwrap(it.nextObject(), StopIteration) 266 except (StopIteration, IndexError): 267 raise KeyError, "popitem on an empty %s" % (type(self).__name__,) 268 else: 269 result = (k, container_unwrap(self.objectForKey_(k), KeyError)) 270 self.removeObjectForKey_(k) 271 return result 272 273CONVENIENCE_METHODS[b'setObject:forKey:'] = ( 274 ('__setitem__', __setitem__setObject_forKey_), 275 ('update', update_setObject_forKey_), 276 ('setdefault', setdefault_setObject_forKey_), 277 ('pop', pop_setObject_forKey_), 278 ('popitem', popitem_setObject_forKey_), 279) 280 281 282CONVENIENCE_METHODS[b'count'] = ( 283 ('__len__', lambda self: self.count()), 284) 285 286CONVENIENCE_METHODS[b'containsObject:'] = ( 287 ('__contains__', lambda self, elem: bool(self.containsObject_(container_wrap(elem)))), 288) 289 290 291 292def objc_hash(self, _max=sys.maxint, _const=((sys.maxint + 1L) * 2L)): 293 rval = self.hash() 294 if rval > _max: 295 rval -= _const 296 # -1 is not a valid hash in Python and hash(x) will 297 # translate a hash of -1 to -2, so we might as well 298 # do it here so that it's not too surprising.. 299 if rval == -1: 300 rval = -2 301 return int(rval) 302CONVENIENCE_METHODS[b'hash'] = ( 303 ('__hash__', objc_hash), 304) 305 306CONVENIENCE_METHODS[b'isEqualTo:'] = ( 307 ('__eq__', lambda self, other: bool(self.isEqualTo_(other))), 308) 309 310CONVENIENCE_METHODS[b'isEqual:'] = ( 311 ('__eq__', lambda self, other: bool(self.isEqual_(other))), 312) 313 314CONVENIENCE_METHODS[b'isGreaterThan:'] = ( 315 ('__gt__', lambda self, other: bool(self.isGreaterThan_(other))), 316) 317 318CONVENIENCE_METHODS[b'isGreaterThanOrEqualTo:'] = ( 319 ('__ge__', lambda self, other: bool(self.isGreaterThanOrEqualTo_(other))), 320) 321 322CONVENIENCE_METHODS[b'isLessThan:'] = ( 323 ('__lt__', lambda self, other: bool(self.isLessThan_(other))), 324) 325 326CONVENIENCE_METHODS[b'isLessThanOrEqualTo:'] = ( 327 ('__le__', lambda self, other: bool(self.isLessThanOrEqualTo_(other))), 328) 329 330CONVENIENCE_METHODS[b'isNotEqualTo:'] = ( 331 ('__ne__', lambda self, other: bool(self.isNotEqualTo_(other))), 332) 333 334CONVENIENCE_METHODS[b'length'] = ( 335 ('__len__', lambda self: self.length()), 336) 337 338CONVENIENCE_METHODS[b'addObject:'] = ( 339 ('append', lambda self, item: self.addObject_(container_wrap(item))), 340) 341 342def reverse_exchangeObjectAtIndex_withObjectAtIndex_(self): 343 begin = 0 344 end = len(self) - 1 345 while begin < end: 346 self.exchangeObjectAtIndex_withObjectAtIndex_(begin, end) 347 begin += 1 348 end -= 1 349 350CONVENIENCE_METHODS[b'exchangeObjectAtIndex:withObjectAtIndex:'] = ( 351 ('reverse', reverse_exchangeObjectAtIndex_withObjectAtIndex_), 352) 353 354def ensureArray(anArray): 355 if not isinstance(anArray, (NSArray, list, tuple)): 356 anArray = list(anArray) 357 return anArray 358 359 360def extend_addObjectsFromArray_(self, anArray): 361 self.addObjectsFromArray_(ensureArray(anArray)) 362 363CONVENIENCE_METHODS[b'addObjectsFromArray:'] = ( 364 ('extend', extend_addObjectsFromArray_), 365) 366 367_index_sentinel=object() 368def index_indexOfObject_inRange_(self, item, start=0, stop=_index_sentinel): 369 #from Foundation import NSNotFound 370 NSNotFound = sys.maxsize 371 if start == 0 and stop is _index_sentinel: 372 res = self.indexOfObject_(container_wrap(item)) 373 if res == NSNotFound: 374 raise ValueError("%s.index(x): x not in list" % (type(self).__name__,)) 375 else: 376 l = len(self) 377 if start < 0: 378 start = l + start 379 if start < 0: 380 start = 0 381 382 if stop is not _index_sentinel: 383 if stop < 0: 384 stop = l + stop 385 if stop < 0: 386 stop = 0 387 else: 388 stop = l 389 390 itemcount = len(self) 391 392 if itemcount == 0: 393 raise ValueError("%s.index(x): x not in list" % (type(self).__name__,)) 394 395 else: 396 if start >= itemcount: 397 start = itemcount - 1 398 if stop >= itemcount: 399 stop = itemcount - 1 400 401 if stop <= start: 402 ln = 0 403 else: 404 405 ln = stop - start 406 407 408 if ln == 0: 409 raise ValueError("%s.index(x): x not in list" % (type(self).__name__,)) 410 411 if ln > sys.maxint: 412 ln = sys.maxint 413 414 res = self.indexOfObject_inRange_(item, (start, ln)) 415 if res == NSNotFound: 416 raise ValueError("%s.index(x): x not in list" % (type(self).__name__,)) 417 return res 418 419CONVENIENCE_METHODS[b'indexOfObject:inRange:'] = ( 420 ('index', index_indexOfObject_inRange_), 421) 422 423def insert_insertObject_atIndex_(self, idx, item): 424 if idx < 0: 425 idx += len(self) 426 if idx < 0: 427 raise IndexError("list index out of range") 428 self.insertObject_atIndex_(container_wrap(item), idx) 429 430CONVENIENCE_METHODS[b'insertObject:atIndex:'] = ( 431 ( 'insert', insert_insertObject_atIndex_), 432) 433 434def __getitem__objectAtIndex_(self, idx): 435 if isinstance(idx, slice): 436 start, stop, step = idx.indices(len(self)) 437 #if step == 1: 438 # m = getattr(self, 'subarrayWithRange_', None) 439 # if m is not None: 440 # return m((start, stop - start)) 441 return [self[i] for i in xrange(start, stop, step)] 442 443 elif not isinstance(idx, (int, long)): 444 raise TypeError("index must be a number") 445 446 if idx < 0: 447 idx += len(self) 448 if idx < 0: 449 raise IndexError("list index out of range") 450 451 return container_unwrap(self.objectAtIndex_(idx), RuntimeError) 452 453def __getslice__objectAtIndex_(self, i, j): 454 i = max(i, 0); j = max(j, 0) 455 return __getitem__objectAtIndex_(self, slice(i, j)) 456 457CONVENIENCE_METHODS[b'objectAtIndex:'] = ( 458 ('__getitem__', __getitem__objectAtIndex_), 459 ('__getslice__', __getslice__objectAtIndex_), 460) 461 462def __delitem__removeObjectAtIndex_(self, idx): 463 if isinstance(idx, slice): 464 start, stop, step = idx.indices(len(self)) 465 if step == 1: 466 if start > stop: 467 start, stop = stop, start 468 m = getattr(self, 'removeObjectsInRange_', None) 469 if m is not None: 470 m((start, stop - start)) 471 return 472 r = range(start, stop, step) 473 r.sort() 474 r.reverse() 475 for i in r: 476 self.removeObjectAtIndex_(i) 477 return 478 if idx < 0: 479 idx += len(self) 480 if idx < 0: 481 raise IndexError("list index out of range") 482 483 self.removeObjectAtIndex_(idx) 484 485def __delslice__removeObjectAtIndex_(self, i, j): 486 __delitem__removeObjectAtIndex_(self, slice(i, j)) 487 488def pop_removeObjectAtIndex_(self, idx=-1): 489 length = len(self) 490 if length <= 0: 491 raise IndexError("pop from empty list") 492 elif idx >= length or (idx + length) < 0: 493 raise IndexError("pop index out of range") 494 elif idx < 0: 495 idx += len(self) 496 if idx < 0: 497 raise IndexError("list index out of range") 498 rval = self[idx] 499 self.removeObjectAtIndex_(idx) 500 return rval 501 502def remove_removeObjectAtIndex_(self, obj): 503 idx = self.index(obj) 504 self.removeObjectAtIndex_(idx) 505 506CONVENIENCE_METHODS[b'removeObjectAtIndex:'] = ( 507 ('remove', remove_removeObjectAtIndex_), 508 ('pop', pop_removeObjectAtIndex_), 509 ('__delitem__', __delitem__removeObjectAtIndex_), 510 ('__delslice__', __delslice__removeObjectAtIndex_), 511) 512 513def __setitem__replaceObjectAtIndex_withObject_(self, idx, anObject): 514 if isinstance(idx, slice): 515 start, stop, step = idx.indices(len(self)) 516 if step >=0: 517 if stop <= start: 518 # Empty slice: insert values 519 stop = start 520 elif start <= stop: 521 start = stop 522 523 if step == 1: 524 m = getattr(self, 'replaceObjectsInRange_withObjectsFromArray_', None) 525 if m is not None: 526 m((start, stop - start), ensureArray(anObject)) 527 return 528 529 if not isinstance(anObject, (NSArray, list, tuple)): 530 anObject = list(anObject) 531 532 slice_len = len(xrange(start, stop, step)) 533 if slice_len != len(anObject): 534 raise ValueError("Replacing extended slice with %d elements by %d elements"%( 535 slice_len, len(anObject))) 536 537 if step > 0: 538 if anObject is self: 539 toAssign = list(anObject) 540 else: 541 toAssign = anObject 542 for inIdx, outIdx in enumerate(xrange(start, stop, step)): 543 self.replaceObjectAtIndex_withObject_(outIdx, toAssign[inIdx]) 544 545 elif step == 0: 546 raise ValueError("Step 0") 547 548 else: 549 if anObject is self: 550 toAssign = list(anObject) 551 else: 552 toAssign = anObject 553 #for inIdx, outIdx in reversed(enumerate(reversed(range(start, stop, step)))): 554 for inIdx, outIdx in enumerate(xrange(start, stop, step)): 555 self.replaceObjectAtIndex_withObject_(outIdx, toAssign[inIdx]) 556 557 558 elif not isinstance(idx, (int, long)): 559 raise TypeError("index is not an integer") 560 561 else: 562 563 if idx < 0: 564 idx += len(self) 565 if idx < 0: 566 raise IndexError("list index out of range") 567 568 self.replaceObjectAtIndex_withObject_(idx, anObject) 569 570def __setslice__replaceObjectAtIndex_withObject_(self, i, j, seq): 571 i = max(i, 0) 572 j = max(j, 0) 573 __setitem__replaceObjectAtIndex_withObject_(self, slice(i, j), seq) 574 575CONVENIENCE_METHODS[b'replaceObjectAtIndex:withObject:'] = ( 576 ('__setitem__', __setitem__replaceObjectAtIndex_withObject_), 577 ('__setslice__', __setslice__replaceObjectAtIndex_withObject_), 578) 579 580def enumeratorGenerator(anEnumerator): 581 while True: 582 yield container_unwrap(anEnumerator.nextObject(), StopIteration) 583 584def dictItems(aDict): 585 """ 586 NSDictionary.items() 587 """ 588 keys = aDict.allKeys() 589 return zip(keys, imap(aDict.__getitem__, keys)) 590 591#CONVENIENCE_METHODS[b'allKeys'] = ( 592# ('keys', lambda self: self.allKeys()), 593# ('items', lambda self: dictItems(self)), 594#) 595 596#CONVENIENCE_METHODS[b'allValues'] = ( 597 #('values', lambda self: self.allValues()), 598#) 599 600def itemsGenerator(aDict): 601 for key in aDict: 602 yield (key, aDict[key]) 603 604def __iter__objectEnumerator_keyEnumerator(self): 605 meth = getattr(self, 'keyEnumerator', None) 606 if meth is None: 607 meth = self.objectEnumerator 608 return iter(meth()) 609 610CONVENIENCE_METHODS[b'keyEnumerator'] = ( 611 ('__iter__', __iter__objectEnumerator_keyEnumerator), 612 ('iterkeys', lambda self: iter(self.keyEnumerator())), 613 ('iteritems', lambda self: itemsGenerator(self)), 614) 615 616CONVENIENCE_METHODS[b'objectEnumerator'] = ( 617 ('__iter__', __iter__objectEnumerator_keyEnumerator), 618 ('itervalues', lambda self: iter(self.objectEnumerator())), 619) 620 621CONVENIENCE_METHODS[b'reverseObjectEnumerator'] = ( 622 ('__reversed__', lambda self: iter(self.reverseObjectEnumerator())), 623) 624 625CONVENIENCE_METHODS[b'removeAllObjects'] = ( 626 ('clear', lambda self: self.removeAllObjects()), 627) 628 629CONVENIENCE_METHODS[b'dictionaryWithDictionary:'] = ( 630 ('copy', lambda self: type(self).dictionaryWithDictionary_(self)), 631) 632 633CONVENIENCE_METHODS[b'nextObject'] = ( 634 ('__iter__', enumeratorGenerator), 635) 636 637# 638# NSNumber seems to be and abstract base-class that is implemented using 639# NSCFNumber, a CoreFoundation 'proxy'. 640# 641NSNull = lookUpClass('NSNull') 642NSArray = lookUpClass('NSArray') 643#null = NSNull.null() 644 645number_wrap = repythonify 646 647def container_wrap(v): 648 if v is None: 649 return NSNull.null() 650 return v 651 652def container_unwrap(v, exc_type, *exc_args): 653 if v is None: 654 raise exc_type(*exc_args) 655 elif v is NSNull.null(): 656 return None 657 return v 658 659 660def fromkeys_dictionaryWithObjects_forKeys_(cls, keys, values=None): 661 if not isinstance(keys, (list, tuple)): 662 keys = list(keys) 663 if values is None: 664 values = (None,) * len(keys) 665 elif not isinstance(values, (list, tuple)): 666 values = list(values) 667 return cls.dictionaryWithObjects_forKeys_(values, keys) 668 669#CONVENIENCE_METHODS[b'dictionaryWithObjects:forKeys:'] = ( 670 #('fromkeys', 671 #classmethod(fromkeys_dictionaryWithObjects_forKeys_)), 672#) 673 674if sys.version_info[0] == 3: 675 def cmp(a, b): 676 if a == b: 677 return 0 678 elif a < b: 679 return -1 680 else: 681 return 1 682 683def sort(self, key=None, reverse=False, cmpfunc=cmp): 684 # NOTE: cmpfunc argument is for backward compatibility. 685 if key is None: 686 if reverse: 687 def doCmp(a, b, cmpfunc): 688 return -cmpfunc(a, b) 689 else: 690 def doCmp(a, b, cmpfunc): 691 return cmpfunc(a, b) 692 else: 693 # This is (a lot) slower than the algoritm used for 694 # list.sort, but so be it. 695 if reverse: 696 def doCmp(a, b, cmpfunc): 697 return -cmpfunc(key(a), key(b)) 698 else: 699 def doCmp(a, b, cmpfunc): 700 return cmpfunc(key(a), key(b)) 701 702 self.sortUsingFunction_context_(doCmp, cmpfunc) 703 704 705registerMetaDataForSelector(b"NSObject", b"sortUsingFunction:context:", 706 dict( 707 arguments={ 708 2: { 709 'callable': { 710 'reval': 'i', 711 'arguments': { 712 0: { 'type': '@' }, 713 1: { 'type': '@' }, 714 2: { 'type': '@' }, 715 } 716 }, 717 'callable_retained': False, 718 }, 719 3: { 'type': '@' }, 720 }, 721 )) 722 723 724CONVENIENCE_METHODS[b'sortUsingFunction:context:'] = ( 725 ('sort', sort), 726) 727 728CONVENIENCE_METHODS[b'hasPrefix:'] = ( 729 ('startswith', lambda self, pfx: self.hasPrefix_(pfx)), 730) 731 732CONVENIENCE_METHODS[b'hasSuffix:'] = ( 733 ('endswith', lambda self, pfx: self.hasSuffix_(pfx)), 734) 735 736 737CONVENIENCE_METHODS[b'copyWithZone:'] = ( 738 ('__copy__', lambda self: self.copyWithZone_(None)), 739) 740 741# This won't work: 742#NSKeyedArchiver = lookUpClass('NSKeyedArchiver') 743#NSKeyedUnarchiver = lookUpClass('NSKeyedUnarchiver') 744#def coder_deepcopy(self, memo): 745# buf = NSKeyedArchiver.archivedDataWithRootObject_(self) 746# result = NSKeyedUnarchiver.unarchiveObjectWithData_(buf) 747# return result 748# 749#CONVENIENCE_METHODS['encodeWithCoder:'] = ( 750# ('__deepcopy__', coder_deepcopy ), 751#) 752 753CLASS_METHODS['NSNull'] = ( 754 ('__nonzero__', lambda self: False ), 755 ('__bool__', lambda self: False ), 756) 757 758NSDecimalNumber = lookUpClass('NSDecimalNumber') 759def _makeD(v): 760 if isinstance(v, NSDecimalNumber): 761 return v 762 return NSDecimalNumber.decimalNumberWithDecimal_(v) 763 764CLASS_METHODS['NSDecimalNumber'] = ( 765 ('__add__', lambda self, other: _makeD(self.decimalValue() + other)), 766 ('__radd__', lambda self, other: _makeD(other + self.decimalValue())), 767 ('__sub__', lambda self, other: _makeD(self.decimalValue() - other)), 768 ('__rsub__', lambda self, other: _makeD(other - self.decimalValue())), 769 ('__mul__', lambda self, other: _makeD(self.decimalValue() * other)), 770 ('__rmul__', lambda self, other: _makeD(other * self.decimalValue())), 771 ('__div__', lambda self, other: _makeD(self.decimalValue() / other)), 772 ('__rdiv__', lambda self, other: _makeD(other / self.decimalValue())), 773 ('__mod__', lambda self, other: _makeD(self.decimalValue() % other)), 774 ('__rmod__', lambda self, other: _makeD(other % self.decimalValue())), 775 ('__neg__', lambda self: _makeD(-(self.decimalValue()))), 776 ('__pos__', lambda self: _makeD(+(self.decimalValue()))), 777 ('__abs__', lambda self: _makeD(abs(self.decimalValue()))), 778) 779 780def NSData__getslice__(self, i, j): 781 return self.bytes()[i:j] 782 783def NSData__getitem__(self, item): 784 buff = self.bytes() 785 try: 786 return buff[item] 787 except TypeError: 788 return buff[:][item] 789 790 791if sys.version_info[:2] <= (2,6): 792 def NSData__str__(self): 793 return self.bytes()[:] 794 795elif sys.version_info[0] == 2: 796 def NSData__str__(self): 797 return str(self.bytes().tobytes()) 798 799else: 800 def NSData__str__(self): 801 return str(self.bytes().tobytes()) 802 803 804CLASS_METHODS['NSData'] = ( 805 ('__str__', NSData__str__), 806 ('__getitem__', NSData__getitem__), 807 ('__getslice__', NSData__getslice__), 808) 809 810def NSMutableData__setslice__(self, i, j, sequence): 811 # XXX - could use replaceBytes:inRange:, etc. 812 self.mutableBytes()[i:j] = sequence 813 814def NSMutableData__setitem__(self, item, value): 815 self.mutableBytes()[item] = value 816 817CLASS_METHODS['NSMutableData'] = ( 818 ('__setslice__', NSMutableData__setslice__), 819 ('__setitem__', NSMutableData__setitem__), 820) 821 822 823def __call__(self, *args, **kwds): 824 return _block_call(self, self.__block_signature__, args, kwds) 825 826CLASS_METHODS['NSBlock'] = ( 827 ('__call__', __call__), 828) 829 830 831if sys.version_info[0] == 3 or (sys.version_info[0] == 2 and sys.version_info[1] >= 6): 832 import collections 833 834 def all_contained_in(inner, outer): 835 """ 836 Return True iff all items in ``inner`` are also in ``outer``. 837 """ 838 for v in inner: 839 if v not in outer: 840 return False 841 842 return True 843 844 class nsdict_view (object): 845 __slots__ = () 846 847 def __eq__(self, other): 848 if not isinstance(other, collections.Set): 849 return NotImplemented 850 851 if len(self) == len(other): 852 return all_contained_in(self, other) 853 854 else: 855 return False 856 857 def __ne__(self, other): 858 if not isinstance(other, collections.Set): 859 return NotImplemented 860 861 if len(self) == len(other): 862 return not all_contained_in(self, other) 863 864 else: 865 return True 866 867 def __lt__(self, other): 868 if not isinstance(other, collections.Set): 869 return NotImplemented 870 871 if len(self) < len(other): 872 return all_contained_in(self, other) 873 874 else: 875 return False 876 877 def __le__(self, other): 878 if not isinstance(other, collections.Set): 879 return NotImplemented 880 881 if len(self) <= len(other): 882 return all_contained_in(self, other) 883 884 else: 885 return False 886 887 def __gt__(self, other): 888 if not isinstance(other, collections.Set): 889 return NotImplemented 890 891 if len(self) > len(other): 892 return all_contained_in(other, self) 893 894 else: 895 return False 896 897 def __ge__(self, other): 898 if not isinstance(other, collections.Set): 899 return NotImplemented 900 901 if len(self) >= len(other): 902 return all_contained_in(other, self) 903 904 else: 905 return False 906 907 def __and__(self, other): 908 if not isinstance(other, collections.Set): 909 return NotImplemented 910 result = set(self) 911 result.intersection_update(other) 912 return result 913 914 def __or__(self, other): 915 if not isinstance(other, collections.Set): 916 return NotImplemented 917 result = set(self) 918 result.update(other) 919 return result 920 921 def __ror__(self, other): 922 if not isinstance(other, collections.Set): 923 return NotImplemented 924 result = set(self) 925 result.update(other) 926 return result 927 928 def __sub__(self, other): 929 if not isinstance(other, collections.Set): 930 return NotImplemented 931 result = set(self) 932 result.difference_update(other) 933 return result 934 935 def __xor__(self, other): 936 if not isinstance(other, collections.Set): 937 return NotImplemented 938 result = set(self) 939 result.symmetric_difference_update(other) 940 return result 941 942 collections.Set.register(nsdict_view) 943 944 class nsdict_keys(nsdict_view): 945 __slots__=('__value') 946 def __init__(self, value): 947 self.__value = value 948 949 def __repr__(self): 950 keys = list(self.__value) 951 keys.sort() 952 953 return "<nsdict_keys({0})>".format(keys) 954 955 956 def __len__(self): 957 return len(self.__value) 958 959 def __iter__(self): 960 return iter(self.__value) 961 962 def __contains__(self, value): 963 return value in self.__value 964 965 class nsdict_values(nsdict_view): 966 __slots__=('__value') 967 def __init__(self, value): 968 self.__value = value 969 970 def __repr__(self): 971 values = list(self) 972 values.sort() 973 974 return "<nsdict_values({0})>".format(values) 975 976 def __len__(self): 977 return len(self.__value) 978 979 def __iter__(self): 980 return iter(self.__value.objectEnumerator()) 981 982 def __contains__(self, value): 983 for v in iter(self): 984 if value == v: 985 return True 986 return False 987 988 class nsdict_items(nsdict_view): 989 990 __slots__=('__value') 991 992 def __init__(self, value): 993 self.__value = value 994 995 def __repr__(self): 996 values = list(self) 997 values.sort() 998 999 return "<nsdict_items({0})>".format(values) 1000 1001 def __len__(self): 1002 return len(self.__value) 1003 1004 def __iter__(self): 1005 for k in self.__value: 1006 yield (k, self.__value[k]) 1007 1008 def __contains__(self, value): 1009 for v in iter(self): 1010 if value == v: 1011 return True 1012 return False 1013 1014 collections.KeysView.register(nsdict_keys) 1015 collections.ValuesView.register(nsdict_values) 1016 collections.ItemsView.register(nsdict_items) 1017 1018 collections.Mapping.register(lookUpClass('NSDictionary')) 1019 collections.MutableMapping.register(lookUpClass('NSMutableDictionary')) 1020 1021 1022 1023 NSDictionary = lookUpClass('NSDictionary') 1024 def nsdict_fromkeys(cls, keys, value=None): 1025 keys = [container_wrap(k) for k in keys] 1026 values = [container_wrap(value)]*len(keys) 1027 1028 return NSDictionary.dictionaryWithObjects_forKeys_(values, keys) 1029 1030 NSMutableDictionary = lookUpClass('NSMutableDictionary') 1031 def nsmutabledict_fromkeys(cls, keys, value=None): 1032 result = NSMutableDictionary.dictionary() 1033 value = container_wrap(value) 1034 for k in keys: 1035 result[container_wrap(k)] = value 1036 1037 return result 1038 1039 def dict_new(cls, args, kwds): 1040 if len(args) == 0: 1041 pass 1042 1043 elif len(args) == 1: 1044 d = dict() 1045 if isinstance(args[0], collections.Mapping): 1046 items = args[0].iteritems() 1047 else: 1048 items = args[0] 1049 for k , v in items: 1050 d[container_wrap(k)] = container_wrap(v) 1051 1052 for k, v in kwds.iteritems(): 1053 d[container_wrap(k)] = container_wrap(v) 1054 1055 return cls.dictionaryWithDictionary_(d) 1056 1057 else: 1058 raise TypeError( 1059 "dict expected at most 1 arguments, got {0}".format( 1060 len(args))) 1061 if kwds: 1062 d = dict() 1063 for k, v in kwds.iteritems(): 1064 d[container_wrap(k)] = container_wrap(v) 1065 1066 return cls.dictionaryWithDictionary_(d) 1067 1068 return cls.dictionary() 1069 1070 def nsdict_new(cls, *args, **kwds): 1071 return dict_new(NSDictionary, args, kwds) 1072 1073 def nsmutabledict_new(cls, *args, **kwds): 1074 return dict_new(NSMutableDictionary, args, kwds) 1075 1076 1077 def nsdict__eq__(self, other): 1078 if not isinstance(other, collections.Mapping): 1079 return False 1080 1081 return self.isEqualToDictionary_(other) 1082 1083 def nsdict__ne__(self, other): 1084 return not nsdict__eq__(self, other) 1085 1086 def nsdict__richcmp__(self, other): 1087 return NotImplemented 1088 1089 1090 if sys.version_info[0] == 3: 1091 CLASS_METHODS['NSDictionary'] = ( 1092 ('fromkeys', classmethod(nsdict_fromkeys)), 1093 ('keys', lambda self: nsdict_keys(self)), 1094 ('values', lambda self: nsdict_values(self)), 1095 ('items', lambda self: nsdict_items(self)), 1096 ) 1097 1098 CLASS_METHODS['NSMutableDictionary'] = ( 1099 ('fromkeys', classmethod(nsmutabledict_fromkeys)), 1100 ) 1101 1102 else: 1103 CLASS_METHODS['NSDictionary'] = ( 1104 ('fromkeys', classmethod(nsdict_fromkeys)), 1105 ('viewkeys', lambda self: nsdict_keys(self)), 1106 ('viewvalues', lambda self: nsdict_values(self)), 1107 ('viewitems', lambda self: nsdict_items(self)), 1108 ('keys', lambda self: self.allKeys()), 1109 ('items', lambda self: dictItems(self)), 1110 ('values', lambda self: self.allValues()), 1111 ) 1112 1113 CLASS_METHODS['NSDictionary'] += ( 1114 ('__eq__', nsdict__eq__), 1115 ('__ne__', nsdict__ne__), 1116 ('__lt__', nsdict__richcmp__), 1117 ('__le__', nsdict__richcmp__), 1118 ('__gt__', nsdict__richcmp__), 1119 ('__ge__', nsdict__richcmp__), 1120 ) 1121 1122 NSDictionary.__new__ = nsdict_new 1123 NSMutableDictionary.__new__ = nsmutabledict_new 1124 1125 NSMutableDictionary.dictionary() 1126 1127 #FIXME: This shouldn't be necessary 1128 1129NSMutableArray = lookUpClass('NSMutableArray') 1130def nsarray_add(self, other): 1131 result = NSMutableArray.arrayWithArray_(self) 1132 result.extend(other) 1133 return result 1134 1135def nsarray_radd(self, other): 1136 result = NSMutableArray.arrayWithArray_(other) 1137 result.extend(self) 1138 return result 1139 1140def nsarray_mul(self, other): 1141 """ 1142 This tries to implement anNSArray * N 1143 somewhat efficently (and definitely more 1144 efficient that repeated appending). 1145 """ 1146 result = NSMutableArray.array() 1147 1148 if other <= 0: 1149 return result 1150 1151 n = 1 1152 tmp = self 1153 while other: 1154 if other & n != 0: 1155 result.extend(tmp) 1156 other -= n 1157 1158 if other: 1159 n <<= 1 1160 tmp = tmp.arrayByAddingObjectsFromArray_(tmp) 1161 1162 #for n in xrange(other): 1163 #result.extend(self) 1164 return result 1165 1166 1167 1168def nsarray_new(cls, sequence=None): 1169 if not sequence: 1170 return NSArray.array() 1171 1172 elif isinstance(sequence, (str, unicode)): 1173 return NSArray.arrayWithArray_(list(sequence)) 1174 1175 else: 1176 if not isinstance(sequence, (list, tuple)): 1177 # FIXME: teach bridge to treat range and other list-lik 1178 # types correctly 1179 return NSArray.arrayWithArray_(list(sequence)) 1180 1181 return NSArray.arrayWithArray_(sequence) 1182 1183def nsmutablearray_new(cls, sequence=None): 1184 if not sequence: 1185 return NSMutableArray.array() 1186 1187 elif isinstance(sequence, (str, unicode)): 1188 return NSMutableArray.arrayWithArray_(list(sequence)) 1189 1190 else: 1191 if not isinstance(sequence, (list, tuple)): 1192 # FIXME: teach bridge to treat range and other list-lik 1193 # types correctly 1194 return NSMutableArray.arrayWithArray_(list(sequence)) 1195 1196 return NSMutableArray.arrayWithArray_(sequence) 1197 1198CLASS_METHODS['NSArray'] = ( 1199 ('__add__', nsarray_add), 1200 ('__radd__', nsarray_radd), 1201 ('__mul__', nsarray_mul), 1202 ('__rmul__', nsarray_mul), 1203) 1204 1205# Force scans to ensure __new__ is set correctly 1206# FIXME: This shouldn't be necessary! 1207NSArray.__new__ = nsarray_new 1208NSMutableArray.__new__ = nsmutablearray_new 1209NSMutableArray.alloc().init() 1210#NSMutableSet.set() 1211 1212NSSet = lookUpClass('NSSet') 1213NSMutableSet = lookUpClass('NSMutableSet') 1214 1215try: 1216 from collections import Set 1217 Set.register(NSSet) 1218except: 1219 Set = (set, frozenset, NSSet) 1220 1221def nsset_isdisjoint(self, other): 1222 for item in self: 1223 if item in other: 1224 return False 1225 return True 1226 1227def nsset_union(self, *other): 1228 result = NSMutableSet() 1229 result.unionSet_(self) 1230 for val in other: 1231 if isinstance(val, Set): 1232 result.unionSet_(val) 1233 else: 1234 result.unionSet_(set(val)) 1235 return result 1236 1237def nsset_intersection(self, *others): 1238 if len(others) == 0: 1239 return self.mutableCopy() 1240 result = NSMutableSet() 1241 for item in self: 1242 for o in others: 1243 if item not in o: 1244 break 1245 else: 1246 result.add(item) 1247 return result 1248 1249def nsset_difference(self, *others): 1250 result = self.mutableCopy() 1251 1252 for value in others: 1253 if isinstance(value, Set): 1254 result.minusSet_(value) 1255 else: 1256 result.minusSet_(set(value)) 1257 1258 return result 1259 1260def nsset_symmetric_difference(self, other): 1261 result = NSMutableSet() 1262 for item in self: 1263 if item not in other: 1264 result.add(item) 1265 for item in other: 1266 if item not in self: 1267 result.add(item) 1268 return result 1269 1270 1271def nsset__contains__(self, value): 1272 hash(value) # Force error for non-hashable values 1273 return self.containsObject_(value) 1274 1275def nsset__or__(self, other): 1276 if not isinstance(self, Set): 1277 raise TypeError("NSSet|value where value is not a set") 1278 if not isinstance(other, Set): 1279 raise TypeError("NSSet|value where value is not a set") 1280 return nsset_union(self, other) 1281 1282def nsset__ror__(self, other): 1283 if not isinstance(self, Set): 1284 raise TypeError("value|NSSet where value is not a set") 1285 if not isinstance(other, Set): 1286 raise TypeError("value|NSSet where value is not a set") 1287 return nsset_union(other, self) 1288 1289def nsset__and__(self, other): 1290 if not isinstance(self, Set): 1291 raise TypeError("NSSet&value where value is not a set") 1292 if not isinstance(other, Set): 1293 raise TypeError("NSSet&value where value is not a set") 1294 return nsset_intersection(self, other) 1295 1296def nsset__rand__(self, other): 1297 if not isinstance(self, Set): 1298 raise TypeError("value&NSSet where value is not a set") 1299 if not isinstance(other, Set): 1300 raise TypeError("value&NSSet where value is not a set") 1301 return nsset_intersection(other, self) 1302 1303def nsset__sub__(self, other): 1304 if not isinstance(self, Set): 1305 raise TypeError("NSSet-value where value is not a set") 1306 if not isinstance(other, Set): 1307 raise TypeError("NSSet-value where value is not a set") 1308 return nsset_difference(self, other) 1309 1310def nsset_rsub__(self, other): 1311 if not isinstance(self, Set): 1312 raise TypeError("NSSet-value where value is not a set") 1313 if not isinstance(other, Set): 1314 raise TypeError("NSSet-value where value is not a set") 1315 return nsset_difference(other, self) 1316 1317def nsset__xor__(self, other): 1318 if not isinstance(self, Set): 1319 raise TypeError("NSSet-value where value is not a set") 1320 if not isinstance(other, Set): 1321 raise TypeError("NSSet-value where value is not a set") 1322 return nsset_symmetric_difference(other, self) 1323 1324def nsset_issubset(self, other): 1325 if isinstance(other, Set): 1326 return self.isSubsetOfSet_(other) 1327 1328 else: 1329 return self.isSubsetOfSet_(set(other)) 1330 1331def nsset__le__(self, other): 1332 if not isinstance(other, Set): 1333 raise TypeError() 1334 return nsset_issubset(self, other) 1335 1336def nsset__eq__(self, other): 1337 if not isinstance(other, Set): 1338 return False 1339 1340 return self.isEqualToSet_(other) 1341 1342def nsset__ne__(self, other): 1343 if not isinstance(other, Set): 1344 return True 1345 1346 return not self.isEqualToSet_(other) 1347 1348def nsset__lt__(self, other): 1349 if not isinstance(other, Set): 1350 raise TypeError() 1351 1352 return (self <= other) and (self != other) 1353 1354def nsset_issuperset(self, other): 1355 if not isinstance(other, Set): 1356 other = set(other) 1357 1358 for item in other: 1359 if item not in self: 1360 return False 1361 1362 return True 1363 1364def nsset__ge__(self, other): 1365 if not isinstance(other, Set): 1366 raise TypeError() 1367 return nsset_issuperset(self, other) 1368 1369def nsset__gt__(self, other): 1370 if not isinstance(other, Set): 1371 raise TypeError() 1372 return (self >= other) and (self != other) 1373 1374if sys.version_info[0] == 2: 1375 def nsset__cmp__(self, other): 1376 try: 1377 if self < other: 1378 return -1 1379 elif self == other: 1380 return 0 1381 else: 1382 return 1 1383 except TypeError: 1384 return cmp(id(self), id(other)) 1385 1386def nsset__length_hint__(self): 1387 return len(self) 1388 1389def nsset_update(self, *others): 1390 for other in others: 1391 if isinstance(other, Set): 1392 self.unionSet_(other) 1393 else: 1394 self.unionSet_(set(other)) 1395 1396def nsset_intersection_update(self, *others): 1397 for other in others: 1398 if isinstance(other, Set): 1399 self.intersectSet_(other) 1400 else: 1401 self.intersectSet_(set(other)) 1402 1403def nsset_difference_update(self, *others): 1404 for other in others: 1405 if isinstance(other, Set): 1406 self.minusSet_(other) 1407 else: 1408 self.minusSet_(set(other)) 1409 1410def nsset_symmetric_difference_update(self, other): 1411 toadd = set() 1412 toremove = set() 1413 1414 if isinstance(other, Set): 1415 totest = other 1416 else: 1417 totest = set(other) 1418 1419 for value in self: 1420 if value in totest: 1421 toremove.add(value) 1422 for value in totest: 1423 if value not in self: 1424 toadd.add(value) 1425 1426 self.minusSet_(toremove) 1427 self.unionSet_(toadd) 1428 1429def nsset_pop(self): 1430 if len(self) == 0: 1431 raise KeyError() 1432 1433 v = self.anyObject() 1434 self.removeObject_(v) 1435 return container_unwrap(v, KeyError) 1436 1437def nsset_remove(self, value): 1438 hash(value) 1439 value = container_wrap(value) 1440 if value not in self: 1441 raise KeyError(value) 1442 self.removeObject_(value) 1443 1444def nsset_discard(self, value): 1445 hash(value) 1446 self.removeObject_(container_wrap(value)) 1447 1448def nsset_add(self, value): 1449 hash(value) 1450 self.addObject_(container_wrap(value)) 1451 1452class nsset__iter__ (object): 1453 def __init__(self, value): 1454 self._size = len(value) 1455 self._enum = value.objectEnumerator() 1456 1457 def __length_hint__(self): 1458 return self._size 1459 1460 def __iter__(self): 1461 return self 1462 1463 def next(self): 1464 self._size -= 1 1465 return container_unwrap(self._enum.nextObject(), StopIteration) 1466 1467 1468CLASS_METHODS['NSSet'] = ( 1469 ('__iter__', lambda self: nsset__iter__(self)), 1470 ('__length_hint__', nsset__length_hint__), 1471 ('__contains__', nsset__contains__), 1472 ('isdisjoint', nsset_isdisjoint), 1473 ('union', nsset_union), 1474 ('intersection', nsset_intersection), 1475 ('difference', nsset_difference), 1476 ('symmetric_difference', nsset_symmetric_difference), 1477 ('issubset', nsset_issubset), 1478 ('__eq__', nsset__eq__), 1479 ('__ne__', nsset__ne__), 1480 ('__le__', nsset__le__), 1481 ('__lt__', nsset__lt__), 1482 ('issuperset', nsset_issuperset), 1483 ('__ge__', nsset__ge__), 1484 ('__gt__', nsset__gt__), 1485 ('__or__', nsset__or__), 1486 ('__ror__', nsset__ror__), 1487 ('__and__', nsset__and__), 1488 ('__rand__', nsset__rand__), 1489 ('__xor__', nsset__xor__), 1490 ('__rxor__', nsset__xor__), 1491 ('__sub__', nsset__sub__), 1492) 1493 1494if sys.version_info[0] == 2: 1495 CLASS_METHODS['NSSet'] += ( 1496 ('__cmp__', 'nsset__cmp__'), 1497 ) 1498 1499CLASS_METHODS['NSMutableSet'] = ( 1500 ('add', nsset_add), 1501 ('remove', nsset_remove), 1502 ('discard', nsset_discard), 1503 ('update', nsset_update), 1504 ('intersection_update', nsset_intersection_update), 1505 ('difference_update', nsset_difference_update), 1506 ('symmetric_difference_update', nsset_symmetric_difference_update), 1507 ('clear', lambda self: self.removeAllObjects()), 1508 ('pop', nsset_pop), 1509) 1510 1511def nsset_new(cls, sequence=None): 1512 if not sequence: 1513 return NSSet.set() 1514 1515 if isinstance(sequence, (NSSet, set, frozenset)): 1516 return NSSet.set().setByAddingObjectsFromSet_(sequence) 1517 1518 else: 1519 return NSSet.set().setByAddingObjectsFromSet_(set(sequence)) 1520 1521def nsmutableset_new(cls, sequence=None): 1522 if not sequence: 1523 value = NSMutableSet.set() 1524 1525 elif isinstance(sequence, (NSSet, set, frozenset)): 1526 value = NSMutableSet.set() 1527 value.unionSet_(sequence) 1528 1529 else: 1530 value = NSMutableSet.set() 1531 value.unionSet_(set(sequence)) 1532 1533 return value 1534 1535NSSet.__new__ = nsset_new 1536NSMutableSet.__new__ = nsmutableset_new 1537 1538NSMutableSet.alloc().init() 1539