def get_methods(clas, *prefixes): '''Yield all methods of an ObjC class with a name starting with one of the given prefixes. @param clas: The class (L{Class_t}). @param prefixes: No, one or more method names to match (C{str}-s). @return: For each method yield a 4-tuple (I{name, encoding, rargtypes, method}), where I{name} is the method name, I{encoding} is the type encoding of the method signature including the return type, I{rargtypes} the C{ctypes} signature, the argtypes C{list}** preceeded by the restype and I{method} the L{Method_t} object. @note: In Python 3+ I{rargtypes} is a C{map} object, not a C{list}. ''' def _ctype(code): return emcoding2ctype(code, dflt=IMP_t) # c_void_p n = c_uint() for method in libobjc.class_copyMethodList(clas, byref(n)): sel = libobjc.method_getName(method) name = bytes2str(libobjc.sel_getName(sel)) if name.startswith(prefixes or name): # XXX should yield name, ObjCMethod instance encoding = libobjc.method_getTypeEncoding(method) rescode = libobjc.method_copyReturnType(method) if not encoding.startswith(rescode): encoding = rescode + encoding rargtypes = map(_ctype, split_encoding(encoding)) yield name, str2bytes(encoding), tuple(rargtypes), method
def get_class(name): '''Get a registered ObjC class by name. @param name: The class name (C{str}). @return: The class (L{Class_t}) if found, None otherwise. ''' return libobjc.objc_getClass(str2bytes(name)) or None
def get_protocol(name): '''Get a registered ObjC protocol by name. @param name: The protocol name (C{str}). @return: The protocol (L{Protocol_t}) if found, None otherwise. ''' return libobjc.objc_getProtocol(str2bytes(name)) or None
def get_ivars(clas, *prefixes): '''Yield all instance variables (ivar) of an ObjC class with a name starting with one of the given prefixes. @param clas: The class (L{Class_t}). @param prefixes: No, one or more ivar names to match (C{str}-s). @return: For each ivar yield a 4-tuple (I{name, encoding, ctype, ivar}) where I{name} is the ivar name, I{encoding} is the ivar's type encoding, I{ctype} is the ivar's C{ctypes} type and I{ivar} the L{Ivar_t} object. ''' n = c_uint() for ivar in libobjc.class_copyIvarList(clas, byref(n)): name = bytes2str(libobjc.ivar_getName(ivar)) if name.startswith(prefixes or name): # XXX should yield name, ObjCIvar instance encoding = libobjc.ivar_getTypeEncoding(ivar) ctype = emcoding2ctype(encoding, dflt=Ivar_t) # c_void_p yield name, str2bytes(encoding), ctype, ivar
def get_ivar(objc, name, ctype=None): '''Get the value of an instance variable (ivar). @param objc: The object (C{Object} or L{Id_t}). @param name: The instance variable name (C{str}). @keyword ctype: The instance variable type (C{ctypes}), @return: The ivar value (C{any}) if found, None otherwise. ''' if ctype is None: # lookup ivar by name ctype = _ivar_ctype(objc, name) ivar = ctype() libobjc.object_getInstanceVariable(objc, str2bytes(name), byref(ivar)) try: return ivar.value except AttributeError: if ivar: # ctype POINTER? return ivar.contents return None
def get_c_func_t(encoding, codes=None): '''Get the C{ctypes} function type for a given function signature. @param encoding: Type encoding of the signature (C{str} or C{bytes}). @keyword codes: The individual type codes (C{type encoding}[]) @note: The signature I{encoding} is a C{str} or C{bytes}, not unicode and I{codes} is a list of the individual type encodings, limited to basic type encodings and pointers to basic type encodings. Does not handle C{array}s, C{bitfield}s, arbitrary C{struct}s and C{union}s. If keyword I{codes} is not supplied, it will be created from the I{signature} by L{split_encoding}, not L{split_emcoding2}. ''' encoding = str2bytes(encoding) try: c_func_t = _c_func_t_cache[encoding] except KeyError: # create new CFUNCTYPE for the encoding c_func_t = CFUNCTYPE(*map(encoding2ctype, codes or split_encoding(encoding))) # XXX cache new CFUNCTYPE (to prevent it to be gc'd?) _c_func_t_cache[encoding] = c_func_t return c_func_t
def split_encoding(encoding): # MCCABE 18 '''Split a type encoding into separate type encodings. Does not handle C{bitfield}s, C{array}s, C{struct}s, C{union}s, etc. and strips any offset, size or width specifiers from the encoding. @return: The individual type encodings (C{list}). @raise TypeCodeError: Invalid or unbalanced I{encoding}. @example: >>> split_encoding('^v16@0:8') >>> ['^v', '@', ':'] >>> split_encoding('{CGSize=dd}40@0:8{PyObject=@}Q32') >>> ['{CGSize=dd}', '@', ':', '{PyObject=@}', 'Q'] Supported Type Encodings: - B = bool (C++ bool, C99 _Bool) - c, C = char, unsigned char - f, d = float, double - i, I = int, unsigned int - l, L = long, unsigned long (32-bit in 64-bit Apps) - q, Q = long long, unsigned long long - s, S = short, unsigned short - t, T = 128-bit int, unsigned int - v = void - * = string (char *) - : = method selector C{SEL/cmd} - # = class - #"name" = class "name" - @ = object instance C{Id/self} or statically typed - @"name" = instance C{Id/self} of class "name" - ^type = pointer to type - ? = unknown type (among other things, used for function pointers) PyCocoa specific: - P = Python object, shorthand for C{PyObjectEncoding} Unsupported Type Encodings: - bW = bitfield of width W - [Ltype] = array of L items of type - E{lb}name=type...E{rb} = structure - (name=type...) = union - <...> = block - ?<...> = block with signature Unknown or for ObjC internal use: - A = ? - j = ? - n, N = in, inout - o, O = out, bycopy - r, R = const, byref - V = oneway @note: Type encodings may be preceeded by C{"name"}, for example a C{bitfield} C{"name"b1}, C{struct} items C{E{lb}CGsize="width"d"heigth"dE{rb}}, C{union} items, etc. and all such C{"name"} prefixes are ignored. @see: U{Type Encodings<https://Developer.Apple.com/library/content/documentation/ Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html>}, U{NSHipster Type Encodings<https://NSHipster.com/type-encodings>} and U{Digits in type encoding<https://StackOverflow.com/questions/11527385/ how-are-the-digits-in-objc-method-type-encoding-calculated>}. ''' code = [] codes = [] opened = [] # opened braces, brackets, parensm etc. quoted = False # inside double quotes for b in iterbytes(str2bytes(encoding)): if b in _TYPEOPENERS: if code and code[-1] != b'^' and not opened: codes.append(_join(code)) code = [] opened.append(_TYPE2CLOSER[b]) code.append(b) elif b in _TYPECLOSERS: code.append(b) if not opened or b != opened.pop(): raise TypeCodeError('encoding %s: %r' % ('unbalanced', bytes2str(_join(code)))) if not opened: codes.append(_join(code)) code = [] elif opened: # inside braces, etc # XXX ignore digits? code.append(b) # stick anything on elif b == b'"': code.append(b) if quoted: # closing quotes code = _join(code) if code[:2] in (b'@"', b'#"'): # XXX only @"..." and #"..." are OK # XXX what about ^@"..." and ^#"..."? codes.append(code) elif code[:1] == b'"': pass # ignore prefix "name" else: raise TypeCodeError('encoding %s: %r' % ('invalid', bytes2str(code))) code = [] quoted = not quoted elif quoted: # inside quotes # XXX only alphanumeric, '_', '.'? code.append(b) # stick anything on elif b in _TYPECODESET: if code and code[-1] != b'^': # not a pointer, previous char != '^' codes.append(_join(code)) code = [] code.append(b) elif b in _TYPESKIPPED: pass # ignore type, width and offsets if opened: raise TypeCodeError('encoding %s: %r' % ('unbalanced', bytes2str(encoding))) if code: # final type code codes.append(_join(code)) return codes