コード例 #1
0
ファイル: getters.py プロジェクト: mrJean1/PyCocoa
def get_properties(clas_or_proto, *prefixes):
    '''Yield all properties of an ObjC class or protocol
       with a name starting with one of the given prefixes.

       @param clas_or_proto: The class or protocol (L{Class_t} or L{Protocol_t}).
       @param prefixes: No, one or more property names to match (C{str}-s).

       @return: For each property, yield a 3-tuple (I{name}, I{attributes},
                I{setter}, I{property}) where I{attributes} is a comma-separated
                list of the property attibutes, I{setter} is the name of the
                property setter method, provided the property is writable and
                I{property} is the C{Property} object.  For read-only properties,
                the I{setter} is an empty name "".

       @note: ObjC Property Attributes:

           - T<type>"name" = Type
           - & = Retain last value (retain)
           - C = Copy
           - D = Dynamic (@dynamic)
           - G<name> = Getter selector name
           - N = Non-atomic (nonatomic)
           - P = To be garbage collected
           - R = Read-only (readonly)
           - S<name> = Setter selector name
           - t<encoding> = Old-style type encoding
           - W = Weak reference (__weak)

       @see: U{Property Attributes<https://Developer.Apple.com/library/content/documentation/
             Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html>}.
    '''
    n = c_uint()
    if isinstance(clas_or_proto, Class_t):
        props = libobjc.class_copyPropertyList(clas_or_proto, byref(n))
        setters = set(_[0] for _ in get_methods(clas_or_proto, 'set'))
    elif isinstance(clas_or_proto, Protocol_t):
        props = libobjc.protocol_copyPropertyList(clas_or_proto, byref(n))
        setters = []
    else:
        raise TypeError('%s not a %s nor %s: %r' %
                        ('clas_or_proto', Class_t.__name__,
                         Protocol_t.__name__, clas_or_proto))

    for prop in props:
        name = bytes2str(libobjc.property_getName(prop))
        if name and name.startswith(prefixes or name):
            # XXX should yield name, ObjCProperty instance
            # attrs T@"type",&,C,D,G<name>,N,P,R,S<name>,W,t<encoding>,V<varname>
            attrs = bytes2str(libobjc.property_getAttributes(prop))
            attrs = '%s=(%s)' % (attrs,
                                 _COMMASPACE_.join(
                                     _PropertyAttributes.get(_[:1], _[:1]) +
                                     _[1:] for _ in attrs.split(',')))
            setter = _NN_
            if setters:
                set_ = _NN_('set', name.capitalize(), _COLON_)
                if set_ in setters:
                    setter = _NN_(_PropertyAttributes['S'], set_)
            yield name, attrs, setter, prop
コード例 #2
0
ファイル: getters.py プロジェクト: mrJean1/PyCocoa
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
コード例 #3
0
def nsBundleRename(ns_title, match='Python'):
    '''Change the bundle title if the current title matches.

       @param ns_title: New bundle title (L{NSStr}).
       @keyword match: Optional, previous title to match (C{str}).

       @return: The previous bundle title (C{str}) or None.

       @note: Used to mimick C{NSApplication.setTitle_(ns_title)},
              the name of an L{App} shown in the menu bar.
    '''
    t = ns_title and ns2py(ns_title)
    if t:
        _Globals.argv0 = bytes2str(t)

    # <https://Developer.Apple.com/documentation/
    #        foundation/nsbundle/1495012-bundlewithpath>
    # ns = NSBundle.bundleWithPath_(os.path.abspath(match))
    p, ns = None, NSMain.Bundle
    if ns:
        ns = ns.localizedInfoDictionary() or ns.infoDictionary()
        if ns:
            p = ns.objectForKey_(NSMain.BundleName) or None
            if p:
                p = ns2py(p, dflt='') or ''
                if t and match in (p, '', None):  # can't be empty
                    ns.setObject_forKey_(ns_title, NSMain.BundleName)
    return p
コード例 #4
0
ファイル: getters.py プロジェクト: mrJean1/PyCocoa
def get_selectornameof(sel):
    '''Get the name of an ObjC selector.

       @param sel: The selector (L{SEL_t}).

       @return: The selector name (C{str}) if found, C{""} otherwise.
    '''
    isinstanceOf(sel, SEL_t, name='sel')
    return bytes2str(libobjc.sel_getName(sel)) or _NN_
コード例 #5
0
    def __init__(self, title='Main', screen=None, fraction=0.5,
                                     frame=None, excl=0, auto=False, **kwds):
        '''Create a new L{Window}.

           @keyword title: Window title (C{str}).
           @keyword screen: The screen to place the window on (C{int}) or
                            C{None} for the current one.  Use C{screen=0}
                            for the built-in screen or C{screen=1} for the
                            first external monitor, etc.
           @keyword fraction: Window size as fraction of the screen (C{float}),
                              defining the window C{B{frame}}.
           @keyword frame: The window's origin and I{content} size (L{Rect},
                           L{NSRect_t}, L{NSRect4_t}), overriding B{C{fraction}}.
           @keyword excl: Window I{styles} to exclude (L{WindowStyle}C{.attribute}).
           @keyword auto: Release window resource when closed (C{bool}).
           @keyword kwds: Optional, additional keyword arguments.

           @raise WindowError: Unique C{Id} exists.
        '''
        if frame is None:
            self._frame = Frame(screen, fraction=fraction)
            self.NS = NSWindow.alloc().initWithContentRect_styleMask_backing_defer_(
                                       self._frame.NS,
                                       WindowStyle.Typical ^ excl,  # PYCHOK expected
                                       NSBackingStoreBuffered,
                                       NO)
        else:  # for .tables.TableWindow
            self._frame = Frame(frame)
            self.NS = NSWindow.alloc().initWithContentRect_styleMask_backing_defer_screen_(
                                       self._frame.NS,
                                       WindowStyle.Typical ^ excl,  # PYCHOK expected
                                       NSBackingStoreBuffered,
                                       NO,
                                       Screens.Main.NS)  # like .tables.TableWindow
        self.title = bytes2str(title)
        self.front(True)

        if kwds:
            super(Window, self).__init__(**kwds)

        # XXX self.NS.setIdentifier_(int2NS(id(self)))
        self._NSuniqID = u = self.NS.uniqueID()
        if u in _Globals.Windows:
            raise WindowError('%s %r exists: %r' % ('.uniqueID', u,  _Globals.Windows[u]))
        elif u:
            _Globals.Windows[u] = self

        if _Globals.App and not self.app:
            self.app = _Globals.App

        if auto:
            self.NS.setReleasedWhenClosed_(YES)
            self._auto = True

        self.NSdelegate = retain(NSWindowDelegate.alloc().init(self))
コード例 #6
0
ファイル: getters.py プロジェクト: mrJean1/PyCocoa
def get_classname(clas, dflt=missing):
    '''Get the name of an ObjC class.

       @param clas: The class (L{Class_t}).

       @return: The class name (C{str}).

       @raise ValueError: Invalid I{clas}, iff no I{dflt} provided.
    '''
    if clas and isinstanceOf(clas, Class_t, name='clas'):
        return bytes2str(libobjc.class_getName(clas))
    if dflt is missing:
        raise ValueError('no such %s: %r' % ('Class', clas))
    return dflt
コード例 #7
0
ファイル: getters.py プロジェクト: mrJean1/PyCocoa
def get_method(clas, name):
    '''Get a method of an ObjC class by name.

       @param clas: Class (L{Class_t}).
       @param name: Method name (C{str}).

       @return: The method (L{Method_t}) if found, None otherwise.
    '''
    n = c_uint()
    for method in libobjc.class_copyMethodList(clas, byref(n)):
        sel = libobjc.method_getName(method)
        if bytes2str(libobjc.sel_getName(sel)) == name:
            return method
    return None
コード例 #8
0
    def __new__(cls, ustr):
        '''New L{NSStr} wrapping C{NS[Constant]String}.

           @param ustr: The string value (C{str} or C{unicode}).

           @return: The string (L{NSStr}).
        '''
        self = super(NSStr, cls).__new__(cls, cfString(ustr))
        self._str = bytes2str(ustr)
        # L{ObjCinstance} caches ObjC objects using the
        # objc_ptr.value as key, which may or may not
        # create in a singleton for each string value,
        # retaining strings seems to help singletons.
        return self if len(ustr) > 1 else retain(self)
コード例 #9
0
ファイル: getters.py プロジェクト: mrJean1/PyCocoa
def get_protocols(clas, *prefixes):
    '''Yield all protocols 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 protocol names to match (C{str}-s).

       @return: For each protocol, yield a 2-tuple (I{name}, I{protocol})
                where I{name} is the protocol name and I{protocol} the
                L{Protocol_t} object.
    '''
    n = c_uint()
    for proto in libobjc.class_copyProtocolList(clas, byref(n)):
        name = bytes2str(libobjc.protocol_getName(proto))
        if name.startswith(prefixes or name):
            # XXX should yield name, ObjCProtocol instance
            yield name, proto
コード例 #10
0
 def title(self, title):
     '''Set the title.
     '''
     if isinstance(title, NSStr):
         try:
             self.NS.setTitle_(title)
         except AttributeError:  # no NSApplication.setTitle_
             pass
         title = nsString2str(title)
     else:
         try:
             t = NSStr(title)
             self.NS.setTitle_(t)
             release(t)
         except AttributeError:  # no NSApplication.setTitle_
             t.release()
     self._title = bytes2str(title)
コード例 #11
0
def _traitsin(traits, raiser=True):
    # Convert and check traits
    if isinstance(traits, _Ints):
        ts = traits & _maskTraits
    elif isinstance(traits, _ByteStrs):
        ts = 0
        for t in bytes2str(traits).strip().split():
            m = _familyTraits.get(t.lower(), None)
            if m:
                ts |= m
            elif raiser and m is None:
                raise FontTraitError('invalid %s: %r' % ('trait', t))
    else:
        raise FontTraitError('invalid %s: %r' % ('traits', traits))
    # check for mutually exclusive traits
    if _traitex(ts, FontTrait.Condensed | FontTrait.Expanded) or \
       _traitex(ts, FontTrait.Italic    | FontTrait.UnItalic) or \
       _traitex(ts, FontTrait.Bold      | FontTrait.UnBold):
        raise FontTraitError('incompatible %s: %r' % ('traits', traits))
    return ts
コード例 #12
0
ファイル: getters.py プロジェクト: mrJean1/PyCocoa
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
コード例 #13
0
    def __init__(self, title='Main', frame=None, excl=0, auto=False, **kwds):
        '''Create a new L{Window}.

           @keyword title: Window title (C{str}).
           @keyword frame: Window frame (L{Rect}, L{NSRect_t}, L{NSRect4_t}, or None).
           @keyword excl: Window styles to exclude (L{WindowStyle}C{.attribute}).
           @keyword auto: Release window resource when closed (C{bool}).
           @keyword kwds: Optional, additional keyword arguments.

           @raise WindowError: Unique C{Id} exists.
        '''
        self._frame = Screen(0.5) if frame is None else Rect(frame)
        self._ratio = self._frame.width, self._frame.height

        self.NS = NSWindow.alloc(
        ).initWithContentRect_styleMask_backing_defer_(
            self.frame.NS,
            WindowStyle.Typical ^ excl,  # PYCHOK expected
            NSBackingStoreBuffered,
            NO)
        self.title = bytes2str(title)
        self.front(True)

        if kwds:
            super(Window, self).__init__(**kwds)

        # XXX self.NS.setIdentifier_(int2NS(id(self)))
        self._NSuniqID = u = self.NS.uniqueID()
        if u in _Globals.Windows:
            raise WindowError('%s %r exists: %r' %
                              ('.uniqueID', u, _Globals.Windows[u]))
        _Globals.Windows[u] = self

        if _Globals.App and not self.app:
            self.app = _Globals.App

        if auto:
            self.NS.setReleasedWhenClosed_(YES)
            self._auto = True

        self.NSdelegate = retain(NSWindowDelegate.alloc().init(self))
コード例 #14
0
def url2NS(py, url2=None):
    '''Create an C{NSURL} instance from a Python string.

       @param py: The URL (C{str} or C{unicode}).
       @keyword url2: Optionally, relative to this URL (C{str} or C{unicode}).

       @return: The ObjC instance (C{NSURL}).

       @see: U{URL<https://Developer.Apple.com/documentation/foundation/url>}
             for parsing an C{NSURL}.
    '''
    ns = release(NSStr(py))
    if ':' in bytes2str(py):
        if url2:
            return NSURL.alloc().initWithString_relativeToURL_(ns, url2NS(url2))
        else:
            return NSURL.alloc().initWithString_(ns)
    elif url2:
        return NSURL.alloc().initFileURLWithPath_relativeToURL_(ns, url2NS(url2))
    else:
        return NSURL.alloc().initFileURLWithPath_(ns)
コード例 #15
0
    def ratio(self, ratio):
        '''Set this window's aspect ratio.

           @param ratio: New ratio (L{Size}, 2-tuple (width, height), str("w:h") or C{NSSize_t}).

           @raise WindowError: Invalid I{ratio}.
        '''
        try:
            r = bytes2str(ratio, dflt=None)
            if r is not None:
                r = map(int, r.split(':'))
            elif isinstance(ratio, (tuple, list)) and len(ratio) == 2:
                r = tuple(ratio)
            else:  # NSSize_t
                r = ratio.width, ratio.height
        except (AttributeError, ValueError):
            raise WindowError('invalid %s: %r' % ('ratio', ratio))

        r = aspect_ratio(*r)
        if r:
            self._ratio = r
            self.NS.setContentAspectRatio_(NSSize_t(*r))
