You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

failure.py 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798
  1. # -*- test-case-name: twisted.test.test_failure -*-
  2. # See also test suite twisted.test.test_pbfailure
  3. # Copyright (c) Twisted Matrix Laboratories.
  4. # See LICENSE for details.
  5. """
  6. Asynchronous-friendly error mechanism.
  7. See L{Failure}.
  8. """
  9. from __future__ import division, absolute_import, print_function
  10. # System Imports
  11. import copy
  12. import sys
  13. import linecache
  14. import inspect
  15. import opcode
  16. from inspect import getmro
  17. from twisted.python import reflect
  18. from twisted.python.compat import _PY3, NativeStringIO as StringIO
  19. count = 0
  20. traceupLength = 4
  21. class DefaultException(Exception):
  22. pass
  23. def format_frames(frames, write, detail="default"):
  24. """
  25. Format and write frames.
  26. @param frames: is a list of frames as used by Failure.frames, with
  27. each frame being a list of
  28. (funcName, fileName, lineNumber, locals.items(), globals.items())
  29. @type frames: list
  30. @param write: this will be called with formatted strings.
  31. @type write: callable
  32. @param detail: Four detail levels are available:
  33. default, brief, verbose, and verbose-vars-not-captured.
  34. C{Failure.printDetailedTraceback} uses the latter when the caller asks
  35. for verbose, but no vars were captured, so that an explicit warning
  36. about the missing data is shown.
  37. @type detail: string
  38. """
  39. if detail not in ('default', 'brief', 'verbose',
  40. 'verbose-vars-not-captured'):
  41. raise ValueError(
  42. "Detail must be default, brief, verbose, or "
  43. "verbose-vars-not-captured. (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-vars-not-captured":
  53. for method, filename, lineno, localVars, globalVars in frames:
  54. w("%s:%d: %s(...)\n" % (filename, lineno, method))
  55. w(' [Capture of Locals and Globals disabled (use captureVars=True)]\n')
  56. elif detail == "verbose":
  57. for method, filename, lineno, localVars, globalVars in frames:
  58. w("%s:%d: %s(...)\n" % (filename, lineno, method))
  59. w(' [ Locals ]\n')
  60. # Note: the repr(val) was (self.pickled and val) or repr(val)))
  61. for name, val in localVars:
  62. w(" %s : %s\n" % (name, repr(val)))
  63. w(' ( Globals )\n')
  64. for name, val in globalVars:
  65. w(" %s : %s\n" % (name, repr(val)))
  66. # slyphon: i have a need to check for this value in trial
  67. # so I made it a module-level constant
  68. EXCEPTION_CAUGHT_HERE = "--- <exception caught here> ---"
  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. def _Traceback(stackFrames, tbFrames):
  75. """
  76. Construct a fake traceback object using a list of frames. Note that
  77. although frames generally include locals and globals, this information
  78. is not kept by this method, since locals and globals are not used in
  79. standard tracebacks.
  80. @param stackFrames: [(methodname, filename, lineno, locals, globals), ...]
  81. @param tbFrames: [(methodname, filename, lineno, locals, globals), ...]
  82. """
  83. assert len(tbFrames) > 0, "Must pass some frames"
  84. # We deliberately avoid using recursion here, as the frames list may be
  85. # long.
  86. # 'stackFrames' is a list of frames above (ie, older than) the point the
  87. # exception was caught, with oldest at the start. Start by building these
  88. # into a linked list of _Frame objects (with the f_back links pointing back
  89. # towards the oldest frame).
  90. stack = None
  91. for sf in stackFrames:
  92. stack = _Frame(sf, stack)
  93. # 'tbFrames' is a list of frames from the point the exception was caught,
  94. # down to where it was thrown, with the oldest at the start. Add these to
  95. # the linked list of _Frames, but also wrap each one with a _Traceback
  96. # frame which is linked in the opposite direction (towards the newest
  97. # frame).
  98. stack = _Frame(tbFrames[0], stack)
  99. firstTb = tb = _TracebackFrame(stack)
  100. for sf in tbFrames[1:]:
  101. stack = _Frame(sf, stack)
  102. tb.tb_next = _TracebackFrame(stack)
  103. tb = tb.tb_next
  104. # Return the first _TracebackFrame.
  105. return firstTb
  106. class _TracebackFrame(object):
  107. """
  108. Fake traceback object which can be passed to functions in the standard
  109. library L{traceback} module.
  110. """
  111. def __init__(self, frame):
  112. """
  113. @param frame: _Frame object
  114. """
  115. self.tb_frame = frame
  116. self.tb_lineno = frame.f_lineno
  117. self.tb_next = None
  118. class _Frame(object):
  119. """
  120. A fake frame object, used by L{_Traceback}.
  121. @ivar f_code: fake L{code<types.CodeType>} object
  122. @ivar f_lineno: line number
  123. @ivar f_globals: fake f_globals dictionary (usually empty)
  124. @ivar f_locals: fake f_locals dictionary (usually empty)
  125. @ivar f_back: previous stack frame (towards the caller)
  126. """
  127. def __init__(self, frameinfo, back):
  128. """
  129. @param frameinfo: (methodname, filename, lineno, locals, globals)
  130. @param back: previous (older) stack frame
  131. @type back: C{frame}
  132. """
  133. name, filename, lineno, localz, globalz = frameinfo
  134. self.f_code = _Code(name, filename)
  135. self.f_lineno = lineno
  136. self.f_globals = {}
  137. self.f_locals = {}
  138. self.f_back = back
  139. class _Code(object):
  140. """
  141. A fake code object, used by L{_Traceback} via L{_Frame}.
  142. """
  143. def __init__(self, name, filename):
  144. self.co_name = name
  145. self.co_filename = filename
  146. _inlineCallbacksExtraneous = []
  147. def _extraneous(f):
  148. """
  149. Mark the given callable as extraneous to inlineCallbacks exception
  150. reporting; don't show these functions.
  151. @param f: a function that you NEVER WANT TO SEE AGAIN in ANY TRACEBACK
  152. reported by Failure.
  153. @type f: function
  154. @return: f
  155. """
  156. _inlineCallbacksExtraneous.append(f.__code__)
  157. return f
  158. class Failure(BaseException):
  159. """
  160. A basic abstraction for an error that has occurred.
  161. This is necessary because Python's built-in error mechanisms are
  162. inconvenient for asynchronous communication.
  163. The C{stack} and C{frame} attributes contain frames. Each frame is a tuple
  164. of (funcName, fileName, lineNumber, localsItems, globalsItems), where
  165. localsItems and globalsItems are the contents of
  166. C{locals().items()}/C{globals().items()} for that frame, or an empty tuple
  167. if those details were not captured.
  168. @ivar value: The exception instance responsible for this failure.
  169. @ivar type: The exception's class.
  170. @ivar stack: list of frames, innermost last, excluding C{Failure.__init__}.
  171. @ivar frames: list of frames, innermost first.
  172. """
  173. pickled = 0
  174. stack = None
  175. # The opcode of "yield" in Python bytecode. We need this in
  176. # _findFailure in order to identify whether an exception was
  177. # thrown by a throwExceptionIntoGenerator.
  178. # on PY3, b'a'[0] == 97 while in py2 b'a'[0] == b'a' opcodes
  179. # are stored in bytes so we need to properly account for this
  180. # difference.
  181. if _PY3:
  182. _yieldOpcode = opcode.opmap["YIELD_VALUE"]
  183. else:
  184. _yieldOpcode = chr(opcode.opmap["YIELD_VALUE"])
  185. def __init__(self, exc_value=None, exc_type=None, exc_tb=None,
  186. captureVars=False):
  187. """
  188. Initialize me with an explanation of the error.
  189. By default, this will use the current C{exception}
  190. (L{sys.exc_info}()). However, if you want to specify a
  191. particular kind of failure, you can pass an exception as an
  192. argument.
  193. If no C{exc_value} is passed, then an "original" C{Failure} will
  194. be searched for. If the current exception handler that this
  195. C{Failure} is being constructed in is handling an exception
  196. raised by L{raiseException}, then this C{Failure} will act like
  197. the original C{Failure}.
  198. For C{exc_tb} only L{traceback} instances or L{None} are allowed.
  199. If L{None} is supplied for C{exc_value}, the value of C{exc_tb} is
  200. ignored, otherwise if C{exc_tb} is L{None}, it will be found from
  201. execution context (ie, L{sys.exc_info}).
  202. @param captureVars: if set, capture locals and globals of stack
  203. frames. This is pretty slow, and makes no difference unless you
  204. are going to use L{printDetailedTraceback}.
  205. """
  206. global count
  207. count = count + 1
  208. self.count = count
  209. self.type = self.value = tb = None
  210. self.captureVars = captureVars
  211. if isinstance(exc_value, str) and exc_type is None:
  212. raise TypeError("Strings are not supported by Failure")
  213. stackOffset = 0
  214. if exc_value is None:
  215. exc_value = self._findFailure()
  216. if exc_value is None:
  217. self.type, self.value, tb = sys.exc_info()
  218. if self.type is None:
  219. raise NoCurrentExceptionError()
  220. stackOffset = 1
  221. elif exc_type is None:
  222. if isinstance(exc_value, Exception):
  223. self.type = exc_value.__class__
  224. else:
  225. # Allow arbitrary objects.
  226. self.type = type(exc_value)
  227. self.value = exc_value
  228. else:
  229. self.type = exc_type
  230. self.value = exc_value
  231. if isinstance(self.value, Failure):
  232. self._extrapolate(self.value)
  233. return
  234. if hasattr(self.value, "__failure__"):
  235. # For exceptions propagated through coroutine-awaiting (see
  236. # Deferred.send, AKA Deferred.__next__), which can't be raised as
  237. # Failure because that would mess up the ability to except: them:
  238. self._extrapolate(self.value.__failure__)
  239. # Clean up the inherently circular reference established by storing
  240. # the failure there. This should make the common case of a Twisted
  241. # / Deferred-returning coroutine somewhat less hard on the garbage
  242. # collector.
  243. del self.value.__failure__
  244. return
  245. if tb is None:
  246. if exc_tb:
  247. tb = exc_tb
  248. elif getattr(self.value, "__traceback__", None):
  249. # Python 3
  250. tb = self.value.__traceback__
  251. frames = self.frames = []
  252. stack = self.stack = []
  253. # Added 2003-06-23 by Chris Armstrong. Yes, I actually have a
  254. # use case where I need this traceback object, and I've made
  255. # sure that it'll be cleaned up.
  256. self.tb = tb
  257. if tb:
  258. f = tb.tb_frame
  259. elif not isinstance(self.value, Failure):
  260. # We don't do frame introspection since it's expensive,
  261. # and if we were passed a plain exception with no
  262. # traceback, it's not useful anyway
  263. f = stackOffset = None
  264. while stackOffset and f:
  265. # This excludes this Failure.__init__ frame from the
  266. # stack, leaving it to start with our caller instead.
  267. f = f.f_back
  268. stackOffset -= 1
  269. # Keeps the *full* stack. Formerly in spread.pb.print_excFullStack:
  270. #
  271. # The need for this function arises from the fact that several
  272. # PB classes have the peculiar habit of discarding exceptions
  273. # with bareword "except:"s. This premature exception
  274. # catching means tracebacks generated here don't tend to show
  275. # what called upon the PB object.
  276. while f:
  277. if captureVars:
  278. localz = f.f_locals.copy()
  279. if f.f_locals is f.f_globals:
  280. globalz = {}
  281. else:
  282. globalz = f.f_globals.copy()
  283. for d in globalz, localz:
  284. if "__builtins__" in d:
  285. del d["__builtins__"]
  286. localz = localz.items()
  287. globalz = globalz.items()
  288. else:
  289. localz = globalz = ()
  290. stack.insert(0, (
  291. f.f_code.co_name,
  292. f.f_code.co_filename,
  293. f.f_lineno,
  294. localz,
  295. globalz,
  296. ))
  297. f = f.f_back
  298. while tb is not None:
  299. f = tb.tb_frame
  300. if captureVars:
  301. localz = f.f_locals.copy()
  302. if f.f_locals is f.f_globals:
  303. globalz = {}
  304. else:
  305. globalz = f.f_globals.copy()
  306. for d in globalz, localz:
  307. if "__builtins__" in d:
  308. del d["__builtins__"]
  309. localz = list(localz.items())
  310. globalz = list(globalz.items())
  311. else:
  312. localz = globalz = ()
  313. frames.append((
  314. f.f_code.co_name,
  315. f.f_code.co_filename,
  316. tb.tb_lineno,
  317. localz,
  318. globalz,
  319. ))
  320. tb = tb.tb_next
  321. if inspect.isclass(self.type) and issubclass(self.type, Exception):
  322. parentCs = getmro(self.type)
  323. self.parents = list(map(reflect.qual, parentCs))
  324. else:
  325. self.parents = [self.type]
  326. def _extrapolate(self, otherFailure):
  327. """
  328. Extrapolate from one failure into another, copying its stack frames.
  329. @param otherFailure: Another L{Failure}, whose traceback information,
  330. if any, should be preserved as part of the stack presented by this
  331. one.
  332. @type otherFailure: L{Failure}
  333. """
  334. # Copy all infos from that failure (including self.frames).
  335. self.__dict__ = copy.copy(otherFailure.__dict__)
  336. # If we are re-throwing a Failure, we merge the stack-trace stored in
  337. # the failure with the current exception's stack. This integrated with
  338. # throwExceptionIntoGenerator and allows to provide full stack trace,
  339. # even if we go through several layers of inlineCallbacks.
  340. _, _, tb = sys.exc_info()
  341. frames = []
  342. while tb is not None:
  343. f = tb.tb_frame
  344. if f.f_code not in _inlineCallbacksExtraneous:
  345. frames.append((
  346. f.f_code.co_name,
  347. f.f_code.co_filename,
  348. tb.tb_lineno, (), ()
  349. ))
  350. tb = tb.tb_next
  351. # Merging current stack with stack stored in the Failure.
  352. frames.extend(self.frames)
  353. self.frames = frames
  354. def trap(self, *errorTypes):
  355. """
  356. Trap this failure if its type is in a predetermined list.
  357. This allows you to trap a Failure in an error callback. It will be
  358. automatically re-raised if it is not a type that you expect.
  359. The reason for having this particular API is because it's very useful
  360. in Deferred errback chains::
  361. def _ebFoo(self, failure):
  362. r = failure.trap(Spam, Eggs)
  363. print('The Failure is due to either Spam or Eggs!')
  364. if r == Spam:
  365. print('Spam did it!')
  366. elif r == Eggs:
  367. print('Eggs did it!')
  368. If the failure is not a Spam or an Eggs, then the Failure will be
  369. 'passed on' to the next errback. In Python 2 the Failure will be
  370. raised; in Python 3 the underlying exception will be re-raised.
  371. @type errorTypes: L{Exception}
  372. """
  373. error = self.check(*errorTypes)
  374. if not error:
  375. if _PY3:
  376. self.raiseException()
  377. else:
  378. raise self
  379. return error
  380. def check(self, *errorTypes):
  381. """
  382. Check if this failure's type is in a predetermined list.
  383. @type errorTypes: list of L{Exception} classes or
  384. fully-qualified class names.
  385. @returns: the matching L{Exception} type, or None if no match.
  386. """
  387. for error in errorTypes:
  388. err = error
  389. if inspect.isclass(error) and issubclass(error, Exception):
  390. err = reflect.qual(error)
  391. if err in self.parents:
  392. return error
  393. return None
  394. # It would be nice to use twisted.python.compat.reraise, but that breaks
  395. # the stack exploration in _findFailure; possibly this can be fixed in
  396. # #5931.
  397. if getattr(BaseException, "with_traceback", None):
  398. # Python 3
  399. def raiseException(self):
  400. raise self.value.with_traceback(self.tb)
  401. else:
  402. exec("""def raiseException(self):
  403. raise self.type, self.value, self.tb""")
  404. raiseException.__doc__ = (
  405. """
  406. raise the original exception, preserving traceback
  407. information if available.
  408. """)
  409. @_extraneous
  410. def throwExceptionIntoGenerator(self, g):
  411. """
  412. Throw the original exception into the given generator,
  413. preserving traceback information if available.
  414. @return: The next value yielded from the generator.
  415. @raise StopIteration: If there are no more values in the generator.
  416. @raise anything else: Anything that the generator raises.
  417. """
  418. # Note that the actual magic to find the traceback information
  419. # is done in _findFailure.
  420. return g.throw(self.type, self.value, self.tb)
  421. def _findFailure(cls):
  422. """
  423. Find the failure that represents the exception currently in context.
  424. """
  425. tb = sys.exc_info()[-1]
  426. if not tb:
  427. return
  428. secondLastTb = None
  429. lastTb = tb
  430. while lastTb.tb_next:
  431. secondLastTb = lastTb
  432. lastTb = lastTb.tb_next
  433. lastFrame = lastTb.tb_frame
  434. # NOTE: f_locals.get('self') is used rather than
  435. # f_locals['self'] because psyco frames do not contain
  436. # anything in their locals() dicts. psyco makes debugging
  437. # difficult anyhow, so losing the Failure objects (and thus
  438. # the tracebacks) here when it is used is not that big a deal.
  439. # Handle raiseException-originated exceptions
  440. if lastFrame.f_code is cls.raiseException.__code__:
  441. return lastFrame.f_locals.get('self')
  442. # Handle throwExceptionIntoGenerator-originated exceptions
  443. # this is tricky, and differs if the exception was caught
  444. # inside the generator, or above it:
  445. # It is only really originating from
  446. # throwExceptionIntoGenerator if the bottom of the traceback
  447. # is a yield.
  448. # Pyrex and Cython extensions create traceback frames
  449. # with no co_code, but they can't yield so we know it's okay to
  450. # just return here.
  451. if ((not lastFrame.f_code.co_code) or
  452. lastFrame.f_code.co_code[lastTb.tb_lasti] != cls._yieldOpcode):
  453. return
  454. # If the exception was caught above the generator.throw
  455. # (outside the generator), it will appear in the tb (as the
  456. # second last item):
  457. if secondLastTb:
  458. frame = secondLastTb.tb_frame
  459. if frame.f_code is cls.throwExceptionIntoGenerator.__code__:
  460. return frame.f_locals.get('self')
  461. # If the exception was caught below the generator.throw
  462. # (inside the generator), it will appear in the frames' linked
  463. # list, above the top-level traceback item (which must be the
  464. # generator frame itself, thus its caller is
  465. # throwExceptionIntoGenerator).
  466. frame = tb.tb_frame.f_back
  467. if frame and frame.f_code is cls.throwExceptionIntoGenerator.__code__:
  468. return frame.f_locals.get('self')
  469. _findFailure = classmethod(_findFailure)
  470. def __repr__(self):
  471. return "<%s %s: %s>" % (reflect.qual(self.__class__),
  472. reflect.qual(self.type),
  473. self.getErrorMessage())
  474. def __str__(self):
  475. return "[Failure instance: %s]" % self.getBriefTraceback()
  476. def __getstate__(self):
  477. """Avoid pickling objects in the traceback.
  478. """
  479. if self.pickled:
  480. return self.__dict__
  481. c = self.__dict__.copy()
  482. c['frames'] = [
  483. [
  484. v[0], v[1], v[2],
  485. _safeReprVars(v[3]),
  486. _safeReprVars(v[4]),
  487. ] for v in self.frames
  488. ]
  489. # Added 2003-06-23. See comment above in __init__
  490. c['tb'] = None
  491. if self.stack is not None:
  492. # XXX: This is a band-aid. I can't figure out where these
  493. # (failure.stack is None) instances are coming from.
  494. c['stack'] = [
  495. [
  496. v[0], v[1], v[2],
  497. _safeReprVars(v[3]),
  498. _safeReprVars(v[4]),
  499. ] for v in self.stack
  500. ]
  501. c['pickled'] = 1
  502. return c
  503. def cleanFailure(self):
  504. """
  505. Remove references to other objects, replacing them with strings.
  506. On Python 3, this will also set the C{__traceback__} attribute of the
  507. exception instance to L{None}.
  508. """
  509. self.__dict__ = self.__getstate__()
  510. if getattr(self.value, "__traceback__", None):
  511. # Python 3
  512. self.value.__traceback__ = None
  513. def getTracebackObject(self):
  514. """
  515. Get an object that represents this Failure's stack that can be passed
  516. to traceback.extract_tb.
  517. If the original traceback object is still present, return that. If this
  518. traceback object has been lost but we still have the information,
  519. return a fake traceback object (see L{_Traceback}). If there is no
  520. traceback information at all, return None.
  521. """
  522. if self.tb is not None:
  523. return self.tb
  524. elif len(self.frames) > 0:
  525. return _Traceback(self.stack, self.frames)
  526. else:
  527. return None
  528. def getErrorMessage(self):
  529. """
  530. Get a string of the exception which caused this Failure.
  531. """
  532. if isinstance(self.value, Failure):
  533. return self.value.getErrorMessage()
  534. return reflect.safe_str(self.value)
  535. def getBriefTraceback(self):
  536. io = StringIO()
  537. self.printBriefTraceback(file=io)
  538. return io.getvalue()
  539. def getTraceback(self, elideFrameworkCode=0, detail='default'):
  540. io = StringIO()
  541. self.printTraceback(file=io, elideFrameworkCode=elideFrameworkCode,
  542. detail=detail)
  543. return io.getvalue()
  544. def printTraceback(self, file=None, elideFrameworkCode=False,
  545. detail='default'):
  546. """
  547. Emulate Python's standard error reporting mechanism.
  548. @param file: If specified, a file-like object to which to write the
  549. traceback.
  550. @param elideFrameworkCode: A flag indicating whether to attempt to
  551. remove uninteresting frames from within Twisted itself from the
  552. output.
  553. @param detail: A string indicating how much information to include
  554. in the traceback. Must be one of C{'brief'}, C{'default'}, or
  555. C{'verbose'}.
  556. """
  557. if file is None:
  558. from twisted.python import log
  559. file = log.logerr
  560. w = file.write
  561. if detail == 'verbose' and not self.captureVars:
  562. # We don't have any locals or globals, so rather than show them as
  563. # empty make the output explicitly say that we don't have them at
  564. # all.
  565. formatDetail = 'verbose-vars-not-captured'
  566. else:
  567. formatDetail = detail
  568. # Preamble
  569. if detail == 'verbose':
  570. w('*--- Failure #%d%s---\n' %
  571. (self.count,
  572. (self.pickled and ' (pickled) ') or ' '))
  573. elif detail == 'brief':
  574. if self.frames:
  575. hasFrames = 'Traceback'
  576. else:
  577. hasFrames = 'Traceback (failure with no frames)'
  578. w("%s: %s: %s\n" % (
  579. hasFrames,
  580. reflect.safe_str(self.type),
  581. reflect.safe_str(self.value)))
  582. else:
  583. w('Traceback (most recent call last):\n')
  584. # Frames, formatted in appropriate style
  585. if self.frames:
  586. if not elideFrameworkCode:
  587. format_frames(self.stack[-traceupLength:], w, formatDetail)
  588. w("%s\n" % (EXCEPTION_CAUGHT_HERE,))
  589. format_frames(self.frames, w, formatDetail)
  590. elif not detail == 'brief':
  591. # Yeah, it's not really a traceback, despite looking like one...
  592. w("Failure: ")
  593. # Postamble, if any
  594. if not detail == 'brief':
  595. w("%s: %s\n" % (reflect.qual(self.type),
  596. reflect.safe_str(self.value)))
  597. # Chaining
  598. if isinstance(self.value, Failure):
  599. # TODO: indentation for chained failures?
  600. file.write(" (chained Failure)\n")
  601. self.value.printTraceback(file, elideFrameworkCode, detail)
  602. if detail == 'verbose':
  603. w('*--- End of Failure #%d ---\n' % self.count)
  604. def printBriefTraceback(self, file=None, elideFrameworkCode=0):
  605. """
  606. Print a traceback as densely as possible.
  607. """
  608. self.printTraceback(file, elideFrameworkCode, detail='brief')
  609. def printDetailedTraceback(self, file=None, elideFrameworkCode=0):
  610. """
  611. Print a traceback with detailed locals and globals information.
  612. """
  613. self.printTraceback(file, elideFrameworkCode, detail='verbose')
  614. def _safeReprVars(varsDictItems):
  615. """
  616. Convert a list of (name, object) pairs into (name, repr) pairs.
  617. L{twisted.python.reflect.safe_repr} is used to generate the repr, so no
  618. exceptions will be raised by faulty C{__repr__} methods.
  619. @param varsDictItems: a sequence of (name, value) pairs as returned by e.g.
  620. C{locals().items()}.
  621. @returns: a sequence of (name, repr) pairs.
  622. """
  623. return [(name, reflect.safe_repr(obj)) for (name, obj) in varsDictItems]
  624. # slyphon: make post-morteming exceptions tweakable
  625. DO_POST_MORTEM = True
  626. def _debuginit(self, exc_value=None, exc_type=None, exc_tb=None,
  627. captureVars=False,
  628. Failure__init__=Failure.__init__):
  629. """
  630. Initialize failure object, possibly spawning pdb.
  631. """
  632. if (exc_value, exc_type, exc_tb) == (None, None, None):
  633. exc = sys.exc_info()
  634. if not exc[0] == self.__class__ and DO_POST_MORTEM:
  635. try:
  636. strrepr = str(exc[1])
  637. except:
  638. strrepr = "broken str"
  639. print("Jumping into debugger for post-mortem of exception '%s':" %
  640. (strrepr,))
  641. import pdb
  642. pdb.post_mortem(exc[2])
  643. Failure__init__(self, exc_value, exc_type, exc_tb, captureVars)
  644. def startDebugMode():
  645. """
  646. Enable debug hooks for Failures.
  647. """
  648. Failure.__init__ = _debuginit