teams-bots/write-message/venv/lib/python3.10/site-packages/objc/_convenience.py

162 lines
3.5 KiB
Python

"""
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__),))