Repository: twisted/twisted

Issue:
Optimize the Deferred errback benchmark
There appear to be some straightforward ways to make Deferred/Failure error handling faster.

Discussion:

Patch:
diff --git a/src/twisted/newsfragments/12227.feature b/src/twisted/newsfragments/12227.feature
new file mode 100644
index 0000000000..8f11ffd838
--- /dev/null
+++ b/src/twisted/newsfragments/12227.feature
@@ -0,0 +1,1 @@
+twisted.internet.defer.Deferred error handling is now faster, taking 40% less time to run.
\ No newline at end of file
diff --git a/src/twisted/python/failure.py b/src/twisted/python/failure.py
index e2048523a9..39712ecf5c 100644
--- a/src/twisted/python/failure.py
+++ b/src/twisted/python/failure.py
@@ -365,7 +365,6 @@ class Failure(BaseException):
         #   with bareword "except:"s.  This premature exception
         #   catching means tracebacks generated here don't tend to show
         #   what called upon the PB object.
-
         while f:
             if captureVars:
                 localz = f.f_locals.copy()
@@ -634,16 +633,17 @@ class Failure(BaseException):
         # Backwards compatibility with old code, e.g. for Perspective Broker:
         c["parents"] = c.pop("_parents")
 
-        c["frames"] = [
-            [
-                v[0],
-                v[1],
-                v[2],
-                _safeReprVars(v[3]),
-                _safeReprVars(v[4]),
+        if self.captureVars:
+            c["frames"] = [
+                [
+                    v[0],
+                    v[1],
+                    v[2],
+                    _safeReprVars(v[3]),
+                    _safeReprVars(v[4]),
+                ]
+                for v in self.frames
             ]
-            for v in self.frames
-        ]
 
         # Added 2003-06-23. See comment above in __init__
         c["tb"] = None
@@ -651,16 +651,17 @@ class Failure(BaseException):
         if self.stack is not None:
             # XXX: This is a band-aid.  I can't figure out where these
             # (failure.stack is None) instances are coming from.
-            c["stack"] = [
-                [
-                    v[0],
-                    v[1],
-                    v[2],
-                    _safeReprVars(v[3]),
-                    _safeReprVars(v[4]),
+            if self.captureVars:
+                c["stack"] = [
+                    [
+                        v[0],
+                        v[1],
+                        v[2],
+                        _safeReprVars(v[3]),
+                        _safeReprVars(v[4]),
+                    ]
+                    for v in self.stack
                 ]
-                for v in self.stack
-            ]
 
         c["pickled"] = 1
         return c


Test patch:
diff --git a/src/twisted/test/test_defer.py b/src/twisted/test/test_defer.py
index 8f8ebd5737..f068d827f0 100644
--- a/src/twisted/test/test_defer.py
+++ b/src/twisted/test/test_defer.py
@@ -1624,8 +1624,8 @@ class DeferredTests(unittest.SynchronousTestCase, ImmediateFailureMixin):
         fail = l[0]
         self.assertEqual(fail.value, exc)
         localz, globalz = fail.frames[0][-2:]
-        self.assertEqual([], localz)
-        self.assertEqual([], globalz)
+        self.assertEqual((), localz)
+        self.assertEqual((), globalz)
 
     def test_errbackWithNoArgs(self) -> None:
         """
@@ -1665,8 +1665,8 @@ class DeferredTests(unittest.SynchronousTestCase, ImmediateFailureMixin):
         d.addErrback(l.append)
         fail = l[0]
         localz, globalz = fail.frames[0][-2:]
-        self.assertEqual([], localz)
-        self.assertEqual([], globalz)
+        self.assertEqual((), localz)
+        self.assertEqual((), globalz)
 
     def test_errorInCallbackCapturesVarsWhenDebugging(self) -> None:
         """