Skip to content

Commit

Permalink
Bug fixes for FastAPI and Sentry SDK 1.10.0 (#1699)
Browse files Browse the repository at this point in the history
Make sure receive/send callbacks can also be functools.partial objects (or other objects that are not having a __name__)

Refs #1697
  • Loading branch information
antonpirker committed Oct 21, 2022
1 parent fdb7512 commit 2c0ff93
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 6 deletions.
11 changes: 7 additions & 4 deletions sentry_sdk/integrations/starlette.py
Expand Up @@ -103,25 +103,28 @@ async def _sentry_receive(*args, **kwargs):
hub = Hub.current
with hub.start_span(
op=OP.MIDDLEWARE_STARLETTE_RECEIVE,
description=receive.__qualname__,
description=getattr(receive, "__qualname__", str(receive)),
) as span:
span.set_tag("starlette.middleware_name", middleware_name)
return await receive(*args, **kwargs)

receive_patched = receive.__name__ == "_sentry_receive"
receive_name = getattr(receive, "__name__", str(receive))
receive_patched = receive_name == "_sentry_receive"
new_receive = _sentry_receive if not receive_patched else receive

# Creating spans for the "send" callback
async def _sentry_send(*args, **kwargs):
# type: (*Any, **Any) -> Any
hub = Hub.current
with hub.start_span(
op=OP.MIDDLEWARE_STARLETTE_SEND, description=send.__qualname__
op=OP.MIDDLEWARE_STARLETTE_SEND,
description=getattr(send, "__qualname__", str(send)),
) as span:
span.set_tag("starlette.middleware_name", middleware_name)
return await send(*args, **kwargs)

send_patched = send.__name__ == "_sentry_send"
send_name = getattr(send, "__name__", str(send))
send_patched = send_name == "_sentry_send"
new_send = _sentry_send if not send_patched else send

return await old_call(app, scope, new_receive, new_send, **kwargs)
Expand Down
101 changes: 99 additions & 2 deletions tests/integrations/starlette/test_starlette.py
@@ -1,5 +1,6 @@
import asyncio
import base64
import functools
import json
import os

Expand Down Expand Up @@ -189,6 +190,30 @@ async def __call__(self, scope, receive, send):
await self.app(scope, receive, send)


class SamplePartialReceiveSendMiddleware:
def __init__(self, app):
self.app = app

async def __call__(self, scope, receive, send):
message = await receive()
assert message
assert message["type"] == "http.request"

send_output = await send({"type": "something-unimportant"})
assert send_output is None

async def my_receive(*args, **kwargs):
pass

async def my_send(*args, **kwargs):
pass

partial_receive = functools.partial(my_receive)
partial_send = functools.partial(my_send)

await self.app(scope, partial_receive, partial_send)


@pytest.mark.asyncio
async def test_starlettrequestextractor_content_length(sentry_init):
with mock.patch(
Expand Down Expand Up @@ -659,8 +684,7 @@ def test_middleware_callback_spans(sentry_init, capture_events):
idx += 1


@pytest.mark.asyncio
async def test_middleware_receive_send(sentry_init, capture_events):
def test_middleware_receive_send(sentry_init, capture_events):
sentry_init(
traces_sample_rate=1.0,
integrations=[StarletteIntegration()],
Expand All @@ -678,6 +702,79 @@ async def test_middleware_receive_send(sentry_init, capture_events):
pass


def test_middleware_partial_receive_send(sentry_init, capture_events):
sentry_init(
traces_sample_rate=1.0,
integrations=[StarletteIntegration()],
)
starlette_app = starlette_app_factory(
middleware=[Middleware(SamplePartialReceiveSendMiddleware)]
)
events = capture_events()

client = TestClient(starlette_app, raise_server_exceptions=False)
try:
client.get("/message", auth=("Gabriela", "hello123"))
except Exception:
pass

(_, transaction_event) = events

expected = [
{
"op": "middleware.starlette",
"description": "ServerErrorMiddleware",
"tags": {"starlette.middleware_name": "ServerErrorMiddleware"},
},
{
"op": "middleware.starlette",
"description": "SamplePartialReceiveSendMiddleware",
"tags": {"starlette.middleware_name": "SamplePartialReceiveSendMiddleware"},
},
{
"op": "middleware.starlette.receive",
"description": "_ASGIAdapter.send.<locals>.receive"
if STARLETTE_VERSION < (0, 21)
else "_TestClientTransport.handle_request.<locals>.receive",
"tags": {"starlette.middleware_name": "ServerErrorMiddleware"},
},
{
"op": "middleware.starlette.send",
"description": "ServerErrorMiddleware.__call__.<locals>._send",
"tags": {"starlette.middleware_name": "SamplePartialReceiveSendMiddleware"},
},
{
"op": "middleware.starlette.send",
"description": "_ASGIAdapter.send.<locals>.send"
if STARLETTE_VERSION < (0, 21)
else "_TestClientTransport.handle_request.<locals>.send",
"tags": {"starlette.middleware_name": "ServerErrorMiddleware"},
},
{
"op": "middleware.starlette",
"description": "ExceptionMiddleware",
"tags": {"starlette.middleware_name": "ExceptionMiddleware"},
},
{
"op": "middleware.starlette.send",
"description": "functools.partial(<function SamplePartialReceiveSendMiddleware.__call__.<locals>.my_send at ",
"tags": {"starlette.middleware_name": "ExceptionMiddleware"},
},
{
"op": "middleware.starlette.send",
"description": "functools.partial(<function SamplePartialReceiveSendMiddleware.__call__.<locals>.my_send at ",
"tags": {"starlette.middleware_name": "ExceptionMiddleware"},
},
]

idx = 0
for span in transaction_event["spans"]:
assert span["op"] == expected[idx]["op"]
assert span["description"].startswith(expected[idx]["description"])
assert span["tags"] == expected[idx]["tags"]
idx += 1


def test_last_event_id(sentry_init, capture_events):
sentry_init(
integrations=[StarletteIntegration()],
Expand Down

0 comments on commit 2c0ff93

Please sign in to comment.