~soren/nova/iptables-security-groups

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/python/failure.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- test-case-name: twisted.test.test_failure -*-
 
2
# See also test suite twisted.test.test_pbfailure
 
3
 
 
4
# Copyright (c) 2001-2008 Twisted Matrix Laboratories.
 
5
# See LICENSE for details.
 
6
 
 
7
 
 
8
"""
 
9
Asynchronous-friendly error mechanism.
 
10
 
 
11
See L{Failure}.
 
12
"""
 
13
 
 
14
# System Imports
 
15
import sys
 
16
import linecache
 
17
import inspect
 
18
import opcode
 
19
from cStringIO import StringIO
 
20
 
 
21
from twisted.python import reflect
 
22
 
 
23
count = 0
 
24
traceupLength = 4
 
25
 
 
26
class DefaultException(Exception):
 
27
    pass
 
28
 
 
29
def format_frames(frames, write, detail="default"):
 
30
    """Format and write frames.
 
31
 
 
32
    @param frames: is a list of frames as used by Failure.frames, with
 
33
        each frame being a list of
 
34
        (funcName, fileName, lineNumber, locals.items(), globals.items())
 
35
    @type frames: list
 
36
    @param write: this will be called with formatted strings.
 
37
    @type write: callable
 
38
    @param detail: Three detail levels are available:
 
39
        default, brief, and verbose.
 
40
    @type detail: string
 
41
    """
 
42
    if detail not in ('default', 'brief', 'verbose'):
 
43
        raise ValueError, "Detail must be default, brief, or verbose. (not %r)" % (detail,)
 
44
    w = write
 
45
    if detail == "brief":
 
46
        for method, filename, lineno, localVars, globalVars in frames:
 
47
            w('%s:%s:%s\n' % (filename, lineno, method))
 
48
    elif detail == "default":
 
49
        for method, filename, lineno, localVars, globalVars in frames:
 
50
            w( '  File "%s", line %s, in %s\n' % (filename, lineno, method))
 
51
            w( '    %s\n' % linecache.getline(filename, lineno).strip())
 
52
    elif detail == "verbose":
 
53
        for method, filename, lineno, localVars, globalVars in frames:
 
54
            w("%s:%d: %s(...)\n" % (filename, lineno, method))
 
55
            w(' [ Locals ]\n')
 
56
            # Note: the repr(val) was (self.pickled and val) or repr(val)))
 
57
            for name, val in localVars:
 
58
                w("  %s : %s\n" %  (name, repr(val)))
 
59
            w(' ( Globals )\n')
 
60
            for name, val in globalVars:
 
61
                w("  %s : %s\n" %  (name, repr(val)))
 
62
 
 
63
# slyphon: i have a need to check for this value in trial
 
64
#          so I made it a module-level constant
 
65
EXCEPTION_CAUGHT_HERE = "--- <exception caught here> ---"
 
66
 
 
67
 
 
68
 
 
69
class NoCurrentExceptionError(Exception):
 
70
    """
 
71
    Raised when trying to create a Failure from the current interpreter
 
72
    exception state and there is no current exception state.
 
73
    """
 
74
 
 
75
 
 
76
class _Traceback(object):
 
77
    """
 
78
    Fake traceback object which can be passed to functions in the standard
 
79
    library L{traceback} module.
 
80
    """
 
81
 
 
82
    def __init__(self, frames):
 
83
        """
 
84
        Construct a fake traceback object using a list of frames. Note that
 
85
        although frames generally include locals and globals, this information
 
86
        is not kept by this object, since locals and globals are not used in
 
87
        standard tracebacks.
 
88
 
 
89
        @param frames: [(methodname, filename, lineno, locals, globals), ...]
 
90
        """
 
91
        assert len(frames) > 0, "Must pass some frames"
 
92
        head, frames = frames[0], frames[1:]
 
93
        name, filename, lineno, localz, globalz = head
 
94
        self.tb_frame = _Frame(name, filename)
 
95
        self.tb_lineno = lineno
 
96
        if len(frames) == 0:
 
97
            self.tb_next = None
 
98
        else:
 
99
            self.tb_next = _Traceback(frames)
 
100
 
 
101
 
 
102
class _Frame(object):
 
103
    """
 
104
    A fake frame object, used by L{_Traceback}.
 
105
    """
 
106
 
 
107
    def __init__(self, name, filename):
 
108
        self.f_code = _Code(name, filename)
 
109
        self.f_globals = {}
 
110
 
 
111
 
 
112
class _Code(object):
 
113
    """
 
114
    A fake code object, used by L{_Traceback} via L{_Frame}.
 
115
    """
 
116
    def __init__(self, name, filename):
 
117
        self.co_name = name
 
118
        self.co_filename = filename
 
119
 
 
120
 
 
121
class Failure:
 
122
    """
 
123
    A basic abstraction for an error that has occurred.
 
124
 
 
125
    This is necessary because Python's built-in error mechanisms are
 
126
    inconvenient for asynchronous communication.
 
127
 
 
128
    @ivar value: The exception instance responsible for this failure.
 
129
    @ivar type: The exception's class.
 
130
    """
 
131
 
 
132
    pickled = 0
 
133
    stack = None
 
134
 
 
135
    # The opcode of "yield" in Python bytecode. We need this in _findFailure in
 
136
    # order to identify whether an exception was thrown by a
 
137
    # throwExceptionIntoGenerator.
 
138
    _yieldOpcode = chr(opcode.opmap["YIELD_VALUE"])
 
139
 
 
140
    def __init__(self, exc_value=None, exc_type=None, exc_tb=None):
 
141
        """
 
142
        Initialize me with an explanation of the error.
 
143
 
 
144
        By default, this will use the current C{exception}
 
145
        (L{sys.exc_info}()).  However, if you want to specify a
 
146
        particular kind of failure, you can pass an exception as an
 
147
        argument.
 
148
 
 
149
        If no C{exc_value} is passed, then an "original" C{Failure} will
 
150
        be searched for. If the current exception handler that this
 
151
        C{Failure} is being constructed in is handling an exception
 
152
        raised by L{raiseException}, then this C{Failure} will act like
 
153
        the original C{Failure}.
 
154
 
 
155
        For C{exc_tb} only L{traceback} instances or C{None} are allowed.
 
156
        If C{None} is supplied for C{exc_value}, the value of C{exc_tb} is
 
157
        ignored, otherwise if C{exc_tb} is C{None}, it will be found from
 
158
        execution context (ie, L{sys.exc_info}).
 
159
        """
 
160
        global count
 
161
        count = count + 1
 
162
        self.count = count
 
163
        self.type = self.value = tb = None
 
164
 
 
165
        #strings Exceptions/Failures are bad, mmkay?
 
166
        if isinstance(exc_value, (str, unicode)) and exc_type is None:
 
167
            import warnings
 
168
            warnings.warn(
 
169
                "Don't pass strings (like %r) to failure.Failure (replacing with a DefaultException)." %
 
170
                exc_value, DeprecationWarning, stacklevel=2)
 
171
            exc_value = DefaultException(exc_value)
 
172
 
 
173
        stackOffset = 0
 
174
 
 
175
        if exc_value is None:
 
176
            exc_value = self._findFailure()
 
177
 
 
178
        if exc_value is None:
 
179
            self.type, self.value, tb = sys.exc_info()
 
180
            if self.type is None:
 
181
                raise NoCurrentExceptionError()
 
182
            stackOffset = 1
 
183
        elif exc_type is None:
 
184
            if isinstance(exc_value, Exception):
 
185
                self.type = exc_value.__class__
 
186
            else: #allow arbitrary objects.
 
187
                self.type = type(exc_value)
 
188
            self.value = exc_value
 
189
        else:
 
190
            self.type = exc_type
 
191
            self.value = exc_value
 
192
        if isinstance(self.value, Failure):
 
193
            self.__dict__ = self.value.__dict__
 
194
            return
 
195
        if tb is None:
 
196
            if exc_tb:
 
197
                tb = exc_tb
 
198
#             else:
 
199
#                 log.msg("Erf, %r created with no traceback, %s %s." % (
 
200
#                     repr(self), repr(exc_value), repr(exc_type)))
 
201
#                 for s in traceback.format_stack():
 
202
#                     log.msg(s)
 
203
 
 
204
        frames = self.frames = []
 
205
        stack = self.stack = []
 
206
 
 
207
        # added 2003-06-23 by Chris Armstrong. Yes, I actually have a
 
208
        # use case where I need this traceback object, and I've made
 
209
        # sure that it'll be cleaned up.
 
210
        self.tb = tb
 
211
 
 
212
        if tb:
 
213
            f = tb.tb_frame
 
214
        elif not isinstance(self.value, Failure):
 
215
            # we don't do frame introspection since it's expensive,
 
216
            # and if we were passed a plain exception with no
 
217
            # traceback, it's not useful anyway
 
218
            f = stackOffset = None
 
219
 
 
220
        while stackOffset and f:
 
221
            # This excludes this Failure.__init__ frame from the
 
222
            # stack, leaving it to start with our caller instead.
 
223
            f = f.f_back
 
224
            stackOffset -= 1
 
225
 
 
226
        # Keeps the *full* stack.  Formerly in spread.pb.print_excFullStack:
 
227
        #
 
228
        #   The need for this function arises from the fact that several
 
229
        #   PB classes have the peculiar habit of discarding exceptions
 
230
        #   with bareword "except:"s.  This premature exception
 
231
        #   catching means tracebacks generated here don't tend to show
 
232
        #   what called upon the PB object.
 
233
 
 
234
        while f:
 
235
            localz = f.f_locals.copy()
 
236
            if f.f_locals is f.f_globals:
 
237
                globalz = {}
 
238
            else:
 
239
                globalz = f.f_globals.copy()
 
240
            for d in globalz, localz:
 
241
                if d.has_key("__builtins__"):
 
242
                    del d["__builtins__"]
 
243
            stack.insert(0, [
 
244
                f.f_code.co_name,
 
245
                f.f_code.co_filename,
 
246
                f.f_lineno,
 
247
                localz.items(),
 
248
                globalz.items(),
 
249
                ])
 
250
            f = f.f_back
 
251
 
 
252
        while tb is not None:
 
253
            f = tb.tb_frame
 
254
            localz = f.f_locals.copy()
 
255
            if f.f_locals is f.f_globals:
 
256
                globalz = {}
 
257
            else:
 
258
                globalz = f.f_globals.copy()
 
259
            for d in globalz, localz:
 
260
                if d.has_key("__builtins__"):
 
261
                    del d["__builtins__"]
 
262
 
 
263
            frames.append([
 
264
                f.f_code.co_name,
 
265
                f.f_code.co_filename,
 
266
                tb.tb_lineno,
 
267
                localz.items(),
 
268
                globalz.items(),
 
269
                ])
 
270
            tb = tb.tb_next
 
271
        if inspect.isclass(self.type) and issubclass(self.type, Exception):
 
272
            parentCs = reflect.allYourBase(self.type)
 
273
            self.parents = map(reflect.qual, parentCs)
 
274
            self.parents.append(reflect.qual(self.type))
 
275
        else:
 
276
            self.parents = [self.type]
 
277
 
 
278
    def trap(self, *errorTypes):
 