コード例 #16
0
 def label(self, label):
     '''Set the badge text of the app's dock tile (C{str}).
     '''
     self._label = bytes2str(label)
     self.NS.setBadgeLabel_(release(NSStr(label)))
     self.NS.display()
コード例 #17
0
 def name(self, name):
     '''Set the font name (C{str}).
     '''
     self._name = bytes2str(name)
コード例 #18
0
    def __init__(self, family_or_font, size=0, traits=0, weight=5):
        '''New L{Font}.

           @param family_or_font: Generic font name (C{str}, L{Str}, L{NSStr})
                                  like "Times" or "Helvetica" or a L{Font},
                                  C{NSFont} or C{NSFontDescriptor} instance.
           @keyword size: Desired point size (C{int}), zero for any.
           @keyword traits: Desired font traits (C{str} or C{FontTrait}C{s mask}).
           @keyword weigth: Desired book weight (C{int}) in range 0..15, where
                            0=light, 5=regular, 9=bold and 15=heavy.

           @raise FontError: No such I{family_or_font}.

           @raise FontTraitError: Mutually exclusive I{traits}.

           @raise TypeError: Invalid I{family_or_font}.

           @raise ValueError: Invalid I{weight}.

           @note: The new L{Font} may not exhibit the desired I{traits}
                  and I{weight}.  The I{weight} is ignored if I{traits}
                  include C{FontTrait.Bold}, both I{traits} and I{weight}
                  are ignored if I{family_or_font} is C{NSFontDescriptor}.

           @see: Function L{fontsof} to obtain all available fonts of
                 a particular font family.
        '''
        if isinstance(family_or_font, Str):
            ns, py = family_or_font.NS, str(family_or_font)
        elif isinstance(family_or_font, _ByteStrs):
            ns, py = release(NSStr(family_or_font)), bytes2str(family_or_font)
        elif isinstance(family_or_font, NSStr):
            ns, py = family_or_font, nsString2str(family_or_font)
#       elif isObjCInstanceOf(family_or_font, NSFontDescriptor):
# <https://Developer.Apple.com/documentation/appkit/nsfont/1525386-init>
# ignore traits and weight
#           ns, py = NSFont.alloc().init_(family_or_font, size), None
        elif isObjCInstanceOf(family_or_font, NSFont, name='family_or_font'):
            ns, py = family_or_font, None
            if size == 0:
                size = ns.pointSize()
            if traits == 0:
                traits = NSMain.FontManager.traitsOfFont_(ns)
            if not (size == ns.pointSize()
                    and traits == NSMain.FontManager.traitsOfFont_(ns)):
                ns = ns.familyName()
                py = nsString2str(ns)

        if py is not None:
            # <https://Developer.Apple.com/documentation/appkit/
            #        nsfontmanager/1462332-fontwithfamily>
            self._traits = _traitsin(traits)
            self._weight = _weightin(weight)
            ns = NSMain.FontManager.fontWithFamily_traits_weight_size_(
                ns, self._traits, self._weight, size)
            if isNone(ns):
                self._family = py
                self._size = flint(size)
                raise FontError('no such %s: %s' % ('font', self._argstr()))

        self._NS = ns  # _RO
        # <https://Developer.Apple.com/library/content/documentation/
        #  TextFonts/Conceptual/CocoaTextArchitecture/FontHandling/FontHandling.html>
        self._family = nsString2str(ns.familyName())
        self._height = flint(
            NSMain.LayoutManager.defaultLineHeightForFont_(ns) + 1)
        self._name = nsString2str(ns.fontName())
        self._size = flint(ns.pointSize())
        # traits not always reflect actual traits
        self._traits = NSMain.FontManager.traitsOfFont_(ns) or 0
        # update with the family traits, if any
        self._traits |= _traitsin(self._family, raiser=False)
        if ns.isFixedPitch() and not self.isMonoSpace:
            self._traits |= FontTrait.MonoSpace
        self._weight = NSMain.FontManager.weightOfFont_(ns)
