Ticket #5854: inlinecallbacks-traceback-info-response-to-comments.3.patch

File inlinecallbacks-traceback-info-response-to-comments.3.patch, 7.7 KB (added by talljosh, 9 years ago)

Added more tests in response to latest review.

  • twisted/internet/defer.py

     
    10971097            return deferred
    10981098
    10991099        if isinstance(result, Deferred):
    1100             def extendErrbackStack(r):
     1100            def extendErrbackStack(f):
    11011101                """
    11021102                A failure was yielded by the generator, so for the failure's
    11031103                traceback to be useful insert the current yield line into the
    11041104                stack of frames.
    11051105                """
    1106                 f = g.gi_frame
    1107 
    1108                 # The following code is lifted almost straight from
    1109                 # twisted.python.failure.Failure.__init__()
    1110                 localz = f.f_locals.copy()
    1111                 if f.f_locals is f.f_globals:
    1112                     globalz = {}
    1113                 else:
    1114                     globalz = f.f_globals.copy()
    1115                 for d in globalz, localz:
    1116                     if d.has_key("__builtins__"):
    1117                         del d["__builtins__"]
    1118 
    1119                 r.frames.insert(0, [
    1120                     f.f_code.co_name,
    1121                     f.f_code.co_filename,
    1122                     f.f_lineno,
    1123                     localz.items(),
    1124                     globalz.items(),
    1125                     ])
    1126                 return r
     1106                f.frames.insert(0, failure.buildFrameRecord(g.gi_frame,
     1107                    f.captureVars))
     1108                return f
    11271109            result.addErrback(extendErrbackStack)
    11281110
    11291111            # a deferred was yielded, get the result.
  • twisted/internet/test/test_inlinecb.py

     
    1212from __future__ import division, absolute_import
    1313
    1414from twisted.trial.unittest import TestCase
     15from twisted.internet import defer
    1516from twisted.internet.defer import Deferred, returnValue, inlineCallbacks
    1617from twisted.python.failure import Failure
    1718
     
    9091
    9192
    9293class FrameStackTests(TestCase):
     94    def setUp(self):
     95        # Restore the debug flag to its original state when done.
     96        self.addCleanup(defer.setDebugging, defer.getDebugging())
     97
    9398    def test_failureContainsCallingFrames(self):
    9499        """
    95100        When L{inlineCallbacks} yields a failure, it should add itself to the
     
    121126        self.assertIn('inline2', functionNames)
    122127        self.assertIn('inline3', functionNames)
    123128
     129    def test_framesDoNotIncludeLocalsOrGlobalsWhenDebuggingIsDisabled(self):
     130        """
     131        When a failure is caught from an L{inlineCallbacks} stack, by default
     132        the locals and globals of the intermediate stack frames should not be
     133        saved.
     134        """
     135        defer.setDebugging(False)
     136
     137        @inlineCallbacks
     138        def inline1():
     139            yield 1/0
     140
     141        @inlineCallbacks
     142        def inline2(someLocal=3):
     143            yield inline1()
     144
     145        d = inline2()
     146        results = []
     147        d.addBoth(results.append)
     148
     149        failure = results[0]
     150        self.assertIsInstance(failure, Failure)
     151
     152        for funcName, filename, lineno, localVars, globalVars in failure.frames:
     153            self.assertEqual(len(localVars), 0)
     154            self.assertEqual(len(globalVars), 0)
     155
     156    def test_framesIncludeLocalsAndGlobalsWhenDebuggingIsEnabled(self):
     157        """
     158        When a failure is caught from an L{inlineCallbacks} stack, if that
     159        failure has the captureVars flag set, the flag should be respected.
     160        """
     161        defer.setDebugging(True)
     162
     163        @inlineCallbacks
     164        def inline1():
     165            yield 1/0
     166
     167        @inlineCallbacks
     168        def inline2(someLocal=3):
     169            yield inline1()
     170
     171        d = inline2()
     172        results = []
     173        d.addBoth(results.append)
     174
     175        failure = results[0]
     176        self.assertIsInstance(failure, Failure)
     177
     178        for funcName, filename, lineno, localVars, globalVars in failure.frames:
     179            self.assertNotEqual(len(globalVars), 0)
     180            if funcName == 'inline2':
     181                self.assertIn(('someLocal', 3), localVars)
     182
  • twisted/python/failure.py

     
    7373            for name, val in globalVars:
    7474                w("  %s : %s\n" %  (name, repr(val)))
    7575
     76
     77def buildFrameRecord(frame, captureVars, lineno=None):
     78    """Builds a frame record, optionally capturing local and global variables.
     79
     80    @param frame: the frame from which to build the record
     81    @type frame: python stack frame
     82    @param captureVars: whether or not to capture locals and globals
     83    @type captureVars: bool
     84    @param lineno: if given, overrides the line number in the stack frame
     85    @param lineno: int
     86    @returns: (funcName, fileName, lineNumber, locals.items(), globals.items())
     87    """
     88    if captureVars:
     89        localz = frame.f_locals.copy()
     90        if frame.f_locals is frame.f_globals:
     91            globalz = {}
     92        else:
     93            globalz = frame.f_globals.copy()
     94        for d in globalz, localz:
     95            if "__builtins__" in d:
     96                del d["__builtins__"]
     97        localz = list(localz.items())
     98        globalz = list(globalz.items())
     99    else:
     100        localz = globalz = ()
     101
     102    if lineno is None:
     103        lineno = frame.f_lineno
     104    return (
     105        frame.f_code.co_name,
     106        frame.f_code.co_filename,
     107        lineno,
     108        localz,
     109        globalz,
     110        )
     111
     112
    76113# slyphon: i have a need to check for this value in trial
    77114#          so I made it a module-level constant
    78115EXCEPTION_CAUGHT_HERE = "--- <exception caught here> ---"
     
    262299        #   what called upon the PB object.
    263300
    264301        while f:
    265             if captureVars:
    266                 localz = f.f_locals.copy()
    267                 if f.f_locals is f.f_globals:
    268                     globalz = {}
    269                 else:
    270                     globalz = f.f_globals.copy()
    271                 for d in globalz, localz:
    272                     if "__builtins__" in d:
    273                         del d["__builtins__"]
    274                 localz = localz.items()
    275                 globalz = globalz.items()
    276             else:
    277                 localz = globalz = ()
    278             stack.insert(0, (
    279                 f.f_code.co_name,
    280                 f.f_code.co_filename,
    281                 f.f_lineno,
    282                 localz,
    283                 globalz,
    284                 ))
     302            frameRecord = buildFrameRecord(f, captureVars)
     303            stack.insert(0, frameRecord)
    285304            f = f.f_back
    286305
    287306        while tb is not None:
    288             f = tb.tb_frame
    289             if captureVars:
    290                 localz = f.f_locals.copy()
    291                 if f.f_locals is f.f_globals:
    292                     globalz = {}
    293                 else:
    294                     globalz = f.f_globals.copy()
    295                 for d in globalz, localz:
    296                     if "__builtins__" in d:
    297                         del d["__builtins__"]
    298                 localz = list(localz.items())
    299                 globalz = list(globalz.items())
    300             else:
    301                 localz = globalz = ()
    302             frames.append((
    303                 f.f_code.co_name,
    304                 f.f_code.co_filename,
    305                 tb.tb_lineno,
    306                 localz,
    307                 globalz,
    308                 ))
     307            frameRecord = buildFrameRecord(tb.tb_frame, captureVars,
     308                lineno=tb.tb_lineno)
     309            frames.append(frameRecord)
    309310            tb = tb.tb_next
    310311        if inspect.isclass(self.type) and issubclass(self.type, Exception):
    311312            parentCs = getmro(self.type)