From 00f075acb2be3b2d2677fc1e47bf5395658c7569 Mon Sep 17 00:00:00 2001 From: Taegyun Kim Date: Thu, 19 Dec 2024 17:40:14 -0500 Subject: [PATCH 1/4] chore: remove vendored contextvars module --- ddtrace/internal/coverage/code.py | 2 +- ddtrace/vendor/__init__.py | 16 +-- ddtrace/vendor/contextvars/__init__.py | 160 ------------------------- 3 files changed, 2 insertions(+), 176 deletions(-) delete mode 100644 ddtrace/vendor/contextvars/__init__.py diff --git a/ddtrace/internal/coverage/code.py b/ddtrace/internal/coverage/code.py index b6f5d379661..a33e262f293 100644 --- a/ddtrace/internal/coverage/code.py +++ b/ddtrace/internal/coverage/code.py @@ -1,4 +1,5 @@ from collections import defaultdict +from contextvars import ContextVar from copy import deepcopy from inspect import getmodule import os @@ -19,7 +20,6 @@ from ddtrace.internal.packages import purelib_path from ddtrace.internal.packages import stdlib_path from ddtrace.internal.test_visibility.coverage_lines import CoverageLines -from ddtrace.vendor.contextvars import ContextVar log = get_logger(__name__) diff --git a/ddtrace/vendor/__init__.py b/ddtrace/vendor/__init__.py index 1b9596e82da..7e09b6148ed 100644 --- a/ddtrace/vendor/__init__.py +++ b/ddtrace/vendor/__init__.py @@ -60,20 +60,6 @@ License: BSD 3 -contextvars -------------- - -Source: https://github.com/MagicStack/contextvars -Version: 2.4 -License: Apache License 2.0 - -Notes: - - removal of metaclass usage - - formatting - - use a plain old dict instead of immutables.Map - - removal of `*` syntax - - sqlcommenter ------------ @@ -107,7 +93,7 @@ yacc.py and lex.py files here. Didn't copy: cpp.py, ctokens.py, ygen.py (didn't see them used) - + jsonpath-ng --------- diff --git a/ddtrace/vendor/contextvars/__init__.py b/ddtrace/vendor/contextvars/__init__.py deleted file mode 100644 index f4c5c62dcc6..00000000000 --- a/ddtrace/vendor/contextvars/__init__.py +++ /dev/null @@ -1,160 +0,0 @@ -import threading - -try: - from collections.abc import Mapping -except ImportError: - from collections import Mapping - - -__all__ = ("ContextVar", "Context", "Token", "copy_context") - - -_NO_DEFAULT = object() - - -class Context(Mapping): - def __init__(self): - self._data = {} - self._prev_context = None - - def run(self, callable, *args, **kwargs): - if self._prev_context is not None: - raise RuntimeError("cannot enter context: {} is already entered".format(self)) - - self._prev_context = _get_context() - try: - _set_context(self) - return callable(*args, **kwargs) - finally: - _set_context(self._prev_context) - self._prev_context = None - - def copy(self): - new = Context() - new._data = self._data.copy() - return new - - def __getitem__(self, var): - if not isinstance(var, ContextVar): - raise TypeError("a ContextVar key was expected, got {!r}".format(var)) - return self._data[var] - - def __contains__(self, var): - if not isinstance(var, ContextVar): - raise TypeError("a ContextVar key was expected, got {!r}".format(var)) - return var in self._data - - def __len__(self): - return len(self._data) - - def __iter__(self): - return iter(self._data) - - -class ContextVar(object): - def __init__(self, name, default=_NO_DEFAULT): - if not isinstance(name, str): - raise TypeError("context variable name must be a str") - self._name = name - self._default = default - - @property - def name(self): - return self._name - - def get(self, default=_NO_DEFAULT): - ctx = _get_context() - try: - return ctx[self] - except KeyError: - pass - - if default is not _NO_DEFAULT: - return default - - if self._default is not _NO_DEFAULT: - return self._default - - raise LookupError - - def set(self, value): - ctx = _get_context() - data = ctx._data - try: - old_value = data[self] - except KeyError: - old_value = Token.MISSING - - updated_data = data.copy() - updated_data[self] = value - ctx._data = updated_data - return Token(ctx, self, old_value) - - def reset(self, token): - if token._used: - raise RuntimeError("Token has already been used once") - - if token._var is not self: - raise ValueError("Token was created by a different ContextVar") - - if token._context is not _get_context(): - raise ValueError("Token was created in a different Context") - - ctx = token._context - if token._old_value is Token.MISSING: - del ctx._data[token._var] - else: - ctx._data[token._var] = token._old_value - - token._used = True - - def __repr__(self): - r = "".format(id(self)) - - -class Token(object): - - MISSING = object() - - def __init__(self, context, var, old_value): - self._context = context - self._var = var - self._old_value = old_value - self._used = False - - @property - def var(self): - return self._var - - @property - def old_value(self): - return self._old_value - - def __repr__(self): - r = " Date: Wed, 8 Jan 2025 15:54:00 -0500 Subject: [PATCH 2/4] fix typing and default value --- ddtrace/internal/coverage/code.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ddtrace/internal/coverage/code.py b/ddtrace/internal/coverage/code.py index a33e262f293..5227e08fa42 100644 --- a/ddtrace/internal/coverage/code.py +++ b/ddtrace/internal/coverage/code.py @@ -26,7 +26,7 @@ _original_exec = exec -ctx_covered = ContextVar("ctx_covered", default=None) +ctx_covered: ContextVar[t.List[t.DefaultDict[str, CoverageLines]]] = ContextVar("ctx_covered", default=[]) ctx_is_import_coverage = ContextVar("ctx_is_import_coverage", default=False) ctx_coverage_enabled = ContextVar("ctx_coverage_enabled", default=False) From d19a535d72d4405c786de07c27889dee1efc7411 Mon Sep 17 00:00:00 2001 From: Taegyun Kim Date: Wed, 8 Jan 2025 16:06:08 -0500 Subject: [PATCH 3/4] remove test --- tests/vendor/test_contextvars.py | 348 ------------------------------- 1 file changed, 348 deletions(-) delete mode 100644 tests/vendor/test_contextvars.py diff --git a/tests/vendor/test_contextvars.py b/tests/vendor/test_contextvars.py deleted file mode 100644 index 84d86113952..00000000000 --- a/tests/vendor/test_contextvars.py +++ /dev/null @@ -1,348 +0,0 @@ -# Tests are copied from cpython/Lib/test/test_context.py -# License: PSFL -# Copyright: 2018 Python Software Foundation - - -import concurrent.futures -import functools -import random -import time -import unittest - -import pytest - -from ddtrace.vendor import contextvars - - -def isolated_context(func): - """Needed to make reftracking test mode work.""" - - @functools.wraps(func) - def wrapper(*args, **kwargs): - ctx = contextvars.Context() - return ctx.run(func, *args, **kwargs) - - return wrapper - - -class ContextTest(unittest.TestCase): - def test_context_var_new_1(self): - with self.assertRaises(TypeError): - contextvars.ContextVar() - - with pytest.raises(TypeError) as e: - contextvars.ContextVar(1) - assert "must be a str" in str(e.value) - - c = contextvars.ContextVar("a") - self.assertNotEqual(hash(c), hash("a")) - - @isolated_context - def test_context_var_repr_1(self): - c = contextvars.ContextVar("a") - self.assertIn("a", repr(c)) - - c = contextvars.ContextVar("a", default=123) - self.assertIn("123", repr(c)) - - lst = [] - c = contextvars.ContextVar("a", default=lst) - lst.append(c) - self.assertIn("...", repr(c)) - self.assertIn("...", repr(lst)) - - t = c.set(1) - self.assertIn(repr(c), repr(t)) - self.assertNotIn(" used ", repr(t)) - c.reset(t) - self.assertIn(" used ", repr(t)) - - def test_context_new_1(self): - with self.assertRaises(TypeError): - contextvars.Context(1) - with self.assertRaises(TypeError): - contextvars.Context(1, a=1) - with self.assertRaises(TypeError): - contextvars.Context(a=1) - contextvars.Context(**{}) - - def test_context_typerrors_1(self): - ctx = contextvars.Context() - - with pytest.raises(TypeError) as e: - ctx[1] - assert "ContextVar key was expected" in str(e.value) - - with pytest.raises(TypeError): - assert 1 in ctx - assert "ContextVar key was expected" in str(e.value) - - with pytest.raises(TypeError) as e: - ctx.get(1) - assert "ContextVar key was expected" in str(e.value) - - def test_context_get_context_1(self): - ctx = contextvars.copy_context() - self.assertIsInstance(ctx, contextvars.Context) - - def test_context_run_1(self): - ctx = contextvars.Context() - - with pytest.raises(TypeError): - ctx.run() - - def test_context_run_2(self): - ctx = contextvars.Context() - - def func(*args, **kwargs): - kwargs["spam"] = "foo" - args += ("bar",) - return args, kwargs - - for f in (func, functools.partial(func)): - # partial doesn't support FASTCALL - - self.assertEqual(ctx.run(f), (("bar",), {"spam": "foo"})) - self.assertEqual(ctx.run(f, 1), ((1, "bar"), {"spam": "foo"})) - - self.assertEqual(ctx.run(f, a=2), (("bar",), {"a": 2, "spam": "foo"})) - - self.assertEqual(ctx.run(f, 11, a=2), ((11, "bar"), {"a": 2, "spam": "foo"})) - - a = {} - self.assertEqual(ctx.run(f, 11, **a), ((11, "bar"), {"spam": "foo"})) - self.assertEqual(a, {}) - - def test_context_run_3(self): - ctx = contextvars.Context() - - def func(*args, **kwargs): - 1 / 0 - - with self.assertRaises(ZeroDivisionError): - ctx.run(func) - with self.assertRaises(ZeroDivisionError): - ctx.run(func, 1, 2) - with self.assertRaises(ZeroDivisionError): - ctx.run(func, 1, 2, a=123) - - @isolated_context - def test_context_run_4(self): - ctx1 = contextvars.Context() - ctx2 = contextvars.Context() - var = contextvars.ContextVar("var") - - def func2(): - self.assertIsNone(var.get(None)) - - def func1(): - self.assertIsNone(var.get(None)) - var.set("spam") - ctx2.run(func2) - self.assertEqual(var.get(None), "spam") - - cur = contextvars.copy_context() - self.assertEqual(len(cur), 1) - self.assertEqual(cur[var], "spam") - return cur - - returned_ctx = ctx1.run(func1) - self.assertEqual(ctx1, returned_ctx) - self.assertEqual(returned_ctx[var], "spam") - self.assertIn(var, returned_ctx) - - def test_context_run_5(self): - ctx = contextvars.Context() - var = contextvars.ContextVar("var") - - def func(): - self.assertIsNone(var.get(None)) - var.set("spam") - 1 / 0 - - with self.assertRaises(ZeroDivisionError): - ctx.run(func) - - self.assertIsNone(var.get(None)) - - def test_context_run_6(self): - ctx = contextvars.Context() - c = contextvars.ContextVar("a", default=0) - - def fun(): - self.assertEqual(c.get(), 0) - self.assertIsNone(ctx.get(c)) - - c.set(42) - self.assertEqual(c.get(), 42) - self.assertEqual(ctx.get(c), 42) - - ctx.run(fun) - - def test_context_run_7(self): - ctx = contextvars.Context() - - def fun(): - with pytest.raises(RuntimeError) as e: - ctx.run(fun) - assert "is already entered" in str(e.value) - - ctx.run(fun) - - @isolated_context - def test_context_getset_1(self): - c = contextvars.ContextVar("c") - with self.assertRaises(LookupError): - c.get() - - self.assertIsNone(c.get(None)) - - t0 = c.set(42) - self.assertEqual(c.get(), 42) - self.assertEqual(c.get(None), 42) - self.assertIs(t0.old_value, t0.MISSING) - self.assertIs(t0.old_value, contextvars.Token.MISSING) - self.assertIs(t0.var, c) - - t = c.set("spam") - self.assertEqual(c.get(), "spam") - self.assertEqual(c.get(None), "spam") - self.assertEqual(t.old_value, 42) - c.reset(t) - - self.assertEqual(c.get(), 42) - self.assertEqual(c.get(None), 42) - - c.set("spam2") - with pytest.raises(RuntimeError) as e: - c.reset(t) - assert "has already been used" in str(e.value) - self.assertEqual(c.get(), "spam2") - - ctx1 = contextvars.copy_context() - self.assertIn(c, ctx1) - - c.reset(t0) - with pytest.raises(RuntimeError): - c.reset(t0) - assert "has already been used" in str(e.value) - self.assertIsNone(c.get(None)) - - self.assertIn(c, ctx1) - self.assertEqual(ctx1[c], "spam2") - self.assertEqual(ctx1.get(c, "aa"), "spam2") - self.assertEqual(len(ctx1), 1) - self.assertEqual(list(ctx1.items()), [(c, "spam2")]) - self.assertEqual(list(ctx1.values()), ["spam2"]) - self.assertEqual(list(ctx1.keys()), [c]) - self.assertEqual(list(ctx1), [c]) - - ctx2 = contextvars.copy_context() - self.assertNotIn(c, ctx2) - with self.assertRaises(KeyError): - ctx2[c] - self.assertEqual(ctx2.get(c, "aa"), "aa") - self.assertEqual(len(ctx2), 0) - self.assertEqual(list(ctx2), []) - - @isolated_context - def test_context_getset_2(self): - v1 = contextvars.ContextVar("v1") - v2 = contextvars.ContextVar("v2") - - t1 = v1.set(42) - with pytest.raises(ValueError) as e: - v2.reset(t1) - assert "by a different" in str(e.value) - - @isolated_context - def test_context_getset_3(self): - c = contextvars.ContextVar("c", default=42) - ctx = contextvars.Context() - - def fun(): - self.assertEqual(c.get(), 42) - with self.assertRaises(KeyError): - ctx[c] - self.assertIsNone(ctx.get(c)) - self.assertEqual(ctx.get(c, "spam"), "spam") - self.assertNotIn(c, ctx) - self.assertEqual(list(ctx.keys()), []) - - t = c.set(1) - self.assertEqual(list(ctx.keys()), [c]) - self.assertEqual(ctx[c], 1) - - c.reset(t) - self.assertEqual(list(ctx.keys()), []) - with self.assertRaises(KeyError): - ctx[c] - - ctx.run(fun) - - @isolated_context - def test_context_getset_4(self): - c = contextvars.ContextVar("c", default=42) - ctx = contextvars.Context() - - tok = ctx.run(c.set, 1) - - with pytest.raises(ValueError) as e: - c.reset(tok) - assert "different Context" in str(e.value) - - @isolated_context - def test_context_getset_5(self): - c = contextvars.ContextVar("c", default=42) - c.set([]) - - def fun(): - c.set([]) - c.get().append(42) - self.assertEqual(c.get(), [42]) - - contextvars.copy_context().run(fun) - self.assertEqual(c.get(), []) - - def test_context_copy_1(self): - ctx1 = contextvars.Context() - c = contextvars.ContextVar("c", default=42) - - def ctx1_fun(): - c.set(10) - - ctx2 = ctx1.copy() - self.assertEqual(ctx2[c], 10) - - c.set(20) - self.assertEqual(ctx1[c], 20) - self.assertEqual(ctx2[c], 10) - - ctx2.run(ctx2_fun) - self.assertEqual(ctx1[c], 20) - self.assertEqual(ctx2[c], 30) - - def ctx2_fun(): - self.assertEqual(c.get(), 10) - c.set(30) - self.assertEqual(c.get(), 30) - - ctx1.run(ctx1_fun) - - @isolated_context - def test_context_threads_1(self): - cvar = contextvars.ContextVar("cvar") - - def sub(num): - for i in range(10): - cvar.set(num + i) - time.sleep(random.uniform(0.001, 0.05)) - self.assertEqual(cvar.get(), num + i) - return num - - tp = concurrent.futures.ThreadPoolExecutor(max_workers=10) - try: - results = list(tp.map(sub, range(10))) - finally: - tp.shutdown() - self.assertEqual(results, list(range(10))) From 498a2b04ff8b2d1b654db424855e2ea2894aba74 Mon Sep 17 00:00:00 2001 From: Taegyun Kim Date: Wed, 8 Jan 2025 16:08:34 -0500 Subject: [PATCH 4/4] remove unused constant --- ddtrace/internal/compat.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/ddtrace/internal/compat.py b/ddtrace/internal/compat.py index 7f00043f049..94494723903 100644 --- a/ddtrace/internal/compat.py +++ b/ddtrace/internal/compat.py @@ -185,9 +185,6 @@ def get_connection_response( return conn.getresponse() -CONTEXTVARS_IS_AVAILABLE = True - - try: from collections.abc import Iterable # noqa:F401 except ImportError: