• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /macosx-10.10.1/pyobjc-45/pyobjc/pyobjc-framework-ExceptionHandling-2.5.1/Lib/PyObjCTools/
1"""
2Low level debugging helper for PyObjC.
3
4Allows you to log Python and ObjC (via atos) stack traces for NSExceptions
5raised.
6
7General guidelines for use:
8
9- It's typically only useful when you log EVERY exception, because Foundation
10  and AppKit will swallow most of them.  This means that you should never
11  use this module in a release build.
12
13- Typical use involves only calling installExceptionHandler or
14  installVerboseExceptionHandler.  It may be removed at any time by calling
15  removeDebuggingHandler.
16"""
17from __future__ import print_function
18
19from Foundation import NSObject, NSLog
20import objc
21import os
22import sys
23
24import traceback
25from ExceptionHandling import NSExceptionHandler, NSLogUncaughtExceptionMask, NSLogAndHandleEveryExceptionMask, NSStackTraceKey
26
27DEFAULTMASK = NSLogUncaughtExceptionMask
28EVERYTHINGMASK = NSLogAndHandleEveryExceptionMask
29
30
31__all__ = [
32    'LOGSTACKTRACE', 'DEFAULTVERBOSITY', 'DEFAULTMASK', 'EVERYTHINGMASK',
33    'installExceptionHandler', 'installVerboseExceptionHandler',
34    'installPythonExceptionHandler', 'removeExceptionHandler',
35    'handlerInstalled',
36]
37
38def isPythonException(exception):
39    return (exception.userInfo() or {}).get('__pyobjc_exc_type__') is not None
40
41def nsLogPythonException(exception):
42    userInfo = exception.userInfo()
43    NSLog('%@', '*** Python exception discarded!\n' +
44        ''.join(traceback.format_exception(
45        userInfo['__pyobjc_exc_type__'],
46        userInfo['__pyobjc_exc_value__'],
47        userInfo['__pyobjc_exc_traceback__'],
48    )).decode('utf8'))
49    # we logged it, so don't log it for us
50    return False
51
52def nsLogObjCException(exception):
53    userInfo = exception.userInfo()
54    stack = userInfo.get(NSStackTraceKey)
55    if not stack or not os.path.exists('/usr/bin/atos'):
56        return True
57    pipe = os.popen('/usr/bin/atos -p %d %s' % (os.getpid(), stack))
58    stacktrace = pipe.readlines()
59    stacktrace.reverse()
60    NSLog("%@", "*** ObjC exception '%s' (reason: '%s') discarded\n" % (
61            exception.name(), exception.reason(),
62        ) +
63        'Stack trace (most recent call last):\n' +
64        ''.join([('  '+line) for line in stacktrace]).decode('utf8')
65    )
66    return False
67
68LOGSTACKTRACE = 1 << 0
69DEFAULTVERBOSITY = 0
70
71class PyObjCDebuggingDelegate(NSObject):
72    verbosity = objc.ivar('verbosity', b'i')
73
74    def initWithVerbosity_(self, verbosity):
75        self = self.init()
76        self.verbosity = verbosity
77        return self
78
79    @objc.typedSelector(b'c@:@@I')
80    def exceptionHandler_shouldLogException_mask_(self, sender, exception, aMask):
81        try:
82            if isPythonException(exception):
83                if self.verbosity & LOGSTACKTRACE:
84                    nsLogObjCException(exception)
85                return nsLogPythonException(exception)
86            elif self.verbosity & LOGSTACKTRACE:
87                return nsLogObjCException(exception)
88            else:
89                return False
90        except:
91            print("*** Exception occurred during exception handler ***",
92                    file=sys.stderr)
93            traceback.print_exc(sys.stderr)
94            return True
95
96    @objc.typedSelector(b'c@:@@I')
97    def exceptionHandler_shouldHandleException_mask_(self, sender, exception, aMask):
98        return False
99
100def installExceptionHandler(verbosity=DEFAULTVERBOSITY, mask=DEFAULTMASK):
101    """
102    Install the exception handling delegate that will log every exception
103    matching the given mask with the given verbosity.
104    """
105    # we need to retain this, cause the handler doesn't
106    global _exceptionHandlerDelegate
107    delegate = PyObjCDebuggingDelegate.alloc().initWithVerbosity_(verbosity)
108    NSExceptionHandler.defaultExceptionHandler().setExceptionHandlingMask_(mask)
109    NSExceptionHandler.defaultExceptionHandler().setDelegate_(delegate)
110    _exceptionHandlerDelegate = delegate
111
112def installPythonExceptionHandler():
113    """
114    Install a verbose exception handling delegate that logs every exception
115    raised.
116
117    Will log only Python stack traces, if available.
118    """
119    installExceptionHandler(verbosity=DEFAULTVERBOSITY, mask=EVERYTHINGMASK)
120
121def installVerboseExceptionHandler():
122    """
123    Install a verbose exception handling delegate that logs every exception
124    raised.
125
126    Will log both Python and ObjC stack traces, if available.
127    """
128    installExceptionHandler(verbosity=LOGSTACKTRACE, mask=EVERYTHINGMASK)
129
130def removeExceptionHandler():
131    """
132    Remove the current exception handler delegate
133    """
134    NSExceptionHandler.defaultExceptionHandler().setDelegate_(None)
135    NSExceptionHandler.defaultExceptionHandler().setExceptionHandlingMask_(0)
136
137def handlerInstalled():
138    """
139    Is an exception handler delegate currently installed?
140    """
141    return NSExceptionHandler.defaultExceptionHandler().delegate() is not None
142