PyObjC is a bridge between Python and Objective-C that allows full featured Cocoa applications to be written in pure Python.
—
Key-Value Coding, testing support, signal handling, and other utility functions. These tools provide additional functionality for common PyObjC development patterns and specialized use cases.
Functions for working with Objective-C's Key-Value Coding system, which provides a generic way to access object properties by name.
def getKey(object, key: str):
"""
Get a value from an object using Key-Value Coding.
Args:
object: The object to query
key (str): The key path to retrieve
Returns:
The value at the specified key path
Usage:
from PyObjCTools.KeyValueCoding import getKey
value = getKey(my_object, "name")
nested_value = getKey(my_object, "address.street")
"""
def setKey(object, key: str, value):
"""
Set a value on an object using Key-Value Coding.
Args:
object: The object to modify
key (str): The key path to set
value: The value to set
Usage:
from PyObjCTools.KeyValueCoding import setKey
setKey(my_object, "name", "John Doe")
setKey(my_object, "address.street", "123 Main St")
"""
def getKeyPath(object, keyPath: str):
"""
Get a value using a key path (synonym for getKey).
Args:
object: The object to query
keyPath (str): The key path to retrieve
Returns:
The value at the specified key path
Usage:
from PyObjCTools.KeyValueCoding import getKeyPath
value = getKeyPath(my_object, "user.profile.name")
"""
def setKeyPath(object, keyPath: str, value):
"""
Set a value using a key path (synonym for setKey).
Args:
object: The object to modify
keyPath (str): The key path to set
value: The value to set
Usage:
from PyObjCTools.KeyValueCoding import setKeyPath
setKeyPath(my_object, "user.profile.name", "Jane Smith")
"""
def arrayOfVirtualKeys():
"""
Get an array of virtual keys for KVC operations.
Returns:
list: Array of virtual key strings
"""
def dictionaryOfVirtualKeys():
"""
Get a dictionary of virtual keys for KVC operations.
Returns:
dict: Dictionary mapping virtual keys to their operations
"""Classes and functions for PyObjC-specific testing patterns and test case management.
class TestCase:
"""
Base test case class for PyObjC tests.
Provides PyObjC-specific testing utilities and assertions
for testing Objective-C bridge functionality.
"""
def assertIsInstance(self, obj, cls):
"""Assert that obj is an instance of cls."""
def assertIsObjCClass(self, cls):
"""Assert that cls is an Objective-C class."""
def assertHasAttr(self, obj, attr):
"""Assert that obj has the specified attribute."""
def main():
"""
Run the PyObjC test suite.
Discovers and runs all PyObjC tests in the current module
or package, with appropriate setup and teardown.
"""
def onlyIf(condition: bool, reason: str):
"""
Decorator to conditionally run a test.
Args:
condition (bool): Whether the test should run
reason (str): Reason for skipping if condition is False
Usage:
@onlyIf(sys.platform == 'darwin', "macOS only")
def test_cocoa_feature(self):
pass
"""
def skipUnless(condition: bool, reason: str):
"""
Decorator to skip a test unless a condition is met.
Args:
condition (bool): Condition that must be true to run test
reason (str): Reason for skipping if condition is False
Usage:
@skipUnless(hasattr(objc, 'VERSION'), "Requires objc.VERSION")
def test_version_feature(self):
pass
"""
def fourcc(code: str):
"""
Convert a four-character code string to an integer.
Args:
code (str): Four-character code string
Returns:
int: Integer representation of the four-character code
Usage:
from PyObjCTools.TestSupport import fourcc
type_code = fourcc('JPEG')
"""
def cast_int(value):
"""
Cast a value to an integer for testing purposes.
Args:
value: The value to cast
Returns:
int: The integer representation
"""Functions for handling Unix signals and Mach signals in PyObjC applications.
def signal(signum: int, handler):
"""
Install a signal handler for Unix signals.
Args:
signum (int): Signal number (e.g., signal.SIGTERM)
handler: Signal handler function
Usage:
from PyObjCTools.Signals import signal
import signal as sig
def handle_term(signum, frame):
print("Received SIGTERM")
signal(sig.SIGTERM, handle_term)
"""
def getsignal(signum: int):
"""
Get the current signal handler for a signal.
Args:
signum (int): Signal number
Returns:
The current signal handler function
Usage:
from PyObjCTools.Signals import getsignal
handler = getsignal(signal.SIGTERM)
"""
def dumpStackOnFatalSignal():
"""
Install signal handlers to dump stack traces on fatal signals.
Automatically installs handlers for common fatal signals (SIGSEGV,
SIGBUS, SIGFPE, etc.) that will dump a Python stack trace before
the program terminates.
Usage:
from PyObjCTools.Signals import dumpStackOnFatalSignal
dumpStackOnFatalSignal()
"""
def resetFatalSignals():
"""
Reset signal handlers for fatal signals to their default behavior.
Usage:
from PyObjCTools.Signals import resetFatalSignals
resetFatalSignals()
"""
def handleMachSignal(exception_port):
"""
Handle Mach signals (macOS-specific).
Args:
exception_port: Mach exception port to monitor
Mach signals are low-level kernel signals used for exception
handling and debugging on macOS.
"""Mach-specific signal handling for advanced debugging on macOS.
def dumpStackOnFatalSignal():
"""
Mach-specific implementation of stack dumping on fatal signals.
Uses Mach exception handling for more detailed crash information
than standard Unix signals.
Usage:
from PyObjCTools.MachSignals import dumpStackOnFatalSignal
dumpStackOnFatalSignal()
"""
def resetFatalSignals():
"""
Reset Mach-specific signal handlers to default behavior.
Usage:
from PyObjCTools.MachSignals import resetFatalSignals
resetFatalSignals()
"""Functions for managing execution context and thread locking in PyObjC applications.
def current_bundle():
"""
Get the current NSBundle object.
Returns:
NSBundle: The current application bundle, or None if not available
Usage:
from PyObjCTools import current_bundle
bundle = current_bundle()
if bundle:
resources_path = bundle.resourcePath()
"""
def object_lock(obj):
"""
Context manager for locking individual Objective-C objects.
Args:
obj: The Objective-C object to lock
Usage:
with object_lock(my_object):
# Thread-safe access to my_object
my_object.modifyState()
"""
def release_lock():
"""
Context manager to release the PyObjC global lock.
Temporarily releases the global interpreter lock to allow
other threads to execute Python or Objective-C code.
Usage:
with release_lock():
# Other threads can run while this block executes
long_running_objc_operation()
"""Classes for creating categories and implementing lazy loading patterns.
class Category:
"""
Helper class for creating Objective-C categories.
Categories allow adding methods to existing Objective-C classes
without subclassing or modifying the original class definition.
"""
def __init__(self, baseClass):
"""
Initialize a category for the specified base class.
Args:
baseClass: The Objective-C class to extend
"""
def inject(targetClass, categoryClass):
"""
Inject category methods into a target class.
Args:
targetClass: The class to receive new methods
categoryClass: The class containing methods to inject
"""
class LazyList:
"""
Lazy-loaded list implementation for deferred evaluation.
Useful for creating lists of objects that are expensive to create
and may not all be needed immediately.
"""
def __init__(self, values):
"""
Initialize with a list of values or value factories.
Args:
values: List of values or callable factories
"""
class OC_PythonObject:
"""
Python object wrapper for use in Objective-C contexts.
Allows Python objects to be passed to Objective-C methods
that expect Objective-C objects, with automatic bridging.
"""from PyObjCTools.KeyValueCoding import getKey, setKey
from Foundation import NSMutableDictionary
# Create a mutable dictionary
person = NSMutableDictionary.alloc().init()
person.setObject_forKey_("John Doe", "name")
person.setObject_forKey_(30, "age")
# Create nested structure
address = NSMutableDictionary.alloc().init()
address.setObject_forKey_("123 Main St", "street")
address.setObject_forKey_("Anytown", "city")
person.setObject_forKey_(address, "address")
# Use KVC to access values
name = getKey(person, "name")
print(f"Name: {name}") # "John Doe"
# Access nested values with key paths
street = getKey(person, "address.street")
print(f"Street: {street}") # "123 Main St"
# Set values using KVC
setKey(person, "age", 31)
setKey(person, "address.zip", "12345")
print(f"Updated age: {getKey(person, 'age')}") # 31
print(f"Zip code: {getKey(person, 'address.zip')}") # "12345"from PyObjCTools.TestSupport import TestCase, main, onlyIf, skipUnless
import sys
import objc
from Foundation import NSString
class MyPyObjCTests(TestCase):
def test_basic_bridge_functionality(self):
"""Test basic PyObjC bridge operations."""
# Test string creation
s = NSString.stringWithString_("Hello")
self.assertIsInstance(s, NSString)
self.assertEqual(s.length(), 5)
@onlyIf(sys.platform == 'darwin', "macOS only")
def test_macos_specific_feature(self):
"""Test macOS-specific functionality."""
# Test that would only work on macOS
self.assertTrue(hasattr(objc, 'lookUpClass'))
@skipUnless(hasattr(objc, 'runtime'), "Requires runtime access")
def test_runtime_features(self):
"""Test runtime introspection features."""
self.assertIsNotNone(objc.runtime)
if __name__ == '__main__':
main()from PyObjCTools.Signals import signal
import signal as sig
import sys
class MyApplication:
def __init__(self):
self.should_quit = False
self.setup_signal_handlers()
def setup_signal_handlers(self):
"""Set up signal handlers for graceful shutdown."""
signal(sig.SIGTERM, self.handle_termination)
signal(sig.SIGINT, self.handle_termination)
def handle_termination(self, signum, frame):
"""Handle termination signals."""
print(f"Received signal {signum}, shutting down...")
self.should_quit = True
def run(self):
"""Main application loop."""
while not self.should_quit:
# Application logic here
time.sleep(0.1)
print("Application shutdown complete")
# Usage
app = MyApplication()
app.run()from PyObjCTools import object_lock, release_lock
from Foundation import NSMutableArray
import threading
# Shared mutable array
shared_array = NSMutableArray.alloc().init()
def worker_thread(thread_id):
"""Worker thread that modifies shared array."""
for i in range(10):
with object_lock(shared_array):
# Thread-safe modification
shared_array.addObject_(f"Thread {thread_id}, item {i}")
# Release lock to allow other operations
with release_lock():
# Other threads can run while we sleep
time.sleep(0.01)
# Start multiple threads
threads = []
for i in range(3):
t = threading.Thread(target=worker_thread, args=(i,))
threads.append(t)
t.start()
# Wait for completion
for t in threads:
t.join()
print(f"Final array count: {shared_array.count()}")Install with Tessl CLI
npx tessl i tessl/pypi-pyobjc