Skip to content

Commit d5c72fa

Browse files
authored
Merge branch 'master' into ivana/debug-logger-parent-handlers
2 parents 5553692 + 4f51ff3 commit d5c72fa

File tree

6 files changed

+118
-12
lines changed

6 files changed

+118
-12
lines changed

requirements-testing.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ socksio
1414
httpcore[http2]
1515
setuptools
1616
Brotli
17-
docker
17+
docker

scripts/populate_tox/tox.jinja

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,7 @@ deps =
384384
# Quart
385385
quart: quart-auth
386386
quart: pytest-asyncio
387+
quart-{v0.19,latest}: quart-flask-patch
387388
quart-v0.16: blinker<1.6
388389
quart-v0.16: jinja2<3.1.0
389390
quart-v0.16: Werkzeug<2.1.0

sentry_sdk/integrations/flask.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,18 @@ def __init__(
7272
@staticmethod
7373
def setup_once():
7474
# type: () -> None
75+
try:
76+
from quart import Quart # type: ignore
77+
78+
if Flask == Quart:
79+
# This is Quart masquerading as Flask, don't enable the Flask
80+
# integration. See https://github.com/getsentry/sentry-python/issues/2709
81+
raise DidNotEnable(
82+
"This is not a Flask app but rather Quart pretending to be Flask"
83+
)
84+
except ImportError:
85+
pass
86+
7587
version = package_version("flask")
7688
_check_minimum_version(FlaskIntegration, version)
7789

tests/integrations/arq/test_arq.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import asyncio
2+
from datetime import timedelta
3+
24
import pytest
35

46
from sentry_sdk import get_client, start_transaction
@@ -376,3 +378,48 @@ async def job(ctx):
376378
assert event["contexts"]["trace"]["origin"] == "auto.queue.arq"
377379
assert event["spans"][0]["origin"] == "auto.db.redis"
378380
assert event["spans"][1]["origin"] == "auto.db.redis"
381+
382+
383+
@pytest.mark.asyncio
384+
async def test_job_concurrency(capture_events, init_arq):
385+
"""
386+
10 - division starts
387+
70 - sleepy starts
388+
110 - division raises error
389+
120 - sleepy finishes
390+
391+
"""
392+
393+
async def sleepy(_):
394+
await asyncio.sleep(0.05)
395+
396+
async def division(_):
397+
await asyncio.sleep(0.1)
398+
return 1 / 0
399+
400+
sleepy.__qualname__ = sleepy.__name__
401+
division.__qualname__ = division.__name__
402+
403+
pool, worker = init_arq([sleepy, division])
404+
405+
events = capture_events()
406+
407+
await pool.enqueue_job(
408+
"division", _job_id="123", _defer_by=timedelta(milliseconds=10)
409+
)
410+
await pool.enqueue_job(
411+
"sleepy", _job_id="456", _defer_by=timedelta(milliseconds=70)
412+
)
413+
414+
loop = asyncio.get_event_loop()
415+
task = loop.create_task(worker.async_run())
416+
await asyncio.sleep(1)
417+
418+
task.cancel()
419+
420+
await worker.close()
421+
422+
exception_event = events[1]
423+
assert exception_event["exception"]["values"][0]["type"] == "ZeroDivisionError"
424+
assert exception_event["transaction"] == "division"
425+
assert exception_event["extra"]["arq-job"]["task"] == "division"

tests/integrations/quart/test_quart.py

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import importlib
12
import json
23
import threading
34
from unittest import mock
@@ -13,22 +14,22 @@
1314
from sentry_sdk.integrations.logging import LoggingIntegration
1415
import sentry_sdk.integrations.quart as quart_sentry
1516

16-
from quart import Quart, Response, abort, stream_with_context
17-
from quart.views import View
1817

19-
from quart_auth import AuthUser, login_user
20-
21-
try:
22-
from quart_auth import QuartAuth
18+
def quart_app_factory():
19+
# These imports are inlined because the `test_quart_flask_patch` testcase
20+
# tests behavior that is triggered by importing a package before any Quart
21+
# imports happen, so we can't have these on the module level
22+
from quart import Quart
2323

24-
auth_manager = QuartAuth()
25-
except ImportError:
26-
from quart_auth import AuthManager
24+
try:
25+
from quart_auth import QuartAuth
2726

28-
auth_manager = AuthManager()
27+
auth_manager = QuartAuth()
28+
except ImportError:
29+
from quart_auth import AuthManager
2930

31+
auth_manager = AuthManager()
3032

31-
def quart_app_factory():
3233
app = Quart(__name__)
3334
app.debug = False
3435
app.config["TESTING"] = False
@@ -71,6 +72,42 @@ def integration_enabled_params(request):
7172
raise ValueError(request.param)
7273

7374

75+
@pytest.mark.asyncio
76+
@pytest.mark.forked
77+
@pytest.mark.skipif(
78+
not importlib.util.find_spec("quart_flask_patch"),
79+
reason="requires quart_flask_patch",
80+
)
81+
async def test_quart_flask_patch(sentry_init, capture_events, reset_integrations):
82+
# This testcase is forked because `import quart_flask_patch` needs to run
83+
# before anything else Quart-related is imported (since it monkeypatches
84+
# some things) and we don't want this to affect other testcases.
85+
#
86+
# It's also important this testcase be run before any other testcase
87+
# that uses `quart_app_factory`.
88+
import quart_flask_patch # noqa: F401
89+
90+
app = quart_app_factory()
91+
sentry_init(
92+
integrations=[quart_sentry.QuartIntegration()],
93+
)
94+
95+
@app.route("/")
96+
async def index():
97+
1 / 0
98+
99+
events = capture_events()
100+
101+
client = app.test_client()
102+
try:
103+
await client.get("/")
104+
except ZeroDivisionError:
105+
pass
106+
107+
(event,) = events
108+
assert event["exception"]["values"][0]["mechanism"]["type"] == "quart"
109+
110+
74111
@pytest.mark.asyncio
75112
async def test_has_context(sentry_init, capture_events):
76113
sentry_init(integrations=[quart_sentry.QuartIntegration()])
@@ -213,6 +250,8 @@ async def test_quart_auth_configured(
213250
monkeypatch,
214251
integration_enabled_params,
215252
):
253+
from quart_auth import AuthUser, login_user
254+
216255
sentry_init(send_default_pii=send_default_pii, **integration_enabled_params)
217256
app = quart_app_factory()
218257

@@ -368,6 +407,8 @@ async def error_handler(err):
368407

369408
@pytest.mark.asyncio
370409
async def test_bad_request_not_captured(sentry_init, capture_events):
410+
from quart import abort
411+
371412
sentry_init(integrations=[quart_sentry.QuartIntegration()])
372413
app = quart_app_factory()
373414
events = capture_events()
@@ -385,6 +426,8 @@ async def index():
385426

386427
@pytest.mark.asyncio
387428
async def test_does_not_leak_scope(sentry_init, capture_events):
429+
from quart import Response, stream_with_context
430+
388431
sentry_init(integrations=[quart_sentry.QuartIntegration()])
389432
app = quart_app_factory()
390433
events = capture_events()
@@ -514,6 +557,8 @@ async def error():
514557

515558
@pytest.mark.asyncio
516559
async def test_class_based_views(sentry_init, capture_events):
560+
from quart.views import View
561+
517562
sentry_init(integrations=[quart_sentry.QuartIntegration()])
518563
app = quart_app_factory()
519564
events = capture_events()

tox.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,7 @@ deps =
501501
# Quart
502502
quart: quart-auth
503503
quart: pytest-asyncio
504+
quart-{v0.19,latest}: quart-flask-patch
504505
quart-v0.16: blinker<1.6
505506
quart-v0.16: jinja2<3.1.0
506507
quart-v0.16: Werkzeug<2.1.0

0 commit comments

Comments
 (0)