Repository: open-telemetry/opentelemetry-python

Issue:
Consider using ContextManager for Context
Follow up https://github.com/open-telemetry/opentelemetry-python/pull/181#discussion_r329248988. Post alpha v0.1 release.


Discussion:

Resolving patch:
diff --git a/opentelemetry-api/src/opentelemetry/context/base_context.py b/opentelemetry-api/src/opentelemetry/context/base_context.py
--- a/opentelemetry-api/src/opentelemetry/context/base_context.py
+++ b/opentelemetry-api/src/opentelemetry/context/base_context.py
@@ -14,6 +14,7 @@
 
 import threading
 import typing
+from contextlib import contextmanager
 
 
 def wrap_callable(target: "object") -> typing.Callable[[], object]:
@@ -99,6 +100,15 @@ def __getitem__(self, name: str) -> "object":
     def __setitem__(self, name: str, value: "object") -> None:
         self.__setattr__(name, value)
 
+    @contextmanager  # type: ignore
+    def use(self, **kwargs: typing.Dict[str, object]) -> typing.Iterator[None]:
+        snapshot = {key: self[key] for key in kwargs}
+        for key in kwargs:
+            self[key] = kwargs[key]
+        yield
+        for key in kwargs:
+            self[key] = snapshot[key]
+
     def with_current_context(
         self, func: typing.Callable[..., "object"]
     ) -> typing.Callable[..., "object"]:
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py
--- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py
@@ -73,15 +73,12 @@ def on_start(self, span: Span) -> None:
         pass
 
     def on_end(self, span: Span) -> None:
-        suppress_instrumentation = Context.suppress_instrumentation
-        try:
-            Context.suppress_instrumentation = True
-            self.span_exporter.export((span,))
-        # pylint: disable=broad-except
-        except Exception as exc:
-            logger.warning("Exception while exporting data: %s", exc)
-        finally:
-            Context.suppress_instrumentation = suppress_instrumentation
+        with Context.use(suppress_instrumentation=True):
+            try:
+                self.span_exporter.export((span,))
+            # pylint: disable=broad-except
+            except Exception as exc:
+                logger.warning("Exception while exporting data: %s", exc)
 
     def shutdown(self) -> None:
         self.span_exporter.shutdown()
@@ -185,16 +182,16 @@ def export(self) -> None:
         while idx < self.max_export_batch_size and self.queue:
             self.spans_list[idx] = self.queue.pop()
             idx += 1
-        suppress_instrumentation = Context.suppress_instrumentation
-        try:
-            Context.suppress_instrumentation = True
-            # Ignore type b/c the Optional[None]+slicing is too "clever" for mypy
-            self.span_exporter.export(self.spans_list[:idx])  # type: ignore
-        # pylint: disable=broad-except
-        except Exception:
-            logger.exception("Exception while exporting data.")
-        finally:
-            Context.suppress_instrumentation = suppress_instrumentation
+        with Context.use(suppress_instrumentation=True):
+            try:
+                # Ignore type b/c the Optional[None]+slicing is too "clever"
+                # for mypy
+                self.span_exporter.export(
+                    self.spans_list[:idx]
+                )  # type: ignore
+            # pylint: disable=broad-except
+            except Exception:
+                logger.exception("Exception while exporting data.")
 
         # clean up list
         for index in range(idx):


Test patch: