Skip to content

Commit f946729

Browse files
ref(sessions): Deprecate hub-based sessions.py logic
Make several changes to prepare for fully removing Hubs in the next major: - Deprecate the Hub-based `auto_session_tracking` function, replacing it with a new Scope-based function called `track_session` - Deprecate the scope-based `auto_session_tracking_scope` in favor of the new `track_session` function - To limit public API surface, deprecate `is_auto_session_tracking_enabled` and `is_auto_session_tracking_enabled_scope` and replace them with a Scope-based private function called `_is_auto_session_tracking_enabled` - Change usages of `auto_session_tracking_scope` to `track_sessions`. There are no usages of `auto_session_tracking` outside of tests. - Run all tests that were previously run against `auto_session_tracking` also against the new `track_session`. Previously, `auto_session_tracking_scope` was completely untested. Fixes #3417
1 parent 19c4069 commit f946729

File tree

5 files changed

+192
-44
lines changed

5 files changed

+192
-44
lines changed

sentry_sdk/integrations/aiohttp.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from sentry_sdk.consts import OP, SPANSTATUS, SPANDATA
77
from sentry_sdk.integrations import Integration, DidNotEnable
88
from sentry_sdk.integrations.logging import ignore_logger
9-
from sentry_sdk.sessions import auto_session_tracking_scope
9+
from sentry_sdk.sessions import track_session
1010
from sentry_sdk.integrations._wsgi_common import (
1111
_filter_headers,
1212
request_body_within_bounds,
@@ -105,7 +105,7 @@ async def sentry_app_handle(self, request, *args, **kwargs):
105105
weak_request = weakref.ref(request)
106106

107107
with sentry_sdk.isolation_scope() as scope:
108-
with auto_session_tracking_scope(scope, session_mode="request"):
108+
with track_session(scope, session_mode="request"):
109109
# Scope data will not leak between requests because aiohttp
110110
# create a task to wrap each request.
111111
scope.generate_propagation_context()

sentry_sdk/integrations/asgi.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
_get_request_data,
2020
_get_url,
2121
)
22-
from sentry_sdk.sessions import auto_session_tracking_scope
22+
from sentry_sdk.sessions import track_session
2323
from sentry_sdk.tracing import (
2424
SOURCE_FOR_STYLE,
2525
TRANSACTION_SOURCE_ROUTE,
@@ -169,7 +169,7 @@ async def _run_app(self, scope, receive, send, asgi_version):
169169
_asgi_middleware_applied.set(True)
170170
try:
171171
with sentry_sdk.isolation_scope() as sentry_scope:
172-
with auto_session_tracking_scope(sentry_scope, session_mode="request"):
172+
with track_session(sentry_scope, session_mode="request"):
173173
sentry_scope.clear_breadcrumbs()
174174
sentry_scope._name = "asgi"
175175
processor = partial(self.event_processor, asgi_scope=scope)

sentry_sdk/integrations/wsgi.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@
88
from sentry_sdk.consts import OP
99
from sentry_sdk.scope import should_send_default_pii
1010
from sentry_sdk.integrations._wsgi_common import _filter_headers
11-
from sentry_sdk.sessions import (
12-
auto_session_tracking_scope as auto_session_tracking,
13-
) # When the Hub is removed, this should be renamed (see comment in sentry_sdk/sessions.py)
11+
from sentry_sdk.sessions import track_session
1412
from sentry_sdk.scope import use_isolation_scope
1513
from sentry_sdk.tracing import Transaction, TRANSACTION_SOURCE_ROUTE
1614
from sentry_sdk.utils import (
@@ -83,7 +81,7 @@ def __call__(self, environ, start_response):
8381
_wsgi_middleware_applied.set(True)
8482
try:
8583
with sentry_sdk.isolation_scope() as scope:
86-
with auto_session_tracking(scope, session_mode="request"):
84+
with track_session(scope, session_mode="request"):
8785
with capture_internal_exceptions():
8886
scope.clear_breadcrumbs()
8987
scope._name = "wsgi"

sentry_sdk/sessions.py

Lines changed: 81 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import os
22
import time
3+
import warnings
34
from threading import Thread, Lock
45
from contextlib import contextmanager
56

@@ -19,32 +20,56 @@
1920
from typing import Union
2021

2122

22-
def is_auto_session_tracking_enabled(hub=None):
23-
# type: (Optional[sentry_sdk.Hub]) -> Union[Any, bool, None]
24-
"""Utility function to find out if session tracking is enabled."""
25-
# TODO: add deprecation warning
26-
27-
if hub is None:
28-
hub = sentry_sdk.Hub.current
29-
30-
should_track = hub.scope._force_auto_session_tracking
23+
def _is_auto_session_tracking_enabled(scope):
24+
# type: (sentry_sdk.Scope) -> bool
25+
"""
26+
Utility function to find out if session tracking is enabled.
27+
"""
3128

29+
should_track = scope._force_auto_session_tracking
3230
if should_track is None:
33-
client_options = hub.client.options if hub.client else {}
31+
client_options = sentry_sdk.get_client().options
3432
should_track = client_options.get("auto_session_tracking", False)
3533

3634
return should_track
3735

3836

37+
@contextmanager
38+
def track_session(scope, session_mode="application"):
39+
# type: (sentry_sdk.Scope, str) -> None
40+
"""
41+
Start a new session in the provided scope, assuming session tracking is enabled.
42+
This is a no-op context manager if session tracking is not enabled.
43+
"""
44+
45+
if _is_auto_session_tracking_enabled(scope):
46+
scope.start_session(session_mode=session_mode)
47+
try:
48+
yield
49+
finally:
50+
scope.end_session()
51+
else:
52+
yield
53+
54+
3955
@contextmanager
4056
def auto_session_tracking(hub=None, session_mode="application"):
4157
# type: (Optional[sentry_sdk.Hub], str) -> Generator[None, None, None]
42-
"""Starts and stops a session automatically around a block."""
43-
# TODO: add deprecation warning
58+
"""DEPRECATED: Use track_session instead
59+
Starts and stops a session automatically around a block.
60+
"""
61+
warnings.warn(
62+
"This function is deprecated and will be removed in the next major release. "
63+
"Use track_session instead.",
64+
DeprecationWarning,
65+
stacklevel=2,
66+
)
4467

4568
if hub is None:
4669
hub = sentry_sdk.Hub.current
47-
should_track = is_auto_session_tracking_enabled(hub)
70+
with warnings.catch_warnings():
71+
warnings.simplefilter("ignore", DeprecationWarning)
72+
should_track = is_auto_session_tracking_enabled(hub)
4873
if should_track:
4974
hub.start_session(session_mode=session_mode)
5075
try:
@@ -54,41 +79,62 @@ def auto_session_tracking(hub=None, session_mode="application"):
5479
hub.end_session()
5580

5681

57-
def is_auto_session_tracking_enabled_scope(scope):
58-
# type: (sentry_sdk.Scope) -> bool
59-
"""
60-
Utility function to find out if session tracking is enabled.
82+
def is_auto_session_tracking_enabled(hub=None):
83+
# type: (Optional[sentry_sdk.Hub]) -> Union[Any, bool, None]
84+
"""DEPRECATED: Utility function to find out if session tracking is enabled."""
85+
86+
# Internal callers should use private _is_auto_session_tracking_enabled, instead.
87+
warnings.warn(
88+
"This function is deprecated and will be removed in the next major release. "
89+
"There is no public API replacement.",
90+
DeprecationWarning,
91+
stacklevel=2,
92+
)
93+
94+
if hub is None:
95+
hub = sentry_sdk.Hub.current
96+
97+
should_track = hub.scope._force_auto_session_tracking
6198

62-
TODO: This uses the new scopes. When the Hub is removed, the function
63-
is_auto_session_tracking_enabled should be removed and this function
64-
should be renamed to is_auto_session_tracking_enabled.
65-
"""
66-
should_track = scope._force_auto_session_tracking
6799
if should_track is None:
68-
client_options = sentry_sdk.get_client().options
100+
client_options = hub.client.options if hub.client else {}
69101
should_track = client_options.get("auto_session_tracking", False)
70102

71103
return should_track
72104

73105

106+
def is_auto_session_tracking_enabled_scope(scope):
107+
# type: (sentry_sdk.Scope) -> bool
108+
"""
109+
DEPRECATED: Utility function to find out if session tracking is enabled.
110+
"""
111+
112+
warnings.warn(
113+
"This function is deprecated and will be removed in the next major release. "
114+
"There is no public API replacement.",
115+
DeprecationWarning,
116+
stacklevel=2,
117+
)
118+
119+
# Internal callers should use private _is_auto_session_tracking_enabled, instead.
120+
return _is_auto_session_tracking_enabled(scope)
121+
122+
74123
@contextmanager
75124
def auto_session_tracking_scope(scope, session_mode="application"):
76125
# type: (sentry_sdk.Scope, str) -> Generator[None, None, None]
77-
"""
126+
"""DEPRECATED: This function is a deprecated alias for track_session.
78127
Starts and stops a session automatically around a block.
79-
80-
TODO: This uses the new scopes. When the Hub is removed, the function
81-
auto_session_tracking should be removed and this function
82-
should be renamed to auto_session_tracking.
83128
"""
84-
should_track = is_auto_session_tracking_enabled_scope(scope)
85-
if should_track:
86-
scope.start_session(session_mode=session_mode)
87-
try:
129+
130+
warnings.warn(
131+
"This function is a deprecated alias for track_session and will be removed in the next major release.",
132+
DeprecationWarning,
133+
stacklevel=2,
134+
)
135+
136+
with track_session(scope, session_mode=session_mode):
88137
yield
89-
finally:
90-
if should_track:
91-
scope.end_session()
92138

93139

94140
TERMINAL_SESSION_STATES = ("exited", "abnormal", "crashed")

tests/test_sessions.py

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from unittest import mock
22

33
import sentry_sdk
4-
from sentry_sdk.sessions import auto_session_tracking
4+
from sentry_sdk.sessions import auto_session_tracking, track_session
55

66

77
def sorted_aggregates(item):
@@ -50,6 +50,48 @@ def test_aggregates(sentry_init, capture_envelopes):
5050
)
5151
envelopes = capture_envelopes()
5252

53+
with sentry_sdk.isolation_scope() as scope:
54+
with track_session(scope, session_mode="request"):
55+
try:
56+
scope.set_user({"id": "42"})
57+
raise Exception("all is wrong")
58+
except Exception:
59+
sentry_sdk.capture_exception()
60+
61+
with sentry_sdk.isolation_scope() as scope:
62+
with track_session(scope, session_mode="request"):
63+
pass
64+
65+
sentry_sdk.get_isolation_scope().start_session(session_mode="request")
66+
sentry_sdk.get_isolation_scope().end_session()
67+
sentry_sdk.flush()
68+
69+
assert len(envelopes) == 2
70+
assert envelopes[0].get_event() is not None
71+
72+
sess = envelopes[1]
73+
assert len(sess.items) == 1
74+
sess_event = sess.items[0].payload.json
75+
assert sess_event["attrs"] == {
76+
"release": "fun-release",
77+
"environment": "not-fun-env",
78+
}
79+
80+
aggregates = sorted_aggregates(sess_event)
81+
assert len(aggregates) == 1
82+
assert aggregates[0]["exited"] == 2
83+
assert aggregates[0]["errored"] == 1
84+
85+
86+
def test_aggregates_deprecated(
87+
sentry_init, capture_envelopes, suppress_deprecation_warnings
88+
):
89+
sentry_init(
90+
release="fun-release",
91+
environment="not-fun-env",
92+
)
93+
envelopes = capture_envelopes()
94+
5395
with auto_session_tracking(session_mode="request"):
5496
with sentry_sdk.new_scope() as scope:
5597
try:
@@ -90,6 +132,39 @@ def test_aggregates_explicitly_disabled_session_tracking_request_mode(
90132
)
91133
envelopes = capture_envelopes()
92134

135+
with sentry_sdk.isolation_scope() as scope:
136+
with track_session(scope, session_mode="request"):
137+
try:
138+
raise Exception("all is wrong")
139+
except Exception:
140+
sentry_sdk.capture_exception()
141+
142+
with sentry_sdk.isolation_scope() as scope:
143+
with track_session(scope, session_mode="request"):
144+
pass
145+
146+
sentry_sdk.get_isolation_scope().start_session(session_mode="request")
147+
sentry_sdk.get_isolation_scope().end_session()
148+
sentry_sdk.flush()
149+
150+
sess = envelopes[1]
151+
assert len(sess.items) == 1
152+
sess_event = sess.items[0].payload.json
153+
154+
aggregates = sorted_aggregates(sess_event)
155+
assert len(aggregates) == 1
156+
assert aggregates[0]["exited"] == 1
157+
assert "errored" not in aggregates[0]
158+
159+
160+
def test_aggregates_explicitly_disabled_session_tracking_request_mode_deprecated(
161+
sentry_init, capture_envelopes, suppress_deprecation_warnings
162+
):
163+
sentry_init(
164+
release="fun-release", environment="not-fun-env", auto_session_tracking=False
165+
)
166+
envelopes = capture_envelopes()
167+
93168
with auto_session_tracking(session_mode="request"):
94169
with sentry_sdk.new_scope():
95170
try:
@@ -120,6 +195,35 @@ def test_no_thread_on_shutdown_no_errors(sentry_init):
120195
environment="not-fun-env",
121196
)
122197

198+
# make it seem like the interpreter is shutting down
199+
with mock.patch(
200+
"threading.Thread.start",
201+
side_effect=RuntimeError("can't create new thread at interpreter shutdown"),
202+
):
203+
with sentry_sdk.isolation_scope() as scope:
204+
with track_session(scope, session_mode="request"):
205+
try:
206+
raise Exception("all is wrong")
207+
except Exception:
208+
sentry_sdk.capture_exception()
209+
210+
with sentry_sdk.isolation_scope() as scope:
211+
with track_session(scope, session_mode="request"):
212+
pass
213+
214+
sentry_sdk.get_isolation_scope().start_session(session_mode="request")
215+
sentry_sdk.get_isolation_scope().end_session()
216+
sentry_sdk.flush()
217+
218+
219+
def test_no_thread_on_shutdown_no_errors_deprecated(
220+
sentry_init, suppress_deprecation_warnings
221+
):
222+
sentry_init(
223+
release="fun-release",
224+
environment="not-fun-env",
225+
)
226+
123227
# make it seem like the interpreter is shutting down
124228
with mock.patch(
125229
"threading.Thread.start",

0 commit comments

Comments
 (0)