PyObjC Framework: ExceptionHandling
This library provides Python wrappers for the macOS `ExceptionHandling` framework, enabling Python applications to interact with Objective-C's exception mechanisms. It's part of the larger PyObjC bridge, which allows full-featured Cocoa applications to be written in pure Python. PyObjC is actively maintained, with new versions typically released to align with macOS SDK updates and Python version support. The current version is 12.1.
Warnings
- breaking PyObjC 12.0 dropped support for Python 3.9, and PyObjC 11.0 dropped support for Python 3.8. Ensure your Python version meets the `pyobjc-framework-exceptionhandling` requirements (>=3.10).
- breaking Versions 10.3 and 10.3.1 introduced changes in how `__init__` and `__new__` methods are handled in Python subclasses of Objective-C classes, particularly when a user-defined `__new__` is present. Code relying on the PyObjC-provided `__new__` can no longer use `__init__`.
- gotcha PyObjC 11.1 aligned with `clang`'s Automatic Reference Counting (ARC) documentation for initializer methods (those in the 'init' family). PyObjC now correctly models that these methods steal a reference to `self` and return a new one, which might alter reference counting behavior in complex scenarios.
- gotcha Objective-C code is generally not written with Python-style exception safety in mind. It is advisable to avoid raising Python exceptions that will cross the boundary from Python to Objective-C, as this can lead to unexpected program termination or unstable behavior.
- gotcha For debugging exceptions that cross the Python/Objective-C boundary, call `objc.setVerbose(True)`. This will cause PyObjC to print Python stack traces to `stderr` when translating exceptions.
Install
-
pip install pyobjc-framework-exceptionhandling
Imports
- ExceptionHandling
import ExceptionHandling
Quickstart
import objc
from Foundation import NSObject, NSLog
# Enable verbose logging for PyObjC to see detailed stack traces
# when exceptions cross the Python/Objective-C boundary.
# This is key for understanding issues related to ExceptionHandling.
objc.setVerbose(True)
class MyPythonException(Exception):
"""A custom Python exception to demonstrate bridging."""
pass
# Define an Objective-C class (via Python subclassing NSObject)
class MyObjCClass(NSObject):
def init(self):
self = super().init()
if self:
NSLog("MyObjCClass initialized in Python")
return self
def triggerPythonException_(self, arg):
"""An Objective-C callable method that might raise a Python exception."""
NSLog("Objective-C calling Python method to trigger exception with arg: %@", arg)
if arg == "error":
raise MyPythonException("An error occurred in Python!")
return "Python successfully processed: " + arg
if __name__ == "__main__":
obj = MyObjCClass.alloc().init()
# Example 1: Call method that triggers a Python exception
print("\n--- Attempting to trigger an exception ---")
try:
# When Python raises an exception, PyObjC translates it into an
# Objective-C exception. If caught back in Python, it manifests as objc.error.
result = obj.triggerPythonException_("error")
print(f"Result (should not be reached): {result}")
except objc.error as e:
print(f"Caught Objective-C error (originally Python exception): {e}")
except MyPythonException as e: # This would not be caught directly here
print(f"Caught Python exception directly: {e}")
# Example 2: Call method that succeeds
print("\n--- Attempting a successful call ---")
result = obj.triggerPythonException_("success")
print(f"Successful call result: {result}")