279
        """Trap this failure if its type is in a predetermined list.
 
280
 
 
281
        This allows you to trap a Failure in an error callback.  It will be
 
282
        automatically re-raised if it is not a type that you expect.
 
283
 
 
284
        The reason for having this particular API is because it's very useful
 
285
        in Deferred errback chains::
 
286
 
 
287
            def _ebFoo(self, failure):
 
288
                r = failure.trap(Spam, Eggs)
 
289
                print 'The Failure is due to either Spam or Eggs!'
 
290
                if r == Spam:
 
291
                    print 'Spam did it!'
 
292
                elif r == Eggs:
 
293
                    print 'Eggs did it!'
 
294
 
 
295
        If the failure is not a Spam or an Eggs, then the Failure
 
296
        will be 'passed on' to the next errback.
 
297
 
 
298
        @type errorTypes: L{Exception}
 
299
        """
 
300
        error = self.check(*errorTypes)
 
301
        if not error:
 
302
            raise self
 
303
        return error
 
304
 
 
305
    def check(self, *errorTypes):
 
306
        """Check if this failure's type is in a predetermined list.
 
307
 
 
308
        @type errorTypes: list of L{Exception} classes or
 
309
                          fully-qualified class names.
 
310
        @returns: the matching L{Exception} type, or None if no match.
 
311
        """
 
312
        for error in errorTypes:
 
313
            err = error
 
314
            if inspect.isclass(error) and issubclass(error, Exception):
 
315
                err = reflect.qual(error)
 
316
            if err in self.parents:
 
317
                return error
 
318
        return None
 
319
 
 
320
 
 
321
    def raiseException(self):
 
322
        """
 
323
        raise the original exception, preserving traceback
 
324
        information if available.
 
325
        """
 
326
        raise self.type, self.value, self.tb
 
327
 
 
328
 
 
329
    def throwExceptionIntoGenerator(self, g):
 
330
        """
 
331
        Throw the original exception into the given generator,
 
332
        preserving traceback information if available.
 
333
 
 
334
        @return: The next value yielded from the generator.
 
335
        @raise StopIteration: If there are no more values in the generator.
 
336
        @raise anything else: Anything that the generator raises.
 
337
        """
 
338
        return g.throw(self.type, self.value, self.tb)
 
339
 
 
340
 
 
341
    def _findFailure(cls):
 
342
        """
 
343
        Find the failure that represents the exception currently in context.
 
344
        """
 
345
        tb = sys.exc_info()[-1]
 
346
        if not tb:
 
347
            return
 
348
 
 
349
        secondLastTb = None
 
350
        lastTb = tb
 
351
        while lastTb.tb_next:
 
352
            secondLastTb = lastTb
 
353
            lastTb = lastTb.tb_next
 
354
 
 
355
        lastFrame = lastTb.tb_frame
 
356
 
 
357
        # NOTE: f_locals.get('self') is used rather than
 
358
        # f_locals['self'] because psyco frames do not contain
 
359
        # anything in their locals() dicts.  psyco makes debugging
 
360
        # difficult anyhow, so losing the Failure objects (and thus
 
361
        # the tracebacks) here when it is used is not that big a deal.
 
362
 
 
363
        # handle raiseException-originated exceptions
 
364
        if lastFrame.f_code is cls.raiseException.func_code:
 
365
            return lastFrame.f_locals.get('self')
 
366
 
 
367
        # handle throwExceptionIntoGenerator-originated exceptions
 
368
        # this is tricky, and differs if the exception was caught
 
369
        # inside the generator, or above it:
 
370
 
 
371
        # it is only really originating from
 
372
        # throwExceptionIntoGenerator if the bottom of the traceback
 
373
        # is a yield.
 
374
        # Pyrex and Cython extensions create traceback frames
 
375
        # with no co_code, but they can't yield so we know it's okay to just return here.
 
376
        if ((not lastFrame.f_code.co_code) or
 
377
            lastFrame.f_code.co_code[lastTb.tb_lasti] != cls._yieldOpcode):
 
378
            return
 
379
 
 
380
        # if the exception was caught above the generator.throw
 
381
        # (outside the generator), it will appear in the tb (as the
 
382
        # second last item):
 
