Example #1
0
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
Example #2
0
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
Example #3
0
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
Example #4
0
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
Example #5
0
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
Example #6
0
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
Example #7
0
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