Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
a5ee095
Added falcon 3.x to test suite
antonpirker Nov 10, 2022
b6d8131
Updated test matrix for Falcon
antonpirker Nov 10, 2022
e4f7feb
The Falcon integration for Falcon 3.0.1 (#1297)
bartolootrit Nov 10, 2022
979b8c5
Update test matrix
antonpirker Nov 10, 2022
76d511c
Made test matrix according to PyPi supported versions
antonpirker Nov 10, 2022
48b6ff5
Updated test config
antonpirker Nov 10, 2022
462faf5
Merge branch 'master' into antonpirker/falcon3
antonpirker Nov 10, 2022
1787555
Merge branch 'master' into antonpirker/falcon3
antonpirker Nov 24, 2022
96b6d14
Merge branch 'master' into antonpirker/falcon3
antonpirker Jan 25, 2023
2f750b9
Updated test script
antonpirker Jan 25, 2023
2b32178
Merge branch 'master' into antonpirker/falcon3
antonpirker Jan 25, 2023
d53c8a6
Merge branch 'master' into antonpirker/falcon3
antonpirker Jan 30, 2023
caa1fbf
Merge branch 'master' into antonpirker/falcon3
antonpirker Feb 6, 2023
7392877
Merge branch 'master' into antonpirker/falcon3
antonpirker Feb 17, 2023
4ca9ef2
Merge branch 'master' into antonpirker/falcon3
antonpirker Feb 17, 2023
1765639
Mocking http requests instead of doing real ones.
antonpirker Feb 17, 2023
6418e1f
Removed obsolete comment
antonpirker Feb 17, 2023
8469785
Removed usage of httpbin.org and where possible replaced by a mock. (…
antonpirker Feb 17, 2023
f4338f3
Falcon 3 integration code coverage (#1890)
bartolootrit Feb 17, 2023
f051141
Merge branch 'antonpirker/fix-httpx-tests' into antonpirker/falcon3
antonpirker Feb 17, 2023
e3f14a8
Merge branch 'antonpirker/falcon3' of github.com:getsentry/sentry-pyt…
antonpirker Feb 17, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test-integration-falcon.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["2.7","3.5","3.6","3.7","3.8","3.9","3.10","3.11"]
python-version: ["2.7","3.5","3.6","3.7","3.8","3.9"]
# python3.6 reached EOL and is no longer being supported on
# new versions of hosted runners on Github Actions
# ubuntu-20.04 is the last version that supported python3.6
Expand Down
60 changes: 43 additions & 17 deletions sentry_sdk/integrations/falcon.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,29 @@

from sentry_sdk._types import EventProcessor

# In Falcon 3.0 `falcon.api_helpers` is renamed to `falcon.app_helpers`
# and `falcon.API` to `falcon.App`

try:
import falcon # type: ignore
import falcon.api_helpers # type: ignore

from falcon import __version__ as FALCON_VERSION
except ImportError:
raise DidNotEnable("Falcon not installed")

try:
import falcon.app_helpers # type: ignore

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be preferable to attempt to import falcon.app_helpers first, then import falcon.api_helpers if the first fails.
Otherwise, the current code will keep complaining with deprecation warnings that api_helpers has been renamed.


falcon_helpers = falcon.app_helpers
falcon_app_class = falcon.App
FALCON3 = True
except ImportError:
import falcon.api_helpers # type: ignore

falcon_helpers = falcon.api_helpers
falcon_app_class = falcon.API
FALCON3 = False


class FalconRequestExtractor(RequestExtractor):
def env(self):
Expand Down Expand Up @@ -58,16 +73,27 @@ def raw_data(self):
else:
return None

def json(self):
# type: () -> Optional[Dict[str, Any]]
try:
return self.request.media
except falcon.errors.HTTPBadRequest:
# NOTE(jmagnusson): We return `falcon.Request._media` here because
# falcon 1.4 doesn't do proper type checking in
# `falcon.Request.media`. This has been fixed in 2.0.
# Relevant code: https://github.com/falconry/falcon/blob/1.4.1/falcon/request.py#L953
return self.request._media
if FALCON3:

def json(self):
# type: () -> Optional[Dict[str, Any]]
try:
return self.request.media
except falcon.errors.HTTPBadRequest:
return None

else:

def json(self):
# type: () -> Optional[Dict[str, Any]]
try:
return self.request.media
except falcon.errors.HTTPBadRequest:
# NOTE(jmagnusson): We return `falcon.Request._media` here because
# falcon 1.4 doesn't do proper type checking in
# `falcon.Request.media`. This has been fixed in 2.0.
# Relevant code: https://github.com/falconry/falcon/blob/1.4.1/falcon/request.py#L953
return self.request._media


class SentryFalconMiddleware(object):
Expand Down Expand Up @@ -120,7 +146,7 @@ def setup_once():

def _patch_wsgi_app():
# type: () -> None
original_wsgi_app = falcon.API.__call__
original_wsgi_app = falcon_app_class.__call__

def sentry_patched_wsgi_app(self, env, start_response):
# type: (falcon.API, Any, Any) -> Any
Expand All @@ -135,12 +161,12 @@ def sentry_patched_wsgi_app(self, env, start_response):

return sentry_wrapped(env, start_response)

falcon.API.__call__ = sentry_patched_wsgi_app
falcon_app_class.__call__ = sentry_patched_wsgi_app


def _patch_handle_exception():
# type: () -> None
original_handle_exception = falcon.API._handle_exception
original_handle_exception = falcon_app_class._handle_exception

def sentry_patched_handle_exception(self, *args):
# type: (falcon.API, *Any) -> Any
Expand Down Expand Up @@ -170,12 +196,12 @@ def sentry_patched_handle_exception(self, *args):

return was_handled

falcon.API._handle_exception = sentry_patched_handle_exception
falcon_app_class._handle_exception = sentry_patched_handle_exception


def _patch_prepare_middleware():
# type: () -> None
original_prepare_middleware = falcon.api_helpers.prepare_middleware
original_prepare_middleware = falcon_helpers.prepare_middleware

def sentry_patched_prepare_middleware(
middleware=None, independent_middleware=False
Expand All @@ -187,7 +213,7 @@ def sentry_patched_prepare_middleware(
middleware = [SentryFalconMiddleware()] + (middleware or [])
return original_prepare_middleware(middleware, independent_middleware)

falcon.api_helpers.prepare_middleware = sentry_patched_prepare_middleware
falcon_helpers.prepare_middleware = sentry_patched_prepare_middleware


def _exception_leads_to_http_5xx(ex):
Expand Down
1 change: 1 addition & 0 deletions test-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ jsonschema==3.2.0
pyrsistent==0.16.0 # TODO(py3): 0.17.0 requires python3, see https://github.com/tobgu/pyrsistent/issues/205
executing
asttokens
responses
ipdb
121 changes: 68 additions & 53 deletions tests/integrations/httpx/test_httpx.py
Original file line number Diff line number Diff line change
@@ -1,68 +1,83 @@
import asyncio

import pytest
import httpx
import responses

from sentry_sdk import capture_message, start_transaction
from sentry_sdk.integrations.httpx import HttpxIntegration


def test_crumb_capture_and_hint(sentry_init, capture_events):
@pytest.mark.parametrize(
"httpx_client",
(httpx.Client(), httpx.AsyncClient()),
)
def test_crumb_capture_and_hint(sentry_init, capture_events, httpx_client):
def before_breadcrumb(crumb, hint):
crumb["data"]["extra"] = "foo"
return crumb

sentry_init(integrations=[HttpxIntegration()], before_breadcrumb=before_breadcrumb)
clients = (httpx.Client(), httpx.AsyncClient())
for i, c in enumerate(clients):
with start_transaction():
events = capture_events()

url = "https://httpbin.org/status/200"
if not asyncio.iscoroutinefunction(c.get):
response = c.get(url)
else:
response = asyncio.get_event_loop().run_until_complete(c.get(url))

assert response.status_code == 200
capture_message("Testing!")

(event,) = events
# send request twice so we need get breadcrumb by index
crumb = event["breadcrumbs"]["values"][i]
assert crumb["type"] == "http"
assert crumb["category"] == "httplib"
assert crumb["data"] == {
"url": url,
"method": "GET",
"http.fragment": "",
"http.query": "",
"status_code": 200,
"reason": "OK",
"extra": "foo",
}


def test_outgoing_trace_headers(sentry_init):

url = "http://example.com/"
responses.add(responses.GET, url, status=200)

with start_transaction():
events = capture_events()

if asyncio.iscoroutinefunction(httpx_client.get):
response = asyncio.get_event_loop().run_until_complete(
httpx_client.get(url)
)
else:
response = httpx_client.get(url)

assert response.status_code == 200
capture_message("Testing!")

(event,) = events

crumb = event["breadcrumbs"]["values"][0]
assert crumb["type"] == "http"
assert crumb["category"] == "httplib"
assert crumb["data"] == {
"url": url,
"method": "GET",
"http.fragment": "",
"http.query": "",
"status_code": 200,
"reason": "OK",
"extra": "foo",
}


@pytest.mark.parametrize(
"httpx_client",
(httpx.Client(), httpx.AsyncClient()),
)
def test_outgoing_trace_headers(sentry_init, httpx_client):
sentry_init(traces_sample_rate=1.0, integrations=[HttpxIntegration()])
clients = (httpx.Client(), httpx.AsyncClient())
for i, c in enumerate(clients):
with start_transaction(
name="/interactions/other-dogs/new-dog",
op="greeting.sniff",
# make trace_id difference between transactions
trace_id=f"012345678901234567890123456789{i}",
) as transaction:
url = "https://httpbin.org/status/200"
if not asyncio.iscoroutinefunction(c.get):
response = c.get(url)
else:
response = asyncio.get_event_loop().run_until_complete(c.get(url))

request_span = transaction._span_recorder.spans[-1]
assert response.request.headers[
"sentry-trace"
] == "{trace_id}-{parent_span_id}-{sampled}".format(
trace_id=transaction.trace_id,
parent_span_id=request_span.span_id,
sampled=1,

url = "http://example.com/"
responses.add(responses.GET, url, status=200)

with start_transaction(
name="/interactions/other-dogs/new-dog",
op="greeting.sniff",
trace_id="01234567890123456789012345678901",
) as transaction:
if asyncio.iscoroutinefunction(httpx_client.get):
response = asyncio.get_event_loop().run_until_complete(
httpx_client.get(url)
)
else:
response = httpx_client.get(url)

request_span = transaction._span_recorder.spans[-1]
assert response.request.headers[
"sentry-trace"
] == "{trace_id}-{parent_span_id}-{sampled}".format(
trace_id=transaction.trace_id,
parent_span_id=request_span.span_id,
sampled=1,
)
6 changes: 3 additions & 3 deletions tests/integrations/opentelemetry/test_span_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,14 +212,14 @@ def test_update_span_with_otel_data_http_method2():
"http.status_code": 429,
"http.status_text": "xxx",
"http.user_agent": "curl/7.64.1",
"http.url": "https://httpbin.org/status/403?password=123&[email protected]&author=User123&auth=1234567890abcdef",
"http.url": "https://example.com/status/403?password=123&[email protected]&author=User123&auth=1234567890abcdef",
}

span_processor = SentrySpanProcessor()
span_processor._update_span_with_otel_data(sentry_span, otel_span)

assert sentry_span.op == "http.server"
assert sentry_span.description == "GET https://httpbin.org/status/403"
assert sentry_span.description == "GET https://example.com/status/403"
assert sentry_span._tags["http.status_code"] == "429"
assert sentry_span.status == "resource_exhausted"

Expand All @@ -229,7 +229,7 @@ def test_update_span_with_otel_data_http_method2():
assert sentry_span._data["http.user_agent"] == "curl/7.64.1"
assert (
sentry_span._data["http.url"]
== "https://httpbin.org/status/403?password=123&[email protected]&author=User123&auth=1234567890abcdef"
== "https://example.com/status/403?password=123&[email protected]&author=User123&auth=1234567890abcdef"
)


Expand Down
9 changes: 7 additions & 2 deletions tests/integrations/requests/test_requests.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import pytest
import responses

requests = pytest.importorskip("requests")

Expand All @@ -8,17 +9,21 @@

def test_crumb_capture(sentry_init, capture_events):
sentry_init(integrations=[StdlibIntegration()])

url = "http://example.com/"
responses.add(responses.GET, url, status=200)

events = capture_events()

response = requests.get("https://httpbin.org/status/418")
response = requests.get(url)
capture_message("Testing!")

(event,) = events
(crumb,) = event["breadcrumbs"]["values"]
assert crumb["type"] == "http"
assert crumb["category"] == "httplib"
assert crumb["data"] == {
"url": "https://httpbin.org/status/418",
"url": url,
"method": "GET",
"http.fragment": "",
"http.query": "",
Expand Down
Loading