From 5d3a9b77cc3eab42ae97d93888cfc05a03c14f75 Mon Sep 17 00:00:00 2001 From: Ivana Kellyerova Date: Fri, 26 Apr 2024 13:03:27 +0200 Subject: [PATCH 1/9] feat(tests): Add a Starlette testcase that uses Uvicorn --- tests/conftest.py | 34 +++++++++++++++++++ .../integrations/starlette/test_starlette.py | 22 ++++++++++++ tox.ini | 3 +- 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 118408cfc3..8cda0d249a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,8 @@ +import contextlib import json import os import socket +import time from threading import Thread from contextlib import contextmanager from http.server import BaseHTTPRequestHandler, HTTPServer @@ -19,6 +21,11 @@ except ImportError: eventlet = None +try: + import uvicorn +except ImportError: + uvicorn = None + import sentry_sdk from sentry_sdk.envelope import Envelope from sentry_sdk.integrations import _processed_integrations # noqa: F401 @@ -614,3 +621,30 @@ def __eq__(self, other): def __ne__(self, other): return not self.__eq__(other) + + +class UvicornServer(uvicorn.Server): + @contextlib.contextmanager + def run_in_thread(self): + thread = Thread(target=self.run) + thread.start() + try: + while not self.started: + time.sleep(1e-3) + yield + finally: + self.should_exit = True + thread.join() + + +@pytest.fixture(scope="session") +def uvicorn_server(request): + app = request.param() + + config = uvicorn.Config( + app, host="127.0.0.1", port=5000, log_level="info", loop="asyncio" + ) + server = UvicornServer(config=config) + + with server.run_in_thread(): + yield diff --git a/tests/integrations/starlette/test_starlette.py b/tests/integrations/starlette/test_starlette.py index e1f3c1a482..8294d80ea1 100644 --- a/tests/integrations/starlette/test_starlette.py +++ b/tests/integrations/starlette/test_starlette.py @@ -9,6 +9,7 @@ from unittest import mock import pytest +import requests from sentry_sdk import capture_message, get_baggage, get_traceparent from sentry_sdk.integrations.asgi import SentryAsgiMiddleware @@ -100,6 +101,9 @@ def starlette_app_factory(middleware=None, debug=True): ) templates = Jinja2Templates(directory=template_dir) + async def _ok(request): + return starlette.responses.JSONResponse({"status": "ok"}) + async def _homepage(request): 1 / 0 return starlette.responses.JSONResponse({"status": "ok"}) @@ -143,6 +147,7 @@ async def _render_template(request): app = starlette.applications.Starlette( debug=debug, routes=[ + starlette.routing.Route("/ok", _ok), starlette.routing.Route("/some_url", _homepage), starlette.routing.Route("/custom_error", _custom_error), starlette.routing.Route("/message", _message), @@ -1078,3 +1083,20 @@ def test_transaction_name_in_middleware( assert ( transaction_event["transaction_info"]["source"] == expected_transaction_source ) + + +@pytest.mark.parametrize("uvicorn_server", [starlette_app_factory], indirect=True) +def test_with_uvicorn(sentry_init, capture_envelopes, uvicorn_server): + # Sanity check that the app works with uvicorn which does its own ASGI 2/3 + # discovery. If we wrap the ASGI app incorrectly, everything might seem ok + # until you try to run the app with uvicorn. + + sentry_init( + integrations=[StarletteIntegration()], + ) + + envelopes = capture_envelopes() + + requests.get("http://127.0.0.1:5000/ok") + + assert not envelopes diff --git a/tox.ini b/tox.ini index e193de52b1..d5ed5f7d15 100644 --- a/tox.ini +++ b/tox.ini @@ -529,15 +529,16 @@ deps = starlette: pytest-asyncio<=0.21.1 starlette: python-multipart starlette: requests + starlette: uvicorn starlette: httpx # (this is a dependency of httpx) starlette: anyio<4.0.0 starlette: jinja2 starlette-v0.19: starlette~=0.19.0 - starlette-v0.20: starlette~=0.20.0 starlette-v0.24: starlette~=0.24.0 starlette-v0.28: starlette~=0.28.0 starlette-v0.32: starlette~=0.32.0 + starlette-v0.36: starlette~=0.36.0 starlette-latest: starlette # Starlite From 5796f8581d1b2d8debacae0c43f7b7335c4df7b4 Mon Sep 17 00:00:00 2001 From: Ivana Kellyerova Date: Fri, 26 Apr 2024 13:13:19 +0200 Subject: [PATCH 2/9] guard against uvicorn not being there --- tests/conftest.py | 49 ++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 8cda0d249a..73a019d7bd 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -623,28 +623,29 @@ def __ne__(self, other): return not self.__eq__(other) -class UvicornServer(uvicorn.Server): - @contextlib.contextmanager - def run_in_thread(self): - thread = Thread(target=self.run) - thread.start() - try: - while not self.started: - time.sleep(1e-3) - yield - finally: - self.should_exit = True - thread.join() - - -@pytest.fixture(scope="session") -def uvicorn_server(request): - app = request.param() - - config = uvicorn.Config( - app, host="127.0.0.1", port=5000, log_level="info", loop="asyncio" - ) - server = UvicornServer(config=config) +if uvicorn is not None: + + class UvicornServer(uvicorn.Server): + @contextlib.contextmanager + def run_in_thread(self): + thread = Thread(target=self.run) + thread.start() + try: + while not self.started: + time.sleep(1e-3) + yield + finally: + self.should_exit = True + thread.join() + + @pytest.fixture(scope="session") + def uvicorn_server(request): + app = request.param() + + config = uvicorn.Config( + app, host="127.0.0.1", port=5000, log_level="info", loop="asyncio" + ) + server = UvicornServer(config=config) - with server.run_in_thread(): - yield + with server.run_in_thread(): + yield From 9826f6ceecbb3aaab4353e8d4bae0b54eda7c027 Mon Sep 17 00:00:00 2001 From: Ivana Kellyerova Date: Fri, 26 Apr 2024 13:16:23 +0200 Subject: [PATCH 3/9] properly update starlette versions in tox --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index d5ed5f7d15..09a0dc765e 100644 --- a/tox.ini +++ b/tox.ini @@ -207,8 +207,8 @@ envlist = # Starlette {py3.7,py3.10}-starlette-v{0.19} - {py3.7,py3.11}-starlette-v{0.20,0.24,0.28} - {py3.8,py3.11,py3.12}-starlette-v{0.32} + {py3.7,py3.11}-starlette-v{0.24,0.28} + {py3.8,py3.11,py3.12}-starlette-v{0.32,0.36} {py3.8,py3.11,py3.12}-starlette-latest # Starlite From 1374dcf1645bcd476cc2f877a38ebf674397a926 Mon Sep 17 00:00:00 2001 From: Ivana Kellyerova Date: Fri, 26 Apr 2024 14:05:17 +0200 Subject: [PATCH 4/9] try to run the test forked --- tests/integrations/starlette/test_starlette.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integrations/starlette/test_starlette.py b/tests/integrations/starlette/test_starlette.py index 8294d80ea1..16da1876d7 100644 --- a/tests/integrations/starlette/test_starlette.py +++ b/tests/integrations/starlette/test_starlette.py @@ -1085,6 +1085,7 @@ def test_transaction_name_in_middleware( ) +@pytest.mark.forked @pytest.mark.parametrize("uvicorn_server", [starlette_app_factory], indirect=True) def test_with_uvicorn(sentry_init, capture_envelopes, uvicorn_server): # Sanity check that the app works with uvicorn which does its own ASGI 2/3 From eb614ed26c9a527c82dc21da87bffff479701244 Mon Sep 17 00:00:00 2001 From: Ivana Kellyerova Date: Fri, 26 Apr 2024 14:29:05 +0200 Subject: [PATCH 5/9] attempt --- tests/conftest.py | 4 +--- tests/integrations/starlette/test_starlette.py | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 73a019d7bd..8fe931c2dd 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -642,9 +642,7 @@ def run_in_thread(self): def uvicorn_server(request): app = request.param() - config = uvicorn.Config( - app, host="127.0.0.1", port=5000, log_level="info", loop="asyncio" - ) + config = uvicorn.Config(app, host="127.0.0.1", port=5000, log_level="info") server = UvicornServer(config=config) with server.run_in_thread(): diff --git a/tests/integrations/starlette/test_starlette.py b/tests/integrations/starlette/test_starlette.py index 16da1876d7..8294d80ea1 100644 --- a/tests/integrations/starlette/test_starlette.py +++ b/tests/integrations/starlette/test_starlette.py @@ -1085,7 +1085,6 @@ def test_transaction_name_in_middleware( ) -@pytest.mark.forked @pytest.mark.parametrize("uvicorn_server", [starlette_app_factory], indirect=True) def test_with_uvicorn(sentry_init, capture_envelopes, uvicorn_server): # Sanity check that the app works with uvicorn which does its own ASGI 2/3 From e6f16bea62290ffd09461123f659cbce74562564 Mon Sep 17 00:00:00 2001 From: Ivana Kellyerova Date: Fri, 26 Apr 2024 14:46:09 +0200 Subject: [PATCH 6/9] ? --- tests/conftest.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 8fe931c2dd..0bb974b00f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -642,7 +642,9 @@ def run_in_thread(self): def uvicorn_server(request): app = request.param() - config = uvicorn.Config(app, host="127.0.0.1", port=5000, log_level="info") + config = uvicorn.Config( + app, host="127.0.0.1", port=5000, log_level="info", loop=None + ) server = UvicornServer(config=config) with server.run_in_thread(): From 560f8f6f6d9ce6bd03e10c074adda04e130f45aa Mon Sep 17 00:00:00 2001 From: Ivana Kellyerova Date: Fri, 26 Apr 2024 14:46:46 +0200 Subject: [PATCH 7/9] ? --- tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 0bb974b00f..e95163b1bb 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -643,7 +643,7 @@ def uvicorn_server(request): app = request.param() config = uvicorn.Config( - app, host="127.0.0.1", port=5000, log_level="info", loop=None + app, host="127.0.0.1", port=5000, log_level="info", loop="none" ) server = UvicornServer(config=config) From c1cf43a8021c0668b6ca852c54d8c7e1a4c92bf5 Mon Sep 17 00:00:00 2001 From: Ivana Kellyerova Date: Fri, 26 Apr 2024 14:57:04 +0200 Subject: [PATCH 8/9] trying something --- scripts/runtox.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/runtox.sh b/scripts/runtox.sh index 50da44dd53..3e4f749073 100755 --- a/scripts/runtox.sh +++ b/scripts/runtox.sh @@ -40,4 +40,4 @@ if [ -z "${ENV}" ]; then exit 0 fi -exec $TOXPATH -e "$ENV" -- "${@:2}" +exec $TOXPATH -po -e "$ENV" -- "${@:2}" From e15edf541b842a0c5bc97955d4ec002e672acbb9 Mon Sep 17 00:00:00 2001 From: Ivana Kellyerova Date: Fri, 26 Apr 2024 14:59:47 +0200 Subject: [PATCH 9/9] . --- scripts/runtox.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/runtox.sh b/scripts/runtox.sh index 3e4f749073..756a47927d 100755 --- a/scripts/runtox.sh +++ b/scripts/runtox.sh @@ -40,4 +40,4 @@ if [ -z "${ENV}" ]; then exit 0 fi -exec $TOXPATH -po -e "$ENV" -- "${@:2}" +exec $TOXPATH -p all -o -e "$ENV" -- "${@:2}"