コード例 #19
0
def encoding2ctype(code, dflt=missing, name='type'):  # MCCABE 20
    '''Return the C{ctypes} type for a single ObjC type encoding code.

       @param code: The type encoding (C{bytes}).
       @keyword dflt: Default encoding (C{ctype}).
       @keyword name: Name of the type (C{str}).

       @return: The C{ctype} (C{ctypes}).

       @raise TypeCodeError: Invalid or unbalanced I{code}, unless
                             a I{dflt} C{ctype} is provided.
    '''
    try:
        return _encoding2ctype[code]
    except KeyError:
        pass

    coderr = code
    if code[:1] == b'r':  # const ptr or decorator
        code = code[1:]
    try:
        c = code[-1:]
        if c == b'"':  # ..."name" suffix
            i = code.find(c)
            if i < 1:
                raise TypeCodeError
            code = code[:i]  # drop "name"

        elif c == b']':  # array ...[...]
            i = code.find(b'[')
            if i < 0:
                raise TypeCodeError
            elif i > 0:  # ignore array type
                code = code[:i + 1] + c
            else:  # convert array to pointer
                code = b'^' + code[1:-1].strip(b'0123456789')

        elif c in _TYPECLOSERS:  # Block, Struct or Union
            o = _TYPECLOSERS[c]
            i = code.find(o)
            if i < 0 or i > 4 or code[:i].strip(b'^'):  # != _bNN_
                raise TypeCodeError
            # if i > 1 code should only contain a name, see ^^{example}
            # above Table 6-2 at <https://Developer.Apple.com/library/
            # archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/
            # Articles/ocrtTypeEncodings.html>
            code = code[:i + 1] + c

        if code[:1] == b'^':
            if len(code) < 2:
                raise TypeCodeError


#           ctype = POINTER(_encoding2ctype[code[1:]])  # breaks on '^^^...'
            ctype = POINTER(encoding2ctype(code[1:]))  # allows '^^^...'
            _encoding2ctype[code] = ctype
            return ctype
        elif len(code):
            return _encoding2ctype[code]
        else:
            raise TypeCodeError

    except TypeCodeError:
        raise TypeCodeError('%s encoding %s: %r' %
                            (bytes2str(name), 'invalid', coderr))
    except KeyError:
        pass

    if dflt is missing:
        raise TypeCodeError('%s encoding %s: %r' %
                            (bytes2str(name), 'unknown', coderr))
    elif code[:1] == b'^':
        return POINTER(dflt)
    else:
        return dflt
コード例 #20
0
 def __str__(self):
     return 'None' if self.value is None else bytes2str(self.name_)
コード例 #21
0
def encoding2ctype(code, dflt=missing, name='type'):  # MCCABE 20
    '''Return the C{ctypes} type for a single ObjC type encoding code.

       @param code: The type encoding (C{bytes}).
       @keyword dflt: Default encoding (C{ctype}).
       @keyword name: Name of the type (C{str}).

       @return: The C{ctype} (C{ctypes}).

       @raise TypeCodeError: Invalid or unbalanced I{code}, unless
                             a I{dflt} C{ctype} is provided.
    '''
    try:
        return _encoding2ctype[code]
    except KeyError:
        pass

    coderr = code
    if code[:1] == b'r':  # const ptr or decorator
        code = code[1:]
    try:
        c = code[-1:]
        if c == b'"':  # ..."name" suffix
            i = code.find(c)
            if i < 1:
                raise TypeCodeError
            code = code[:i]  # drop "name"

        elif c == b']':  # array ...[...]
            i = code.find(b'[')
            if i < 0:
                raise TypeCodeError
            elif i > 0:  # ignore array type
                code = code[:i + 1] + c
            else:  # convert array to pointer
                code = b'^' + code[1:-1].strip(b'0123456789')

        elif c in _TYPECLOSERS:  # Block, Struct or Union
            o = _TYPE2OPENER[c]
            if code[:1] != o:
                o = b'^' + o
                if code[:2] != o:
                    raise TypeCodeError
            code = o + c  # {} or ^{}, etc.

        if code[:1] == b'^':
            if len(code) < 2:
                raise TypeCodeError
#           ctype = POINTER(_encoding2ctype[code[1:]])  # breaks on '^^^...'
            ctype = POINTER(encoding2ctype(code[1:]))  # allows '^^^...'
            _encoding2ctype[code] = ctype
            return ctype
        elif len(code):
            return _encoding2ctype[code]
        else:
            raise TypeCodeError

    except TypeCodeError:
        raise TypeCodeError('%s encoding %s: %r' % (bytes2str(name), 'invalid', coderr))
    except KeyError:
        pass

    if dflt is missing:
        raise TypeCodeError('%s encoding %s: %r' % (bytes2str(name), 'unknown', coderr))
    elif code[:1] == b'^':
        return POINTER(dflt)
    else:
        return dflt
コード例 #22
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