Skip to content

Commit d824f19

Browse files
codebotenc24t
andauthored
Moving contextvars and threadlocal context implementations to the API (open-telemetry#419)
While keeping the flexibility of the interface to support additional implementations of the RuntimeContext, moving the ContextVarsRuntimeContext and ThreadLocalRuntimeContext into the API ensures a useful context implementation for users without requiring additional configuration. This changes checks the version number to determine which implementation to use, unless it is overridden by the OPENTELEMETRY_CONTEXT environment variable. Removing the DefaultRuntimeContext as part of this change. Signed-off-by: Alex Boten <aboten@lightstep.com> Co-authored-by: Chris Kleinknecht <libc@google.com>
1 parent da541c8 commit d824f19

File tree

11 files changed

+99
-192
lines changed

11 files changed

+99
-192
lines changed

opentelemetry-api/setup.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,10 @@
4444
include_package_data=True,
4545
long_description=open("README.rst").read(),
4646
long_description_content_type="text/x-rst",
47-
install_requires=["typing; python_version<'3.5'"],
47+
install_requires=[
48+
"typing; python_version<'3.5'",
49+
"aiocontextvars; python_version<'3.7' and python_version>='3.5'",
50+
],
4851
extras_require={},
4952
license="Apache-2.0",
5053
package_dir={"": "src"},
@@ -58,9 +61,12 @@
5861
zip_safe=False,
5962
entry_points={
6063
"opentelemetry_context": [
61-
"default_context = "
62-
"opentelemetry.context.default_context:"
63-
"DefaultRuntimeContext",
64+
"contextvars_context = "
65+
"opentelemetry.context.contextvars_context:"
66+
"ContextVarsRuntimeContext",
67+
"threadlocal_context = "
68+
"opentelemetry.context.threadlocal_context:"
69+
"ThreadLocalRuntimeContext",
6470
]
6571
},
6672
)

opentelemetry-api/src/opentelemetry/context/__init__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import logging
1616
import typing
1717
from os import environ
18+
from sys import version_info
1819

1920
from pkg_resources import iter_entry_points
2021

@@ -84,9 +85,14 @@ def get_current() -> Context:
8485
if _RUNTIME_CONTEXT is None:
8586
# FIXME use a better implementation of a configuration manager to avoid having
8687
# to get configuration values straight from environment variables
88+
if version_info < (3, 5):
89+
# contextvars are not supported in 3.4, use thread-local storage
90+
default_context = "threadlocal_context"
91+
else:
92+
default_context = "contextvars_context"
8793

