Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(5) Add reset() to thread local ContextVar and no-op copy_context() #2570

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
50 changes: 38 additions & 12 deletions sentry_sdk/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import logging
import math
import os
import random
import re
import subprocess
import sys
Expand Down Expand Up @@ -1248,24 +1249,49 @@ def _make_threadlocal_contextvars(local):
class ContextVar(object):
# Super-limited impl of ContextVar

def __init__(self, name):
# type: (str) -> None
def __init__(self, name, default=None):
# type: (str, Any) -> None
self._name = name
self._default = default
self._local = local()
self._original_local = local()

def get(self, default):
def get(self, default=None):
# type: (Any) -> Any
return getattr(self._local, "value", default)
return getattr(self._local, "value", default or self._default)

def set(self, value):
# type: (Any) -> None
# type: (Any) -> Any
token = str(random.getrandbits(64))
original_value = self.get()
setattr(self._original_local, token, original_value)
self._local.value = value
return token

def reset(self, token):
# type: (Any) -> None
self._local.value = getattr(self._original_local, token)
del self._original_local[token]

return ContextVar


def _make_noop_copy_context():
# type: () -> Callable[[], Any]
class NoOpContext:
def run(self, func, *args, **kwargs):
# type: (Callable[..., Any], *Any, **Any) -> Any
return func(*args, **kwargs)

def copy_context():
# type: () -> NoOpContext
return NoOpContext()

return copy_context


def _get_contextvars():
# type: () -> Tuple[bool, type]
# type: () -> Tuple[bool, type, Callable[[], Any]]
"""
Figure out the "right" contextvars installation to use. Returns a
`contextvars.ContextVar`-like class with a limited API.
Expand All @@ -1281,28 +1307,28 @@ def _get_contextvars():
# `aiocontextvars` is absolutely required for functional
# contextvars on Python 3.6.
try:
from aiocontextvars import ContextVar
from aiocontextvars import ContextVar, copy_context

return True, ContextVar
return True, ContextVar, copy_context
except ImportError:
pass
else:
# On Python 3.7 contextvars are functional.
try:
from contextvars import ContextVar
from contextvars import ContextVar, copy_context

return True, ContextVar
return True, ContextVar, copy_context
except ImportError:
pass

# Fall back to basic thread-local usage.

from threading import local

return False, _make_threadlocal_contextvars(local)
return False, _make_threadlocal_contextvars(local), _make_noop_copy_context()
Comment on lines 1309 to +1328
Copy link

@50-Course 50-Course Dec 11, 2023

Choose a reason for hiding this comment

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

Looks good to me



HAS_REAL_CONTEXTVARS, ContextVar = _get_contextvars()
HAS_REAL_CONTEXTVARS, ContextVar, copy_context = _get_contextvars()

CONTEXTVARS_ERROR_MESSAGE = """

Expand Down
2 changes: 1 addition & 1 deletion tests/utils/test_contextvars.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def test_leaks(maybe_monkeypatched_threading):

from sentry_sdk import utils

_, ContextVar = utils._get_contextvars() # noqa: N806
_, ContextVar, _ = utils._get_contextvars() # noqa: N806

ts = []

Expand Down
Loading