Skip to content

Commit f85f3a2

Browse files
Reapply "feat: SDK 2.0 Support (#31)" (#33)
This reverts commit 687802f and changes the install requires version specifier. To be merged into main only after a patch release containing #32 is released.
1 parent 6544be9 commit f85f3a2

File tree

8 files changed

+110
-93
lines changed

8 files changed

+110
-93
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ jobs:
88
runs-on: ubuntu-latest
99
strategy:
1010
matrix:
11-
python: ['3.7', '3.8', '3.9', '3.10', '3.11']
11+
python: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12']
1212
steps:
1313
- uses: actions/checkout@v3
1414
- uses: actions/setup-python@v4

README.rst

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,14 @@ Advanced Options
8484
================
8585

8686
``pytest-sentry`` supports marking your tests to use a different DSN, client or
87-
hub per-test. You can use this to provide custom options to the ``Client``
87+
scope per-test. You can use this to provide custom options to the ``Client``
8888
object from the `Sentry SDK for Python
8989
<https://github.com/getsentry/sentry-python>`_::
9090

9191
import random
9292
import pytest
9393

94-
from sentry_sdk import Hub
94+
from sentry_sdk import Scope
9595
from pytest_sentry import Client
9696

9797
@pytest.mark.sentry_client(None)
@@ -108,7 +108,7 @@ object from the `Sentry SDK for Python
108108

109109
@pytest.mark.sentry_client(Client("CUSTOM DSN"))
110110
@pytest.mark.sentry_client(lambda: Client("CUSTOM DSN"))
111-
@pytest.mark.sentry_client(Hub(Client("CUSTOM DSN")))
111+
@pytest.mark.sentry_client(Scope(Client("CUSTOM DSN")))
112112
@pytest.mark.sentry_client({"dsn": ..., "debug": True})
113113

114114

@@ -125,12 +125,12 @@ you configured this plugin with. That's because ``pytest-sentry`` goes to
125125
extreme lenghts to keep its own SDK setup separate from the SDK setup of the
126126
tested code.
127127

128-
``pytest-sentry`` exposes the ``sentry_test_hub`` fixture whose return value is
129-
the ``Hub`` being used to send events to Sentry. Use ``with sentry_test_hub:``
128+
``pytest-sentry`` exposes the ``sentry_test_scope`` fixture whose return value is
129+
the ``Scope`` being used to send events to Sentry. Use ``with use_scope(entry_test_scope):``
130130
to temporarily switch context. You can use this to set custom tags like so::
131131

132-
def test_foo(sentry_test_hub):
133-
with sentry_test_hub:
132+
def test_foo(sentry_test_scope):
133+
with use_scope(sentry_test_scope):
134134
sentry_sdk.set_tag("pull_request", os.environ['EXAMPLE_CI_PULL_REQUEST'])
135135

136136

pytest_sentry.py

Lines changed: 40 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
import sentry_sdk
88
from sentry_sdk.integrations import Integration
99

10-
from sentry_sdk import Hub, capture_exception
10+
from sentry_sdk import Scope, capture_exception
1111
from sentry_sdk.tracing import Transaction
12-
from sentry_sdk.scope import add_global_event_processor
12+
from sentry_sdk.scope import add_global_event_processor, use_scope
1313

1414
_ENVVARS_AS_TAGS = frozenset(
1515
[
@@ -59,7 +59,7 @@ def __init__(self, always_report=None):
5959
def setup_once():
6060
@add_global_event_processor
6161
def procesor(event, hint):
62-
if Hub.current.get_integration(PytestIntegration) is None:
62+
if Scope.get_client().get_integration(PytestIntegration) is None:
6363
return event
6464

6565
for key in _ENVVARS_AS_TAGS:
@@ -82,7 +82,10 @@ def procesor(event, hint):
8282
class Client(sentry_sdk.Client):
8383
def __init__(self, *args, **kwargs):
8484
kwargs.setdefault("dsn", os.environ.get("PYTEST_SENTRY_DSN", None))
85-
kwargs.setdefault("traces_sample_rate", float(os.environ.get("PYTEST_SENTRY_TRACES_SAMPLE_RATE", 1.0)))
85+
kwargs.setdefault(
86+
"traces_sample_rate",
87+
float(os.environ.get("PYTEST_SENTRY_TRACES_SAMPLE_RATE", 1.0)),
88+
)
8689
kwargs.setdefault("_experiments", {}).setdefault(
8790
"auto_enabling_integrations", True
8891
)
@@ -94,52 +97,52 @@ def __init__(self, *args, **kwargs):
9497

9598
def hookwrapper(itemgetter, **kwargs):
9699
"""
97-
A version of pytest.hookimpl that sets the current hub to the correct one
100+
A version of pytest.hookimpl that sets the current scope to the correct one
98101
and skips the hook if the integration is disabled.
99102
100103
Assumes the function is a hookwrapper, ie yields once
101104
"""
102105

103106
@wrapt.decorator
104-
def _with_hub(wrapped, instance, args, kwargs):
107+
def _with_scope(wrapped, instance, args, kwargs):
105108
item = itemgetter(*args, **kwargs)
106-
hub = _resolve_hub_marker_value(item.get_closest_marker("sentry_client"))
109+
scope = _resolve_scope_marker_value(item.get_closest_marker("sentry_client"))
107110

108-
if hub.get_integration(PytestIntegration) is None:
111+
if scope.client.get_integration(PytestIntegration) is None:
109112
yield
110113
else:
111-
with hub:
114+
with use_scope(scope):
112115
gen = wrapped(*args, **kwargs)
113116

114117
while True:
115118
try:
116-
with hub:
119+
with use_scope(scope):
117120
chunk = next(gen)
118121

119122
y = yield chunk
120123

121-
with hub:
124+
with use_scope(scope):
122125
gen.send(y)
123126

124127
except StopIteration:
125128
break
126129

127130
def inner(f):
128-
return pytest.hookimpl(hookwrapper=True, **kwargs)(_with_hub(f))
131+
return pytest.hookimpl(hookwrapper=True, **kwargs)(_with_scope(f))
129132

130133
return inner
131134

132135

133136
def pytest_load_initial_conftests(early_config, parser, args):
134137
early_config.addinivalue_line(
135138
"markers",
136-
"sentry_client(client=None): Use this client instance for reporting tests. You can also pass a DSN string directly, or a `Hub` if you need it.",
139+
"sentry_client(client=None): Use this client instance for reporting tests. You can also pass a DSN string directly, or a `Scope` if you need it.",
137140
)
138141

139142

140143
def _start_transaction(**kwargs):
141144
transaction = Transaction.continue_from_headers(
142-
dict(Hub.current.iter_trace_propagation_headers()), **kwargs
145+
dict(Scope.get_current_scope().iter_trace_propagation_headers()), **kwargs
143146
)
144147
transaction.same_process_as_parent = True
145148
return sentry_sdk.start_transaction(transaction)
@@ -154,7 +157,7 @@ def pytest_runtest_protocol(item):
154157
# We use the full name including parameters because then we can identify
155158
# how often a single test has run as part of the same GITHUB_RUN_ID.
156159

157-
with _start_transaction(op=op, name=u"{} {}".format(op, name)) as tx:
160+
with _start_transaction(op=op, name="{} {}".format(op, name)) as tx:
158161
yield
159162

160163
# Purposefully drop transaction to spare quota. We only created it to
@@ -171,14 +174,16 @@ def pytest_runtest_call(item):
171174
# We use the full name including parameters because then we can identify
172175
# how often a single test has run as part of the same GITHUB_RUN_ID.
173176

174-
with _start_transaction(op=op, name=u"{} {}".format(op, name)):
177+
with _start_transaction(op=op, name="{} {}".format(op, name)):
175178
yield
176179

177180

178181
@hookwrapper(itemgetter=lambda fixturedef, request: request._pyfuncitem)
179182
def pytest_fixture_setup(fixturedef, request):
180183
op = "pytest.fixture.setup"
181-
with _start_transaction(op=op, name=u"{} {}".format(op, fixturedef.argname)) as transaction:
184+
with _start_transaction(
185+
op=op, name="{} {}".format(op, fixturedef.argname)
186+
) as transaction:
182187
transaction.set_tag("pytest.fixture.scope", fixturedef.scope)
183188
yield
184189

@@ -198,31 +203,31 @@ def pytest_runtest_makereport(item, call):
198203
call.excinfo
199204
]
200205

201-
integration = Hub.current.get_integration(PytestIntegration)
206+
integration = Scope.get_client().get_integration(PytestIntegration)
202207

203208
if (cur_exc_chain and call.excinfo is None) or integration.always_report:
204209
for exc_info in cur_exc_chain:
205210
capture_exception((exc_info.type, exc_info.value, exc_info.tb))
206211

207212

208-
DEFAULT_HUB = Hub(Client())
213+
DEFAULT_SCOPE = Scope(client=Client())
209214

210-
_hub_cache = {}
215+
_scope_cache = {}
211216

212217

213-
def _resolve_hub_marker_value(marker_value):
214-
if id(marker_value) not in _hub_cache:
215-
_hub_cache[id(marker_value)] = rv = _resolve_hub_marker_value_uncached(
218+
def _resolve_scope_marker_value(marker_value):
219+
if id(marker_value) not in _scope_cache:
220+
_scope_cache[id(marker_value)] = rv = _resolve_scope_marker_value_uncached(
216221
marker_value
217222
)
218223
return rv
219224

220-
return _hub_cache[id(marker_value)]
225+
return _scope_cache[id(marker_value)]
221226

222227

223-
def _resolve_hub_marker_value_uncached(marker_value):
228+
def _resolve_scope_marker_value_uncached(marker_value):
224229
if marker_value is None:
225-
marker_value = DEFAULT_HUB
230+
marker_value = DEFAULT_SCOPE
226231
else:
227232
marker_value = marker_value.args[0]
228233

@@ -231,35 +236,35 @@ def _resolve_hub_marker_value_uncached(marker_value):
231236

232237
if marker_value is None:
233238
# user explicitly disabled reporting
234-
return Hub()
239+
return Scope()
235240

236241
if isinstance(marker_value, str):
237-
return Hub(Client(marker_value))
242+
return Scope(client=Client(marker_value))
238243

239244
if isinstance(marker_value, dict):
240-
return Hub(Client(**marker_value))
245+
return Scope(client=Client(**marker_value))
241246

242247
if isinstance(marker_value, Client):
243-
return Hub(marker_value)
248+
return Scope(client=marker_value)
244249

245-
if isinstance(marker_value, Hub):
250+
if isinstance(marker_value, Scope):
246251
return marker_value
247252

248253
raise RuntimeError(
249-
"The `sentry_client` value must be a client, hub or string, not {}".format(
254+
"The `sentry_client` value must be a client, scope or string, not {}".format(
250255
repr(type(marker_value))
251256
)
252257
)
253258

254259

255260
@pytest.fixture
256-
def sentry_test_hub(request):
261+
def sentry_test_scope(request):
257262
"""
258-
Gives back the current hub.
263+
Gives back the current scope.
259264
"""
260265

261266
item = request.node
262-
return _resolve_hub_marker_value(item.get_closest_marker("sentry_client"))
267+
return _resolve_scope_marker_value(item.get_closest_marker("sentry_client"))
263268

264269

265270
def _process_stacktrace(stacktrace):

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ classifiers =
1616
py_modules = pytest_sentry
1717
install_requires =
1818
pytest
19-
sentry-sdk
19+
sentry-sdk>=2.0.0a1,<3
2020
wrapt
2121

2222
[options.entry_points]

tests/test_envvars.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import pytest
22
import sentry_sdk
3+
from sentry_sdk.scope import use_scope
34
import pytest_sentry
45

56
events = []
@@ -26,9 +27,9 @@ def clear_events(monkeypatch):
2627
pytestmark = pytest.mark.sentry_client(pytest_sentry.Client(transport=MyTransport()))
2728

2829

29-
def test_basic(sentry_test_hub):
30-
with sentry_test_hub:
31-
sentry_test_hub.capture_message("hi")
30+
def test_basic(sentry_test_scope):
31+
with use_scope(sentry_test_scope):
32+
sentry_test_scope.capture_message("hi")
3233

3334
(event,) = events
3435
assert event["tags"]["pytest_environ.GITHUB_RUN_ID"] == "123abc"

tests/test_fixture.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
pytestmark = pytest.mark.sentry_client(GLOBAL_CLIENT)
88

99

10-
def test_basic(sentry_test_hub):
11-
assert sentry_test_hub.client is GLOBAL_CLIENT
10+
def test_basic(sentry_test_scope):
11+
assert sentry_test_scope.client is GLOBAL_CLIENT
1212

1313

1414
@pytest.mark.sentry_client(None)
15-
def test_func(sentry_test_hub):
16-
assert sentry_test_hub.client is None
15+
def test_func(sentry_test_scope):
16+
assert not sentry_test_scope.client.is_active()

tests/test_hub.py

Lines changed: 0 additions & 42 deletions
This file was deleted.

0 commit comments

Comments
 (0)