8894
configured_context = environ.get(
89-
"OPENTELEMETRY_CONTEXT", "default_context"
95+
"OPENTELEMETRY_CONTEXT", default_context
9096
) # type: str
9197
try:
9298
_RUNTIME_CONTEXT = next(

opentelemetry-sdk/src/opentelemetry/sdk/context/aiocontextvarsfix.py renamed to opentelemetry-api/src/opentelemetry/context/aiocontextvarsfix.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def _get_event_loop():
5555
asyncio._set_running_loop = asyncio.events._set_running_loop
5656

5757
# noinspection PyUnresolvedReferences
58-
import aiocontextvars # pylint: disable=unused-import,wrong-import-position # noqa # isort:skip
58+
import aiocontextvars # pylint: disable=import-error,unused-import,wrong-import-position # noqa # isort:skip
5959

6060

6161
def _run_coroutine_threadsafe(coro, loop):

opentelemetry-sdk/src/opentelemetry/sdk/context/contextvars_context.py renamed to opentelemetry-api/src/opentelemetry/context/contextvars_context.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,13 @@
1414
from contextvars import ContextVar
1515
from sys import version_info
1616

17-
from opentelemetry.context import Context
18-
from opentelemetry.context.context import RuntimeContext
17+
from opentelemetry.context.context import Context, RuntimeContext
1918

2019
if (3, 5, 3) <= version_info < (3, 7):
21-
import aiocontextvars # type: ignore # pylint:disable=unused-import
20+
import aiocontextvars # type: ignore # pylint:disable=unused-import,import-error
2221

2322
elif (3, 4) < version_info <= (3, 5, 2):
24-
import opentelemetry.sdk.context.aiocontextvarsfix # pylint:disable=unused-import
23+
import opentelemetry.context.aiocontextvarsfix # pylint:disable=unused-import
2524

2625

2726
class ContextVarsRuntimeContext(RuntimeContext):

opentelemetry-api/src/opentelemetry/context/default_context.py

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

opentelemetry-sdk/src/opentelemetry/sdk/context/threadlocal_context.py renamed to opentelemetry-api/src/opentelemetry/context/threadlocal_context.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
import threading
1616

17-
from opentelemetry.context import Context, RuntimeContext
17+
from opentelemetry.context.context import Context, RuntimeContext
1818

1919

2020
class ThreadLocalRuntimeContext(RuntimeContext):
@@ -38,7 +38,10 @@ def get_current(self) -> Context:
3838
setattr(
3939
self._current_context, self._CONTEXT_KEY, Context(),
4040
)
41-
return getattr(self._current_context, self._CONTEXT_KEY)
41+
context = getattr(
42+
self._current_context, self._CONTEXT_KEY
43+
) # type: Context
44+
return context
4245

4346

4447
__all__ = ["ThreadLocalRuntimeContext"]
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Copyright 2020, OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import unittest
16+
from unittest.mock import patch
17+
18+
from opentelemetry import context
19+
20+
try:
21+
import contextvars # pylint: disable=unused-import
22+
from opentelemetry.context.contextvars_context import (
23+
ContextVarsRuntimeContext,
24+
)
25+
except ImportError:
26+
raise unittest.SkipTest("contextvars not available")
27+
28+
29+
def do_work() -> None:
30+
context.set_current(context.set_value("say", "bar"))
31+
32+
33+
class TestContextVarsContext(unittest.TestCase):
34+
def setUp(self):
35+
self.previous_context = context.get_current()
36+
37+
def tearDown(self):
38+
context.set_current(self.previous_context)
39+
40+
@patch(
41+
"opentelemetry.context._RUNTIME_CONTEXT", ContextVarsRuntimeContext() # type: ignore
42+
)
43+
def test_context(self):
44+
self.assertIsNone(context.get_value("say"))
45+
empty = context.get_current()
46+
second = context.set_value("say", "foo")
47+
48+
self.assertEqual(context.get_value("say", context=second), "foo")
49+
50+
do_work()
51+
self.assertEqual(context.get_value("say"), "bar")
52+
third = context.get_current()
53+
54+
self.assertIsNone(context.get_value("say", context=empty))
55+
self.assertEqual(context.get_value("say", context=second), "foo")
56+
self.assertEqual(context.get_value("say", context=third), "bar")
57+
58+
@patch(
59+
"opentelemetry.context._RUNTIME_CONTEXT", ContextVarsRuntimeContext() # type: ignore
60+
)
61+
def test_set_value(self):
62+
first = context.set_value("a", "yyy")
63+
second = context.set_value("a", "zzz")
64+
third = context.set_value("a", "---", first)
65+
self.assertEqual("yyy", context.get_value("a", context=first))
66+
self.assertEqual("zzz", context.get_value("a", context=second))
67+
self.assertEqual("---", context.get_value("a", context=third))
68+
self.assertEqual(None, context.get_value("a"))

opentelemetry-sdk/tests/context/test_context.py renamed to opentelemetry-api/tests/context/test_threadlocal_context.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@
1616
from unittest.mock import patch
1717

1818
from opentelemetry import context
19-
from opentelemetry.sdk.context.threadlocal_context import (
20-
ThreadLocalRuntimeContext,
21-
)
19+
from opentelemetry.context.threadlocal_context import ThreadLocalRuntimeContext
2220

2321

2422
def do_work() -> None:
@@ -33,7 +31,7 @@ def tearDown(self):
3331
context.set_current(self.previous_context)
3432

3533
@patch(
36-
"opentelemetry.context._RUNTIME_CONTEXT", ThreadLocalRuntimeContext()
34+
"opentelemetry.context._RUNTIME_CONTEXT", ThreadLocalRuntimeContext() # type: ignore
3735
)
3836
def test_context(self):
3937
self.assertIsNone(context.get_value("say"))
@@ -51,7 +49,7 @@ def test_context(self):
5149
self.assertEqual(context.get_value("say", context=third), "bar")
5250

5351
@patch(
54-
"opentelemetry.context._RUNTIME_CONTEXT", ThreadLocalRuntimeContext()
52+
"opentelemetry.context._RUNTIME_CONTEXT", ThreadLocalRuntimeContext() # type: ignore
5553
)
5654
def test_set_value(self):
5755
first = context.set_value("a", "yyy")

opentelemetry-sdk/setup.py

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
include_package_data=True,
4545
long_description=open("README.rst").read(),
4646
long_description_content_type="text/x-rst",
47-
install_requires=["opentelemetry-api==0.4.dev0", "aiocontextvars"],
47+
install_requires=["opentelemetry-api==0.4.dev0"],
4848
extras_require={},
4949
license="Apache-2.0",
5050
package_dir={"": "src"},
@@ -56,14 +56,4 @@
5656
"/tree/master/opentelemetry-sdk"
5757
),
5858
zip_safe=False,
59-
entry_points={
60-
"opentelemetry_context": [
61-
"contextvars_context = "
62-
"opentelemetry.sdk.context.contextvars_context:"
63-
"ContextVarsRuntimeContext",
64-
"threadlocal_context = "
65-
"opentelemetry.sdk.context.threadlocal_context:"
66-
"ThreadLocalRuntimeContext",
67-
]
68-
},
6959
)

