From 62e95b89fca3a4c60d6638776bddbe1b59fcd2f9 Mon Sep 17 00:00:00 2001 From: Jamie Hewland Date: Sun, 31 Jan 2021 11:43:07 +0000 Subject: [PATCH] Fix middleware traceback fetching on Python 3.8+, fix ResourceWarnings in TestClient, fix CI build (#1132) * Add __init__ file for tests.middleware so Mypy 0.800 is happy * testclient: Tie loop lifetime to thread * ServerErrorMiddleware: Don't use undocumented TracebackException.exc_traceback attribute --- starlette/middleware/errors.py | 12 ++++++------ starlette/testclient.py | 6 ++++-- tests/middleware/__init__.py | 0 3 files changed, 10 insertions(+), 8 deletions(-) create mode 100644 tests/middleware/__init__.py diff --git a/starlette/middleware/errors.py b/starlette/middleware/errors.py index f3de1629..4217185e 100644 --- a/starlette/middleware/errors.py +++ b/starlette/middleware/errors.py @@ -217,15 +217,15 @@ class ServerErrorMiddleware: traceback_obj = traceback.TracebackException.from_exception( exc, capture_locals=True ) - frames = inspect.getinnerframes( - traceback_obj.exc_traceback, limit # type: ignore - ) exc_html = "" is_collapsed = False - for frame in reversed(frames): - exc_html += self.generate_frame_html(frame, is_collapsed) - is_collapsed = True + exc_traceback = exc.__traceback__ + if exc_traceback is not None: + frames = inspect.getinnerframes(exc_traceback, limit) + for frame in reversed(frames): + exc_html += self.generate_frame_html(frame, is_collapsed) + is_collapsed = True # escape error class and text error = ( diff --git a/starlette/testclient.py b/starlette/testclient.py index c17c5181..1d5e90dc 100644 --- a/starlette/testclient.py +++ b/starlette/testclient.py @@ -268,7 +268,6 @@ class WebSocketTestSession: self.app = app self.scope = scope self.accepted_subprotocol = None - self._loop = asyncio.new_event_loop() self._receive_queue = queue.Queue() # type: queue.Queue self._send_queue = queue.Queue() # type: queue.Queue self._thread = threading.Thread(target=self._run) @@ -293,13 +292,16 @@ class WebSocketTestSession: """ The sub-thread in which the websocket session runs. """ + loop = asyncio.new_event_loop() scope = self.scope receive = self._asgi_receive send = self._asgi_send try: - self._loop.run_until_complete(self.app(scope, receive, send)) + loop.run_until_complete(self.app(scope, receive, send)) except BaseException as exc: self._send_queue.put(exc) + finally: + loop.close() async def _asgi_receive(self) -> Message: while self._receive_queue.empty(): diff --git a/tests/middleware/__init__.py b/tests/middleware/__init__.py new file mode 100644 index 00000000..e69de29b