We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
remove request.exception if the excview tween fails to handle the exception
Pyramid 1.9 makes `request.exception` and `request.exc_info` a little more important as I've moved the pyramid_tm tween over the excview and in general would advocate to move most tweens over the excview. With that in mind it's currently not possible to test `request.exception` to see if the response was rendered in relation to that exception - the excview tween sets the exception even if it failed to squash it (attempted to render an excview and couldn't find one). Ideally the exception would be related to the response that was generated when it was squashed. This would be more explicit if we used `response.exception` to indicate the response is from a squashed exception but I think that's a larger change.

I'm proposing to remove `request.exception` and `request.exc_info` in the excview tween if it reraises the original exception. This makes introspection `request.exception` more reliable by upstream tweens that want to know what the squashed exception was... Of course any raised exception should be more interesting than the original `request.exception` but if the tween receives a response then they can see if it is a response generated by a squashed exception or if it is a "normal" response.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `pyramid/tweens.py`
Content:
```
1 import sys
2 
3 from pyramid.compat import reraise
4 from pyramid.exceptions import PredicateMismatch
5 from pyramid.interfaces import (
6     IExceptionViewClassifier,
7     IRequest,
8     )
9 
10 from zope.interface import providedBy
11 from pyramid.view import _call_view
12 
13 def excview_tween_factory(handler, registry):
14     """ A :term:`tween` factory which produces a tween that catches an
15     exception raised by downstream tweens (or the main Pyramid request
16     handler) and, if possible, converts it into a Response using an
17     :term:`exception view`."""
18 
19     def excview_tween(request):
20         attrs = request.__dict__
21         try:
22             response = handler(request)
23         except Exception as exc:
24             # WARNING: do not assign the result of sys.exc_info() to a local
25             # var here, doing so will cause a leak.  We used to actually
26             # explicitly delete both "exception" and "exc_info" from ``attrs``
27             # in a ``finally:`` clause below, but now we do not because these
28             # attributes are useful to upstream tweens.  This actually still
29             # apparently causes a reference cycle, but it is broken
30             # successfully by the garbage collector (see
31             # https://github.com/Pylons/pyramid/issues/1223).
32             attrs['exc_info'] = sys.exc_info()
33             attrs['exception'] = exc
34             # clear old generated request.response, if any; it may
35             # have been mutated by the view, and its state is not
36             # sane (e.g. caching headers)
37             if 'response' in attrs:
38                 del attrs['response']
39             # we use .get instead of .__getitem__ below due to
40             # https://github.com/Pylons/pyramid/issues/700
41             request_iface = attrs.get('request_iface', IRequest)
42             provides = providedBy(exc)
43             try:
44                 response = _call_view(
45                     registry,
46                     request,
47                     exc,
48                     provides,
49                     '',
50                     view_classifier=IExceptionViewClassifier,
51                     request_iface=request_iface.combined
52                     )
53 
54             # if views matched but did not pass predicates, squash the error
55             # and re-raise the original exception
56             except PredicateMismatch:
57                 response = None
58 
59             # re-raise the original exception as no exception views were
60             # able to handle the error
61             if response is None:
62                 reraise(*attrs['exc_info'])
63 
64         return response
65 
66     return excview_tween
67 
68 MAIN = 'MAIN'
69 INGRESS = 'INGRESS'
70 EXCVIEW = 'pyramid.tweens.excview_tween_factory'
71
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/pyramid/tweens.py b/pyramid/tweens.py
--- a/pyramid/tweens.py
+++ b/pyramid/tweens.py
@@ -10,6 +10,50 @@
 from zope.interface import providedBy
 from pyramid.view import _call_view
 
+def _error_handler(request, exc):
+    # NOTE: we do not need to delete exc_info because this function
+    # should never be in the call stack of the exception
+    exc_info = sys.exc_info()
+
+    attrs = request.__dict__
+    attrs['exc_info'] = exc_info
+    attrs['exception'] = exc
+    # clear old generated request.response, if any; it may
+    # have been mutated by the view, and its state is not
+    # sane (e.g. caching headers)
+    if 'response' in attrs:
+        del attrs['response']
+    # we use .get instead of .__getitem__ below due to
+    # https://github.com/Pylons/pyramid/issues/700
+    request_iface = attrs.get('request_iface', IRequest)
+    provides = providedBy(exc)
+    try:
+        response = _call_view(
+            request.registry,
+            request,
+            exc,
+            provides,
+            '',
+            view_classifier=IExceptionViewClassifier,
+            request_iface=request_iface.combined
+            )
+
+    # if views matched but did not pass predicates then treat the
+    # same as not finding any matching views
+    except PredicateMismatch:
+        response = None
+
+    # re-raise the original exception as no exception views were
+    # able to handle the error
+    if response is None:
+        if 'exception' in attrs:
+            del attrs['exception']
+        if 'exc_info' in attrs:
+            del attrs['exc_info']
+        reraise(*exc_info)
+
+    return response
+
 def excview_tween_factory(handler, registry):
     """ A :term:`tween` factory which produces a tween that catches an
     exception raised by downstream tweens (or the main Pyramid request
@@ -17,50 +61,10 @@
     :term:`exception view`."""
 
     def excview_tween(request):
-        attrs = request.__dict__
         try:
             response = handler(request)
         except Exception as exc:
-            # WARNING: do not assign the result of sys.exc_info() to a local
-            # var here, doing so will cause a leak.  We used to actually
-            # explicitly delete both "exception" and "exc_info" from ``attrs``
-            # in a ``finally:`` clause below, but now we do not because these
-            # attributes are useful to upstream tweens.  This actually still
-            # apparently causes a reference cycle, but it is broken
-            # successfully by the garbage collector (see
-            # https://github.com/Pylons/pyramid/issues/1223).
-            attrs['exc_info'] = sys.exc_info()
-            attrs['exception'] = exc
-            # clear old generated request.response, if any; it may
-            # have been mutated by the view, and its state is not
-            # sane (e.g. caching headers)
-            if 'response' in attrs:
-                del attrs['response']
-            # we use .get instead of .__getitem__ below due to
-            # https://github.com/Pylons/pyramid/issues/700
-            request_iface = attrs.get('request_iface', IRequest)
-            provides = providedBy(exc)
-            try:
-                response = _call_view(
-                    registry,
-                    request,
-                    exc,
-                    provides,
-                    '',
-                    view_classifier=IExceptionViewClassifier,
-                    request_iface=request_iface.combined
-                    )
-
-            # if views matched but did not pass predicates, squash the error
-            # and re-raise the original exception
-            except PredicateMismatch:
-                response = None
-
-            # re-raise the original exception as no exception views were
-            # able to handle the error
-            if response is None:
-                reraise(*attrs['exc_info'])
-
+            response = _error_handler(request, exc)
         return response
 
     return excview_tween

{"golden_diff": "diff --git a/pyramid/tweens.py b/pyramid/tweens.py\n--- a/pyramid/tweens.py\n+++ b/pyramid/tweens.py\n@@ -10,6 +10,50 @@\n from zope.interface import providedBy\n from pyramid.view import _call_view\n \n+def _error_handler(request, exc):\n+    # NOTE: we do not need to delete exc_info because this function\n+    # should never be in the call stack of the exception\n+    exc_info = sys.exc_info()\n+\n+    attrs = request.__dict__\n+    attrs['exc_info'] = exc_info\n+    attrs['exception'] = exc\n+    # clear old generated request.response, if any; it may\n+    # have been mutated by the view, and its state is not\n+    # sane (e.g. caching headers)\n+    if 'response' in attrs:\n+        del attrs['response']\n+    # we use .get instead of .__getitem__ below due to\n+    # https://github.com/Pylons/pyramid/issues/700\n+    request_iface = attrs.get('request_iface', IRequest)\n+    provides = providedBy(exc)\n+    try:\n+        response = _call_view(\n+            request.registry,\n+            request,\n+            exc,\n+            provides,\n+            '',\n+            view_classifier=IExceptionViewClassifier,\n+            request_iface=request_iface.combined\n+            )\n+\n+    # if views matched but did not pass predicates then treat the\n+    # same as not finding any matching views\n+    except PredicateMismatch:\n+        response = None\n+\n+    # re-raise the original exception as no exception views were\n+    # able to handle the error\n+    if response is None:\n+        if 'exception' in attrs:\n+            del attrs['exception']\n+        if 'exc_info' in attrs:\n+            del attrs['exc_info']\n+        reraise(*exc_info)\n+\n+    return response\n+\n def excview_tween_factory(handler, registry):\n     \"\"\" A :term:`tween` factory which produces a tween that catches an\n     exception raised by downstream tweens (or the main Pyramid request\n@@ -17,50 +61,10 @@\n     :term:`exception view`.\"\"\"\n \n     def excview_tween(request):\n-        attrs = request.__dict__\n         try:\n             response = handler(request)\n         except Exception as exc:\n-            # WARNING: do not assign the result of sys.exc_info() to a local\n-            # var here, doing so will cause a leak.  We used to actually\n-            # explicitly delete both \"exception\" and \"exc_info\" from ``attrs``\n-            # in a ``finally:`` clause below, but now we do not because these\n-            # attributes are useful to upstream tweens.  This actually still\n-            # apparently causes a reference cycle, but it is broken\n-            # successfully by the garbage collector (see\n-            # https://github.com/Pylons/pyramid/issues/1223).\n-            attrs['exc_info'] = sys.exc_info()\n-            attrs['exception'] = exc\n-            # clear old generated request.response, if any; it may\n-            # have been mutated by the view, and its state is not\n-            # sane (e.g. caching headers)\n-            if 'response' in attrs:\n-                del attrs['response']\n-            # we use .get instead of .__getitem__ below due to\n-            # https://github.com/Pylons/pyramid/issues/700\n-            request_iface = attrs.get('request_iface', IRequest)\n-            provides = providedBy(exc)\n-            try:\n-                response = _call_view(\n-                    registry,\n-                    request,\n-                    exc,\n-                    provides,\n-                    '',\n-                    view_classifier=IExceptionViewClassifier,\n-                    request_iface=request_iface.combined\n-                    )\n-\n-            # if views matched but did not pass predicates, squash the error\n-            # and re-raise the original exception\n-            except PredicateMismatch:\n-                response = None\n-\n-            # re-raise the original exception as no exception views were\n-            # able to handle the error\n-            if response is None:\n-                reraise(*attrs['exc_info'])\n-\n+            response = _error_handler(request, exc)\n         return response\n \n     return excview_tween\n", "issue": "remove request.exception if the excview tween fails to handle the exception\nPyramid 1.9 makes `request.exception` and `request.exc_info` a little more important as I've moved the pyramid_tm tween over the excview and in general would advocate to move most tweens over the excview. With that in mind it's currently not possible to test `request.exception` to see if the response was rendered in relation to that exception - the excview tween sets the exception even if it failed to squash it (attempted to render an excview and couldn't find one). Ideally the exception would be related to the response that was generated when it was squashed. This would be more explicit if we used `response.exception` to indicate the response is from a squashed exception but I think that's a larger change.\r\n\r\nI'm proposing to remove `request.exception` and `request.exc_info` in the excview tween if it reraises the original exception. This makes introspection `request.exception` more reliable by upstream tweens that want to know what the squashed exception was... Of course any raised exception should be more interesting than the original `request.exception` but if the tween receives a response then they can see if it is a response generated by a squashed exception or if it is a \"normal\" response.\n", "before_files": [{"content": "import sys\n\nfrom pyramid.compat import reraise\nfrom pyramid.exceptions import PredicateMismatch\nfrom pyramid.interfaces import (\n    IExceptionViewClassifier,\n    IRequest,\n    )\n\nfrom zope.interface import providedBy\nfrom pyramid.view import _call_view\n\ndef excview_tween_factory(handler, registry):\n    \"\"\" A :term:`tween` factory which produces a tween that catches an\n    exception raised by downstream tweens (or the main Pyramid request\n    handler) and, if possible, converts it into a Response using an\n    :term:`exception view`.\"\"\"\n\n    def excview_tween(request):\n        attrs = request.__dict__\n        try:\n            response = handler(request)\n        except Exception as exc:\n            # WARNING: do not assign the result of sys.exc_info() to a local\n            # var here, doing so will cause a leak.  We used to actually\n            # explicitly delete both \"exception\" and \"exc_info\" from ``attrs``\n            # in a ``finally:`` clause below, but now we do not because these\n            # attributes are useful to upstream tweens.  This actually still\n            # apparently causes a reference cycle, but it is broken\n            # successfully by the garbage collector (see\n            # https://github.com/Pylons/pyramid/issues/1223).\n            attrs['exc_info'] = sys.exc_info()\n            attrs['exception'] = exc\n            # clear old generated request.response, if any; it may\n            # have been mutated by the view, and its state is not\n            # sane (e.g. caching headers)\n            if 'response' in attrs:\n                del attrs['response']\n            # we use .get instead of .__getitem__ below due to\n            # https://github.com/Pylons/pyramid/issues/700\n            request_iface = attrs.get('request_iface', IRequest)\n            provides = providedBy(exc)\n            try:\n                response = _call_view(\n                    registry,\n                    request,\n                    exc,\n                    provides,\n                    '',\n                    view_classifier=IExceptionViewClassifier,\n                    request_iface=request_iface.combined\n                    )\n\n            # if views matched but did not pass predicates, squash the error\n            # and re-raise the original exception\n            except PredicateMismatch:\n                response = None\n\n            # re-raise the original exception as no exception views were\n            # able to handle the error\n            if response is None:\n                reraise(*attrs['exc_info'])\n\n        return response\n\n    return excview_tween\n\nMAIN = 'MAIN'\nINGRESS = 'INGRESS'\nEXCVIEW = 'pyramid.tweens.excview_tween_factory'\n", "path": "pyramid/tweens.py"}], "after_files": [{"content": "import sys\n\nfrom pyramid.compat import reraise\nfrom pyramid.exceptions import PredicateMismatch\nfrom pyramid.interfaces import (\n    IExceptionViewClassifier,\n    IRequest,\n    )\n\nfrom zope.interface import providedBy\nfrom pyramid.view import _call_view\n\ndef _error_handler(request, exc):\n    # NOTE: we do not need to delete exc_info because this function\n    # should never be in the call stack of the exception\n    exc_info = sys.exc_info()\n\n    attrs = request.__dict__\n    attrs['exc_info'] = exc_info\n    attrs['exception'] = exc\n    # clear old generated request.response, if any; it may\n    # have been mutated by the view, and its state is not\n    # sane (e.g. caching headers)\n    if 'response' in attrs:\n        del attrs['response']\n    # we use .get instead of .__getitem__ below due to\n    # https://github.com/Pylons/pyramid/issues/700\n    request_iface = attrs.get('request_iface', IRequest)\n    provides = providedBy(exc)\n    try:\n        response = _call_view(\n            request.registry,\n            request,\n            exc,\n            provides,\n            '',\n            view_classifier=IExceptionViewClassifier,\n            request_iface=request_iface.combined\n            )\n\n    # if views matched but did not pass predicates then treat the\n    # same as not finding any matching views\n    except PredicateMismatch:\n        response = None\n\n    # re-raise the original exception as no exception views were\n    # able to handle the error\n    if response is None:\n        if 'exception' in attrs:\n            del attrs['exception']\n        if 'exc_info' in attrs:\n            del attrs['exc_info']\n        reraise(*exc_info)\n\n    return response\n\ndef excview_tween_factory(handler, registry):\n    \"\"\" A :term:`tween` factory which produces a tween that catches an\n    exception raised by downstream tweens (or the main Pyramid request\n    handler) and, if possible, converts it into a Response using an\n    :term:`exception view`.\"\"\"\n\n    def excview_tween(request):\n        try:\n            response = handler(request)\n        except Exception as exc:\n            response = _error_handler(request, exc)\n        return response\n\n    return excview_tween\n\nMAIN = 'MAIN'\nINGRESS = 'INGRESS'\nEXCVIEW = 'pyramid.tweens.excview_tween_factory'\n", "path": "pyramid/tweens.py"}]}