""" This module implements a callback function that is used by the C code to add Python special methods to Objective-C classes with a suitable interface. """ from objc._objc import ( _block_call, _rescanClass, currentBundle, lookUpClass, options, registerMetaDataForSelector, selector, ) import PyObjCTools.KeyValueCoding as kvc __all__ = ("addConvenienceForClass", "registerABCForClass") CLASS_METHODS = {} CLASS_ABC = {} options._getKey = kvc.getKey options._setKey = kvc.setKey options._getKeyPath = kvc.getKeyPath options._setKeyPath = kvc.setKeyPath del kvc for method in ( b"alloc", b"copy", b"copyWithZone:", b"mutableCopy", b"mutableCopyWithZone:", ): registerMetaDataForSelector( b"NSObject", method, {"retval": {"already_retained": True}} ) def register(f): options._class_extender = f @register def add_convenience_methods(cls, type_dict): """ Add additional methods to the type-dict of subclass 'name' of 'super_class'. CLASS_METHODS is a global variable containing a mapping from class name to a list of Python method names and implementation. Matching entries from both mappings are added to the 'type_dict'. """ for nm, value in CLASS_METHODS.get(cls.__name__, ()): type_dict[nm] = value try: for abc_class in CLASS_ABC[cls.__name__]: abc_class.register(cls) del CLASS_ABC[cls.__name__] except KeyError: pass def register(f): options._make_bundleForClass = f @register def makeBundleForClass(): cb = currentBundle() def bundleForClass(cls): return cb return selector(bundleForClass, isClassMethod=True) def registerABCForClass(classname, *abc_class): """ Register *classname* with the *abc_class*-es when the class becomes available. """ global CLASS_ABC try: CLASS_ABC[classname] += tuple(abc_class) except KeyError: CLASS_ABC[classname] = tuple(abc_class) options._mapping_count += 1 _rescanClass(classname) def addConvenienceForClass(classname, methods): """ Add the list with methods to the class with the specified name """ try: CLASS_METHODS[classname] += tuple(methods) except KeyError: CLASS_METHODS[classname] = tuple(methods) options._mapping_count += 1 _rescanClass(classname) # # Helper functions for converting data item to/from a representation # that is usable inside Cocoa data structures. # # In particular: # # - Python "None" is stored as +[NSNull null] because Cocoa containers # won't store NULL as a value (and this transformation is undone when # retrieving data) # # - When a getter returns NULL in Cocoa the queried value is not present, # that's converted to an exception in Python. # _NULL = lookUpClass("NSNull").null() def container_wrap(v): if v is None: return _NULL return v def container_unwrap(v, exc_type, *exc_args): if v is None: raise exc_type(*exc_args) elif v is _NULL: return None return v # # # Misc. small helpers # # addConvenienceForClass("NSNull", (("__bool__", lambda self: False),)) addConvenienceForClass( "NSEnumerator", ( ("__iter__", lambda self: self), ("__next__", lambda self: container_unwrap(self.nextObject(), StopIteration)), ), ) def __call__(self, *args, **kwds): return _block_call(self, self.__block_signature__, args, kwds) addConvenienceForClass("NSBlock", (("__call__", __call__),))