Skip to content

Commit 7422a4c

Browse files
committed
Merge branch 'master' into potel-base
2 parents 4179751 + f5e964f commit 7422a4c

File tree

5 files changed

+116
-73
lines changed

5 files changed

+116
-73
lines changed

sentry_sdk/integrations/strawberry.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@
3535
except ImportError:
3636
raise DidNotEnable("strawberry-graphql is not installed")
3737

38+
try:
39+
from strawberry.extensions.tracing import ( # type: ignore
40+
SentryTracingExtension as StrawberrySentryAsyncExtension,
41+
SentryTracingExtensionSync as StrawberrySentrySyncExtension,
42+
)
43+
except ImportError:
44+
StrawberrySentryAsyncExtension = None
45+
StrawberrySentrySyncExtension = None
46+
3847
from typing import TYPE_CHECKING
3948

4049
if TYPE_CHECKING:

sentry_sdk/transport.py

Lines changed: 42 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -204,15 +204,7 @@ def __init__(self, options):
204204
) # type: DefaultDict[Tuple[EventDataCategory, str], int]
205205
self._last_client_report_sent = time.time()
206206

207-
self._pool = self._make_pool(
208-
self.parsed_dsn,
209-
http_proxy=options["http_proxy"],
210-
https_proxy=options["https_proxy"],
211-
ca_certs=options["ca_certs"],
212-
cert_file=options["cert_file"],
213-
key_file=options["key_file"],
214-
proxy_headers=options["proxy_headers"],
215-
)
207+
self._pool = self._make_pool()
216208

