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