Skip to content

Commit dcbb627

Browse files
authored
Use contextvars if available (getsentry#6)
1 parent 877224d commit dcbb627

3 files changed

Lines changed: 37 additions & 16 deletions

File tree

sentry_sdk/hub.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,25 @@
11
import sys
22
import copy
33
import linecache
4-
from threading import local
54
from contextlib import contextmanager
65

76
from ._compat import with_metaclass
87
from .scope import Scope
98
from .utils import exceptions_from_error_tuple, create_event, \
10-
skip_internal_frames
9+
skip_internal_frames, ContextVar
1110

1211

13-
_local = local()
12+
_local = ContextVar('sentry_current_hub')
1413

1514

1615
class HubMeta(type):
1716

1817
@property
1918
def current(self):
20-
try:
21-
rv = _local.hub
22-
except AttributeError:
23-
_local.hub = rv = Hub(GLOBAL_HUB)
19+
rv = _local.get(None)
20+
if rv is None:
21+
rv = Hub(GLOBAL_HUB)
22+
_local.set(rv)
2423
return rv
2524

2625
@property
@@ -32,10 +31,10 @@ class _HubManager(object):
3231

3332
def __init__(self, hub):
3433
self._old = Hub.current
35-
_local.hub = hub
34+
_local.set(hub)
3635

3736
def __exit__(self, exc_type, exc_value, tb):
38-
_local.hub = self._old
37+
_local.set(self._old)
3938

4039

4140
class _ScopeManager(object):

sentry_sdk/integrations/django/__init__.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from __future__ import absolute_import
22

3-
from threading import Lock, local
3+
from threading import Lock
44

55
from django.conf import settings
66
from django.core import signals
@@ -10,6 +10,7 @@
1010
from django.core.urlresolvers import resolve
1111

1212
from sentry_sdk import get_current_hub, configure_scope, capture_exception
13+
from sentry_sdk.utils import ContextVar
1314

1415

1516
try:
@@ -23,15 +24,15 @@
2324
def _get_transaction_from_request(request):
2425
return resolve(request.path).func.__name__
2526

26-
_request_scope = local()
27+
_request_scope = ContextVar('sentry_django_request_scope')
2728

2829

2930
# request_started (or any other signal) cannot be used because the request is
3031
# not yet available
3132
class SentryMiddleware(MiddlewareMixin):
3233
def process_request(self, request):
33-
assert getattr(_request_scope, 'manager', None) is None, 'race condition'
34-
_request_scope.manager = get_current_hub().push_scope().__enter__()
34+
assert _request_scope.get(None) is None, 'race condition'
35+
_request_scope.set(get_current_hub().push_scope().__enter__())
3536

3637
try:
3738
with configure_scope() as scope:
@@ -41,9 +42,10 @@ def process_request(self, request):
4142

4243

4344
def _request_finished(*args, **kwargs):
44-
assert getattr(_request_scope, 'manager', None) is not None, 'race condition'
45-
_request_scope.manager.__exit__(None, None, None)
46-
_request_scope.manager = None
45+
val = _request_scope.get(None)
46+
assert val is not None, 'race condition'
47+
val.__exit__(None, None, None)
48+
_request_scope.set(None)
4749

4850

4951
def _got_request_exception(request=None, **kwargs):

sentry_sdk/utils.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,3 +339,23 @@ def to_string(value):
339339
return text_type(value)
340340
except UnicodeDecodeError:
341341
return repr(value)[1:-1]
342+
343+
344+
try:
345+
from contextvars import ContextVar
346+
except ImportError:
347+
from threading import local
348+
_nothing = object()
349+
350+
class ContextVar(object):
351+
# Super-limited impl of ContextVar
352+
353+
def __init__(self, name):
354+
self._name = name
355+
self._local = local()
356+
357+
def get(self, default):
358+
return getattr(self._local, 'value', default)
359+
360+
def set(self, value):
361+
setattr(self._local, 'value', value)

0 commit comments

Comments
 (0)