383
        if secondLastTb:
 
384
            frame = secondLastTb.tb_frame
 
385
            if frame.f_code is cls.throwExceptionIntoGenerator.func_code:
 
386
                return frame.f_locals.get('self')
 
387
 
 
388
        # if the exception was caught below the generator.throw
 
389
        # (inside the generator), it will appear in the frames' linked
 
390
        # list, above the top-level traceback item (which must be the
 
391
        # generator frame itself, thus its caller is
 
392
        # throwExceptionIntoGenerator).
 
393
        frame = tb.tb_frame.f_back
 
394
        if frame and frame.f_code is cls.throwExceptionIntoGenerator.func_code:
 
395
            return frame.f_locals.get('self')
 
396
 
 
397
    _findFailure = classmethod(_findFailure)
 
398
 
 
399
    def __repr__(self):
 
400
        return "<%s %s>" % (self.__class__, self.type)
 
401
 
 
402
    def __str__(self):
 
403
        return "[Failure instance: %s]" % self.getBriefTraceback()
 
404
 
 
405
    def __getstate__(self):
 
406
        """Avoid pickling objects in the traceback.
 
407
        """
 
408
        if self.pickled:
 
409
            return self.__dict__
 
410
        c = self.__dict__.copy()
 
411
 
 
412
        c['frames'] = [
 
413
            [
 
414
                v[0], v[1], v[2],
 
415
                [(j[0], reflect.safe_repr(j[1])) for j in v[3]],
 
416
                [(j[0], reflect.safe_repr(j[1])) for j in v[4]]
 
417
            ] for v in self.frames
 
418
        ]
 
419
 
 
420
        # added 2003-06-23. See comment above in __init__
 
421
        c['tb'] = None
 
422
 
 
423
        if self.stack is not None:
 
424
            # XXX: This is a band-aid.  I can't figure out where these
 
425
            # (failure.stack is None) instances are coming from.
 
426
            c['stack'] = [
 
427
                [
 
428
                    v[0], v[1], v[2],
 
429
                    [(j[0], reflect.safe_repr(j[1])) for j in v[3]],
 
430
                    [(j[0], reflect.safe_repr(j[1])) for j in v[4]]
 
431
                ] for v in self.stack
 
432
            ]
 
433
 
 
434
        c['pickled'] = 1
 
435
        return c
 
436
 
 
437
    def cleanFailure(self):
 
438
        """Remove references to other objects, replacing them with strings.
 
439
        """
 
440
        self.__dict__ = self.__getstate__()
 
441
 
 
442
    def getTracebackObject(self):
 
443
        """
 
444
        Get an object that represents this Failure's stack that can be passed
 
445
        to traceback.extract_tb.
 
446
 
 
447
        If the original traceback object is still present, return that. If this
 
448
        traceback object has been lost but we still have the information,
 
449
        return a fake traceback object (see L{_Traceback}). If there is no
 
450
        traceback information at all, return None.
 
451
        """
 
452
        if self.tb is not None:
 
453
            return self.tb
 
454
        elif len(self.frames) > 0:
 
455
            return _Traceback(self.frames)
 
456
        else:
 
457
            return None
 
458
 
 
459
    def getErrorMessage(self):
 
460
        """Get a string of the exception which caused this Failure."""
 
461
        if isinstance(self.value, Failure):
 
462
            return self.value.getErrorMessage()
 
463
        return reflect.safe_str(self.value)
 
464
 
 
465
    def getBriefTraceback(self):
 
466
        io = StringIO()
 
467
        self.printBriefTraceback(file=io)
 
468
        return io.getvalue()
 
469
 
 
470
    def getTraceback(self, elideFrameworkCode=0, detail='default'):
 
471
        io = StringIO()
 
472
        self.printTraceback(file=io, elideFrameworkCode=elideFrameworkCode, detail=detail)
 
473
        return io.getvalue()
 