opentelemetry-sdk/tests/context/test_asyncio.py

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
try:
2727
import contextvars # pylint: disable=unused-import
28-
from opentelemetry.sdk.context.contextvars_context import (
28+
from opentelemetry.context.contextvars_context import (
2929
ContextVarsRuntimeContext,
3030
)
3131
except ImportError:
@@ -53,10 +53,6 @@ def stop_loop_when(loop, cond_func, timeout=5.0):
5353
loop.call_later(0.1, stop_loop_when, loop, cond_func, timeout)
5454

5555

56-
def do_work() -> None:
57-
context.set_current(context.set_value("say", "bar"))
58-
59-
6056
class TestAsyncio(unittest.TestCase):
6157
@asyncio.coroutine
6258
def task(self, name):
@@ -114,41 +110,3 @@ def test_with_asyncio(self):
114110
if span is expected_parent:
115111
continue
116112
self.assertEqual(span.parent, expected_parent)
117-
118-
119-
class TestContextVarsContext(unittest.TestCase):
120-
def setUp(self):
121-
self.previous_context = context.get_current()
122-
123-
def tearDown(self):
124-
context.set_current(self.previous_context)
125-
126-
@patch(
127-
"opentelemetry.context._RUNTIME_CONTEXT", ContextVarsRuntimeContext()
128-
)
129-
def test_context(self):
130-
self.assertIsNone(context.get_value("say"))
131-
empty = context.get_current()
132-
second = context.set_value("say", "foo")
133-
134-
self.assertEqual(context.get_value("say", context=second), "foo")
135-
136-
do_work()
137-
self.assertEqual(context.get_value("say"), "bar")
138-
third = context.get_current()
139-
140-
self.assertIsNone(context.get_value("say", context=empty))
141-
self.assertEqual(context.get_value("say", context=second), "foo")
142-
self.assertEqual(context.get_value("say", context=third), "bar")
143-
144-
@patch(
145-
"opentelemetry.context._RUNTIME_CONTEXT", ContextVarsRuntimeContext()
146-
)
147-
def test_set_value(self):
148-
first = context.set_value("a", "yyy")
149-
second = context.set_value("a", "zzz")
150-
third = context.set_value("a", "---", first)
151-
self.assertEqual("yyy", context.get_value("a", context=first))
152-
self.assertEqual("zzz", context.get_value("a", context=second))
153-
self.assertEqual("---", context.get_value("a", context=third))
154-
self.assertEqual(None, context.get_value("a"))

0 commit comments

Comments
 (0)