217209
experiments = options.get("_experiments", {})
218210
compression_level = experiments.get(
@@ -512,8 +504,8 @@ def _serialize_envelope(self, envelope):
512504

513505
return content_encoding, body
514506

515-
def _get_pool_options(self, ca_certs, cert_file=None, key_file=None):
516-
# type: (Self, Optional[Any], Optional[Any], Optional[Any]) -> Dict[str, Any]
507+
def _get_pool_options(self):
508+
# type: (Self) -> Dict[str, Any]
517509
raise NotImplementedError()
518510

519511
def _in_no_proxy(self, parsed_dsn):
@@ -527,17 +519,8 @@ def _in_no_proxy(self, parsed_dsn):
527519
return True
528520
return False
529521

530-
def _make_pool(
531-
self,
532-
parsed_dsn, # type: Dsn
533-
http_proxy, # type: Optional[str]
534-
https_proxy, # type: Optional[str]
535-
ca_certs, # type: Optional[Any]
536-
cert_file, # type: Optional[Any]
537-
key_file, # type: Optional[Any]
538-
proxy_headers, # type: Optional[Dict[str, str]]
539-
):
540-
# type: (...) -> Union[PoolManager, ProxyManager, httpcore.SOCKSProxy, httpcore.HTTPProxy, httpcore.ConnectionPool]
522+
def _make_pool(self):
523+
# type: (Self) -> Union[PoolManager, ProxyManager, httpcore.SOCKSProxy, httpcore.HTTPProxy, httpcore.ConnectionPool]
541524
raise NotImplementedError()
542525

543526
def _request(
@@ -587,8 +570,8 @@ class HttpTransport(BaseHttpTransport):
587570
if TYPE_CHECKING:
588571
_pool: Union[PoolManager, ProxyManager]
589572

590-
def _get_pool_options(self, ca_certs, cert_file=None, key_file=None):
591-
# type: (Self, Any, Any, Any) -> Dict[str, Any]
573+
def _get_pool_options(self):
574+
# type: (Self) -> Dict[str, Any]
592575

593576
num_pools = self.options.get("_experiments", {}).get("transport_num_pools")
594577
options = {
@@ -614,42 +597,43 @@ def _get_pool_options(self, ca_certs, cert_file=None, key_file=None):
614597
options["socket_options"] = socket_options
615598

616599
options["ca_certs"] = (
617-
ca_certs # User-provided bundle from the SDK init
600+
self.options["ca_certs"] # User-provided bundle from the SDK init
618601
or os.environ.get("SSL_CERT_FILE")
619602
or os.environ.get("REQUESTS_CA_BUNDLE")
620603
or certifi.where()
621604
)
622605

623-
options["cert_file"] = cert_file or os.environ.get("CLIENT_CERT_FILE")
624-
options["key_file"] = key_file or os.environ.get("CLIENT_KEY_FILE")
606+
options["cert_file"] = self.options["cert_file"] or os.environ.get(
607+
"CLIENT_CERT_FILE"
608+
)
609+
options["key_file"] = self.options["key_file"] or os.environ.get(
610+
"CLIENT_KEY_FILE"
611+
)
625612

626613
return options
627614

628-
def _make_pool(
629-
self,
630-
parsed_dsn, # type: Dsn
631-
http_proxy, # type: Optional[str]
632-
https_proxy, # type: Optional[str]
633-
ca_certs, # type: Any
634-
cert_file, # type: Any
635-
key_file, # type: Any
636-
proxy_headers, # type: Optional[Dict[str, str]]
637-
):
638-
# type: (...) -> Union[PoolManager, ProxyManager]
615+
def _make_pool(self):
616+
# type: (Self) -> Union[PoolManager, ProxyManager]
617+
if self.parsed_dsn is None:
618+
raise ValueError("Cannot create HTTP-based transport without valid DSN")
619+
639620
proxy = None
640-
no_proxy = self._in_no_proxy(parsed_dsn)
621+
no_proxy = self._in_no_proxy(self.parsed_dsn)
641622

642623
# try HTTPS first
643-
if parsed_dsn.scheme == "https" and (https_proxy != ""):
624+
https_proxy = self.options["https_proxy"]
625+
if self.parsed_dsn.scheme == "https" and (https_proxy != ""):
644626
proxy = https_proxy or (not no_proxy and getproxies().get("https"))
645627

646628
# maybe fallback to HTTP proxy
629+
http_proxy = self.options["http_proxy"]
647630
if not proxy and (http_proxy != ""):
648631
proxy = http_proxy or (not no_proxy and getproxies().get("http"))
649632

650-
opts = self._get_pool_options(ca_certs, cert_file, key_file)
633+
opts = self._get_pool_options()
651634

652635
if proxy:
636+
proxy_headers = self.options["proxy_headers"]
653637
if proxy_headers:
654638
opts["proxy_headers"] = proxy_headers
655639

@@ -739,10 +723,11 @@ def _request(
739723
)
740724
return response
741725

742-
def _get_pool_options(self, ca_certs, cert_file=None, key_file=None):
743-
# type: (Any, Any, Any) -> Dict[str, Any]
726+
def _get_pool_options(self):
727+
# type: (Self) -> Dict[str, Any]
744728
options = {
745-
"http2": True,
729+
"http2": self.parsed_dsn is not None
730+
and self.parsed_dsn.scheme == "https",
746731
"retries": 3,
747732
} # type: Dict[str, Any]
748733

@@ -761,45 +746,41 @@ def _get_pool_options(self, ca_certs, cert_file=None, key_file=None):
761746

762747
ssl_context = ssl.create_default_context()
763748
ssl_context.load_verify_locations(
764-
ca_certs # User-provided bundle from the SDK init
749+
self.options["ca_certs"] # User-provided bundle from the SDK init
765750
or os.environ.get("SSL_CERT_FILE")
766751
or os.environ.get("REQUESTS_CA_BUNDLE")
767752
or certifi.where()
768753
)
769-
cert_file = cert_file or os.environ.get("CLIENT_CERT_FILE")
770-
key_file = key_file or os.environ.get("CLIENT_KEY_FILE")
754+
cert_file = self.options["cert_file"] or os.environ.get("CLIENT_CERT_FILE")
755+
key_file = self.options["key_file"] or os.environ.get("CLIENT_KEY_FILE")
771756
if cert_file is not None:
772757
ssl_context.load_cert_chain(cert_file, key_file)
773758

774759
options["ssl_context"] = ssl_context
775760

776761
return options
777762

778-
def _make_pool(
779-
self,
780-
parsed_dsn, # type: Dsn
781-
http_proxy, # type: Optional[str]
782-
https_proxy, # type: Optional[str]
783-
ca_certs, # type: Any
784-
cert_file, # type: Any
785-
key_file, # type: Any
786-
proxy_headers, # type: Optional[Dict[str, str]]
787-
):
788-
# type: (...) -> Union[httpcore.SOCKSProxy, httpcore.HTTPProxy, httpcore.ConnectionPool]
763+
def _make_pool(self):
764+
# type: (Self) -> Union[httpcore.SOCKSProxy, httpcore.HTTPProxy, httpcore.ConnectionPool]
765+
if self.parsed_dsn is None:
766+
raise ValueError("Cannot create HTTP-based transport without valid DSN")
789767
proxy = None
790-
no_proxy = self._in_no_proxy(parsed_dsn)
768+
no_proxy = self._in_no_proxy(self.parsed_dsn)
791769

792770
# try HTTPS first
793-
if parsed_dsn.scheme == "https" and (https_proxy != ""):
771+
https_proxy = self.options["https_proxy"]
772+
if self.parsed_dsn.scheme == "https" and (https_proxy != ""):
794773
proxy = https_proxy or (not no_proxy and getproxies().get("https"))
795774

796775
# maybe fallback to HTTP proxy
776+
http_proxy = self.options["http_proxy"]
797777
if not proxy and (http_proxy != ""):
798778
proxy = http_proxy or (not no_proxy and getproxies().get("http"))
799779

800-
opts = self._get_pool_options(ca_certs, cert_file, key_file)
780+
opts = self._get_pool_options()
801781

802782
if proxy:
783+
proxy_headers = self.options["proxy_headers"]
803784
if proxy_headers:
804785
opts["proxy_headers"] = proxy_headers
805786

tests/integrations/strawberry/test_strawberry.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@
2424
)
2525
from tests.conftest import ApproxDict
2626

27+
try:
28+
from strawberry.extensions.tracing import (
29+
SentryTracingExtension,
30+
SentryTracingExtensionSync,
31+
)
32+
except ImportError:
33+
SentryTracingExtension = None
34+
SentryTracingExtensionSync = None
35+
2736
parameterize_strawberry_test = pytest.mark.parametrize(
2837
"client_factory,async_execution,framework_integrations",
2938
(
@@ -139,6 +148,32 @@ def test_infer_execution_type_from_installed_packages_sync(sentry_init):
139148
assert SentrySyncExtension in schema.extensions
140149

141150

151+
@pytest.mark.skipif(
152+
SentryTracingExtension is None,
153+
reason="SentryTracingExtension no longer available in this Strawberry version",
154+
)
155+
def test_replace_existing_sentry_async_extension(sentry_init):
156+
sentry_init(integrations=[StrawberryIntegration()])
157+
158+
schema = strawberry.Schema(Query, extensions=[SentryTracingExtension])
159+
assert SentryTracingExtension not in schema.extensions
160+
assert SentrySyncExtension not in schema.extensions
161+
assert SentryAsyncExtension in schema.extensions
162+
163+
164+
@pytest.mark.skipif(
165+
SentryTracingExtensionSync is None,
166+
reason="SentryTracingExtensionSync no longer available in this Strawberry version",
167+
)
168+
def test_replace_existing_sentry_sync_extension(sentry_init):
169+
sentry_init(integrations=[StrawberryIntegration()])
170+
171+
schema = strawberry.Schema(Query, extensions=[SentryTracingExtensionSync])
172+
assert SentryTracingExtensionSync not in schema.extensions
173+
assert SentryAsyncExtension not in schema.extensions
174+
assert SentrySyncExtension in schema.extensions
175+
176+
142177
@parameterize_strawberry_test
143178
def test_capture_request_if_available_and_send_pii_is_on(
144179
request,

tests/test_transport.py

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ def test_transport_num_pools(make_client, num_pools, expected_num_pools):
205205

206206
client = make_client(_experiments=_experiments)
207207

208-
options = client.transport._get_pool_options([])
208+
options = client.transport._get_pool_options()
209209
assert options["num_pools"] == expected_num_pools
210210

211211

@@ -217,12 +217,15 @@ def test_two_way_ssl_authentication(make_client, http2):
217217
if http2:
218218
_experiments["transport_http2"] = True
219219

220-
client = make_client(_experiments=_experiments)
221-
222220
current_dir = os.path.dirname(__file__)
223221
cert_file = f"{current_dir}/test.pem"
224222
key_file = f"{current_dir}/test.key"
225-
options = client.transport._get_pool_options([], cert_file, key_file)
223+
client = make_client(
224+
cert_file=cert_file,
225+
key_file=key_file,
226+
_experiments=_experiments,
227+
)
228+
options = client.transport._get_pool_options()
226229

227230
if http2:
228231
assert options["ssl_context"] is not None
@@ -240,23 +243,39 @@ def test_socket_options(make_client):
240243

241244
client = make_client(socket_options=socket_options)
242245

243-
options = client.transport._get_pool_options([])
246+
options = client.transport._get_pool_options()
244247
assert options["socket_options"] == socket_options
245248

246249

247250
def test_keep_alive_true(make_client):
248251
client = make_client(keep_alive=True)
249252

250-
options = client.transport._get_pool_options([])
253+
options = client.transport._get_pool_options()
251254
assert options["socket_options"] == KEEP_ALIVE_SOCKET_OPTIONS
252255

253256

254257
def test_keep_alive_on_by_default(make_client):
255258
client = make_client()
256-
options = client.transport._get_pool_options([])
259+
options = client.transport._get_pool_options()
257260
assert "socket_options" not in options
258261

259262

263+
@pytest.mark.skipif(not PY38, reason="HTTP2 libraries are only available in py3.8+")
264+
def test_http2_with_https_dsn(make_client):
265+
client = make_client(_experiments={"transport_http2": True})
266+
client.transport.parsed_dsn.scheme = "https"
267+
options = client.transport._get_pool_options()
268+
assert options["http2"] is True
269+
270+
271+
@pytest.mark.skipif(not PY38, reason="HTTP2 libraries are only available in py3.8+")
272+
def test_no_http2_with_http_dsn(make_client):
273+
client = make_client(_experiments={"transport_http2": True})
274+
client.transport.parsed_dsn.scheme = "http"
275+
options = client.transport._get_pool_options()
276+
assert options["http2"] is False
277+
278+
260279
def test_socket_options_override_keep_alive(make_client):
261280
socket_options = [
262281
(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1),
@@ -266,7 +285,7 @@ def test_socket_options_override_keep_alive(make_client):
266285

267286
client = make_client(socket_options=socket_options, keep_alive=False)
268287

269-
options = client.transport._get_pool_options([])
288+
options = client.transport._get_pool_options()
270289
assert options["socket_options"] == socket_options
271290

272291

@@ -278,7 +297,7 @@ def test_socket_options_merge_with_keep_alive(make_client):
278297

279298
client = make_client(socket_options=socket_options, keep_alive=True)
280299

281-
options = client.transport._get_pool_options([])
300+
options = client.transport._get_pool_options()
282301
try:
283302
assert options["socket_options"] == [
284303
(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 42),
@@ -300,7 +319,7 @@ def test_socket_options_override_defaults(make_client):
300319
# socket option defaults, so we need to set this and not ignore it.
301320
client = make_client(socket_options=[])
302321

303-
options = client.transport._get_pool_options([])
322+
options = client.transport._get_pool_options()
304323
assert options["socket_options"] == []
305324

306325

tox.ini

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -429,8 +429,7 @@ deps =
429429
falcon-v1: falcon~=1.0
430430
falcon-v2: falcon~=2.0
431431
falcon-v3: falcon~=3.0
432-
# TODO: update to 4.0 stable when out
433-
falcon-v4: falcon==4.0.0rc1
432+
falcon-v4: falcon~=4.0
434433
falcon-latest: falcon
435434

436435
# FastAPI

0 commit comments

Comments
 (0)