474
 
 
475
    def printTraceback(self, file=None, elideFrameworkCode=0, detail='default'):
 
476
        """Emulate Python's standard error reporting mechanism.
 
477
        """
 
478
        if file is None:
 
479
            file = log.logerr
 
480
        w = file.write
 
481
 
 
482
        # Preamble
 
483
        if detail == 'verbose':
 
484
            w( '*--- Failure #%d%s---\n' %
 
485
               (self.count,
 
486
                (self.pickled and ' (pickled) ') or ' '))
 
487
        elif detail == 'brief':
 
488
            if self.frames:
 
489
                hasFrames = 'Traceback'
 
490
            else:
 
491
                hasFrames = 'Traceback (failure with no frames)'
 
492
            w("%s: %s: %s\n" % (hasFrames, self.type, self.value))
 
493
        else:
 
494
            w( 'Traceback (most recent call last):\n')
 
495
 
 
496
        # Frames, formatted in appropriate style
 
497
        if self.frames:
 
498
            if not elideFrameworkCode:
 
499
                format_frames(self.stack[-traceupLength:], w, detail)
 
500
                w("%s\n" % (EXCEPTION_CAUGHT_HERE,))
 
501
            format_frames(self.frames, w, detail)
 
502
        elif not detail == 'brief':
 
503
            # Yeah, it's not really a traceback, despite looking like one...
 
504
            w("Failure: ")
 
505
 
 
506
        # postamble, if any
 
507
        if not detail == 'brief':
 
508
            # Unfortunately, self.type will not be a class object if this
 
509
            # Failure was created implicitly from a string exception.
 
510
            # qual() doesn't make any sense on a string, so check for this
 
511
            # case here and just write out the string if that's what we
 
512
            # have.
 
513
            if isinstance(self.type, (str, unicode)):
 
514
                w(self.type + "\n")
 
515
            else:
 
516
                w("%s: %s\n" % (reflect.qual(self.type),
 
517
                                reflect.safe_str(self.value)))
 
518
        # chaining
 
519
        if isinstance(self.value, Failure):
 
520
            # TODO: indentation for chained failures?
 
521
            file.write(" (chained Failure)\n")
 
522
            self.value.printTraceback(file, elideFrameworkCode, detail)
 
523
        if detail == 'verbose':
 
524
            w('*--- End of Failure #%d ---\n' % self.count)
 
525
 
 
526
    def printBriefTraceback(self, file=None, elideFrameworkCode=0):
 
527
        """Print a traceback as densely as possible.
 
528
        """
 
529
        self.printTraceback(file, elideFrameworkCode, detail='brief')
 
530
 
 
531
    def printDetailedTraceback(self, file=None, elideFrameworkCode=0):
 
532
        """Print a traceback with detailed locals and globals information.
 
533
        """
 
534
        self.printTraceback(file, elideFrameworkCode, detail='verbose')
 
535
 
 
536
# slyphon: make post-morteming exceptions tweakable
 
537
 
 
538
DO_POST_MORTEM = True
 
539
 
 
540
def _debuginit(self, exc_value=None, exc_type=None, exc_tb=None,
 
541
             Failure__init__=Failure.__init__.im_func):
 
542
    if (exc_value, exc_type, exc_tb) == (None, None, None):
 
543
        exc = sys.exc_info()
 
544
        if not exc[0] == self.__class__ and DO_POST_MORTEM:
 
545
            print "Jumping into debugger for post-mortem of exception '%s':" % exc[1]
 
546
            import pdb
 
547
            pdb.post_mortem(exc[2])
 
548
    Failure__init__(self, exc_value, exc_type, exc_tb)
 
549
 
 
550
def startDebugMode():
 
551
    """Enable debug hooks for Failures."""
 
552
    Failure.__init__ = _debuginit
 
553
 
 
554
 
 
555
# Sibling imports - at the bottom and unqualified to avoid unresolvable
 
556
# circularity
 
557
import log