Created
July 1, 2018 17:53
-
-
Save dgelessus/018681c297dfae22a06a3bc315d9e6e3 to your computer and use it in GitHub Desktop.
Helper module to log Python strings using NSLog
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""Allows logging Python :class:`str`s to the system log using Foundation's ``NSLog`` function. | |
This module is meant to help with debugging the loading process of Rubicon on systems like iOS, where sometimes only | |
NSLog output (but not stdout/stderr) is available to the developer. | |
""" | |
import ctypes | |
import io | |
import sys | |
__all__ = [ | |
'nslog', | |
'NSLogWriter', | |
] | |
# Name of the UTF-16 encoding with the system byte order. | |
if sys.byteorder == 'little': | |
UTF16_NATIVE = 'utf-16-le' | |
elif sys.byteorder == 'big': | |
UTF16_NATIVE = 'utf-16-be' | |
else: | |
raise AssertionError('Unknown byte order: ' + sys.byteorder) | |
# Note: Many of the following definitions are duplicated in other rubicon.objc submodules. | |
# However because this module is used to debug the early startup process of Rubicon, we can't use any of the other | |
# definitions. | |
class CFTypeRef(ctypes.c_void_p): | |
pass | |
CFIndex = ctypes.c_long | |
UniChar = ctypes.c_uint16 | |
CoreFoundation = ctypes.CDLL('/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation') | |
Foundation = ctypes.CDLL('/System/Library/Frameworks/Foundation.framework/Foundation') | |
# void CFRelease(CFTypeRef arg) | |
CoreFoundation.CFRelease.restype = None | |
CoreFoundation.CFRelease.argtypes = [CFTypeRef] | |
# CFStringRef CFStringCreateWithCharacters(CFAllocatorRef alloc, const UniChar *chars, CFIndex numChars) | |
CoreFoundation.CFStringCreateWithCharacters.restype = CFTypeRef | |
CoreFoundation.CFStringCreateWithCharacters.argtypes = [CFTypeRef, ctypes.POINTER(UniChar), CFIndex] | |
# void NSLog(NSString *format, ...) | |
Foundation.NSLog.restype = None | |
Foundation.NSLog.argtypes = [CFTypeRef] | |
def _cfstr(s): | |
"""Create a ``CFString`` from the given Python :class:`str`.""" | |
encoded = s.encode(UTF16_NATIVE) | |
assert len(encoded) % 2 == 0 | |
arr = (UniChar * (len(encoded)//2)).from_buffer_copy(encoded) | |
cfstring = CoreFoundation.CFStringCreateWithCharacters(None, arr, len(arr)) | |
assert cfstring is not None | |
return cfstring | |
# Format string for a single object. | |
FORMAT = _cfstr('%@') | |
def nslog(s): | |
"""Log the given Python :class:`str` to the system log.""" | |
cfstring = _cfstr(s) | |
Foundation.NSLog(FORMAT, cfstring) | |
CoreFoundation.CFRelease(cfstring) | |
class NSLogWriter(io.TextIOBase): | |
"""An output-only text stream that writes to the system log.""" | |
def write(self, s): | |
nslog(s[:-1] if s.endswith('\n') else s) | |
return len(s) | |
@property | |
def encoding(self): | |
return UTF16_NATIVE |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment