(nsobj)
Convert a Foundation object into an equivalent Python object if possible.

    Currently supported types:

    * :class:`~rubicon.objc.runtime.objc_id`: Wrapped in an :class:`ObjCInstance` and converted as below
    * :class:`NSString`: Converted to :class:`str`
    * :class:`NSData`: Converted to :class:`bytes`
    * :class:`NSDecimalNumber`: Converted to :class:`decimal.Decimal`
    * :class:`NSDictionary`: Converted to :class:`dict`, with all keys and values converted recursively
    * :class:`NSArray`: Converted to :class:`list`, with all elements converted recursively
    * :class:`NSNumber`: Converted to a :class:`bool`, :class:`int` or :class:`float` based on the type of its contents

    Other objects are returned unmodified as an :class:`ObjCInstance`.
Convert a Foundation object into an equivalent Python object if possible.
Convert a Foundation object into an equivalent Python object if possible .
def py_from_ns(nsobj):
    """Convert a Foundation object into an equivalent Python object if possible.

    Currently supported types:

    * :class:`~rubicon.objc.runtime.objc_id`: Wrapped in an :class:`ObjCInstance` and converted as below
    * :class:`NSString`: Converted to :class:`str`
    * :class:`NSData`: Converted to :class:`bytes`
    * :class:`NSDecimalNumber`: Converted to :class:`decimal.Decimal`
    * :class:`NSDictionary`: Converted to :class:`dict`, with all keys and values converted recursively
    * :class:`NSArray`: Converted to :class:`list`, with all elements converted recursively
    * :class:`NSNumber`: Converted to a :class:`bool`, :class:`int` or :class:`float` based on the type of its contents

    Other objects are returned unmodified as an :class:`ObjCInstance`.
    """

    if isinstance(nsobj, (objc_id, Class)):
        nsobj = ObjCInstance(nsobj)
    if not isinstance(nsobj, ObjCInstance):
        return nsobj

    if nsobj.isKindOfClass(NSDecimalNumber):
        return decimal.Decimal(str(nsobj.descriptionWithLocale(None)))
    elif nsobj.isKindOfClass(NSNumber):
        # Choose the property to access based on the type encoding. The actual conversion is done by ctypes.
        # Signed and unsigned integers are in separate cases to prevent overflow with unsigned long longs.
        objc_type = nsobj.objCType
        if objc_type == b'B':
            return nsobj.boolValue
        elif objc_type in b'csilq':
            return nsobj.longLongValue
        elif objc_type in b'CSILQ':
            return nsobj.unsignedLongLongValue
        elif objc_type in b'fd':
            return nsobj.doubleValue
        else:
            raise TypeError(
                'NSNumber containing unsupported type {!r} cannot be converted to a Python object'
                .format(objc_type)
            )
    elif nsobj.isKindOfClass(NSString):
        return str(nsobj)
    elif nsobj.isKindOfClass(NSData):
        # Despite the name, string_at converts the data at the address to a bytes object, not str.
        return string_at(send_message(nsobj, 'bytes', restype=POINTER(c_uint8), argtypes=[]), nsobj.length)
    elif nsobj.isKindOfClass(NSDictionary):
        return {py_from_ns(k): py_from_ns(v) for k, v in nsobj.items()}
    elif nsobj.isKindOfClass(NSArray):
        return [py_from_ns(o) for o in nsobj]
    else:
        return nsobj
def py_from_ns ( nsobj ) : if isinstance ( nsobj , ( objc_id , Class ) ) : nsobj = ObjCInstance ( nsobj ) if not isinstance ( nsobj , ObjCInstance ) : return nsobj if nsobj . isKindOfClass ( NSDecimalNumber ) : return decimal . Decimal ( str ( nsobj . descriptionWithLocale ( None ) ) ) elif nsobj . isKindOfClass ( NSNumber ) : # Choose the property to access based on the type encoding. The actual conversion is done by ctypes. # Signed and unsigned integers are in separate cases to prevent overflow with unsigned long longs. objc_type = nsobj . objCType if objc_type == b'B' : return nsobj . boolValue elif objc_type in b'csilq' : return nsobj . longLongValue elif objc_type in b'CSILQ' : return nsobj . unsignedLongLongValue elif objc_type in b'fd' : return nsobj . doubleValue else : raise TypeError ( 'NSNumber containing unsupported type {!r} cannot be converted to a Python object' . format ( objc_type ) ) elif nsobj . isKindOfClass ( NSString ) : return str ( nsobj ) elif nsobj . isKindOfClass ( NSData ) : # Despite the name, string_at converts the data at the address to a bytes object, not str. return string_at ( send_message ( nsobj , 'bytes' , restype = POINTER ( c_uint8 ) , argtypes = [ ] ) , nsobj . length ) elif nsobj . isKindOfClass ( NSDictionary ) : return { py_from_ns ( k ) : py_from_ns ( v ) for k , v in nsobj . items ( ) } elif nsobj . isKindOfClass ( NSArray ) : return [ py_from_ns ( o ) for o in nsobj ] else : return nsobj