Repository: open-telemetry/opentelemetry-python

Issue:
get_correlations return value should be immutable
According to the [spec](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/correlationcontext/api.md#get-correlations):

> the returned value can be either an immutable collection or an immutable iterator

Currently, we return a `dict` ([link](https://github.com/open-telemetry/opentelemetry-python/blob/3cae0775ba12a2f7b4214b8b8c062c5e81002a19/opentelemetry-api/src/opentelemetry/correlationcontext/__init__.py#L34-L37)): 
```python
correlations = get_value(_CORRELATION_CONTEXT_KEY, context=context)
if isinstance(correlations, dict):
    return correlations.copy()
return {}
```

This was mentioned in the PR but not definitively addressed https://github.com/open-telemetry/opentelemetry-python/pull/471#discussion_r392369812, so I thought it might be worth bringing up again before GA.


Discussion:

Resolving patch:
diff --git a/opentelemetry-api/src/opentelemetry/correlationcontext/__init__.py b/opentelemetry-api/src/opentelemetry/correlationcontext/__init__.py
--- a/opentelemetry-api/src/opentelemetry/correlationcontext/__init__.py
+++ b/opentelemetry-api/src/opentelemetry/correlationcontext/__init__.py
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 import typing
+from types import MappingProxyType
 
 from opentelemetry.context import get_value, set_value
 from opentelemetry.context.context import Context
@@ -22,7 +23,7 @@
 
 def get_correlations(
     context: typing.Optional[Context] = None,
-) -> typing.Dict[str, object]:
+) -> typing.Mapping[str, object]:
     """Returns the name/value pairs in the CorrelationContext
 
     Args:
@@ -33,8 +34,8 @@ def get_correlations(
     """
     correlations = get_value(_CORRELATION_CONTEXT_KEY, context=context)
     if isinstance(correlations, dict):
-        return correlations.copy()
-    return {}
+        return MappingProxyType(correlations.copy())
+    return MappingProxyType({})
 
 
 def get_correlation(
@@ -67,7 +68,7 @@ def set_correlation(
     Returns:
         A Context with the value updated
     """
-    correlations = get_correlations(context=context)
+    correlations = dict(get_correlations(context=context))
     correlations[name] = value
     return set_value(_CORRELATION_CONTEXT_KEY, correlations, context=context)
 
@@ -84,7 +85,7 @@ def remove_correlation(
     Returns:
         A Context with the name/value removed
     """
-    correlations = get_correlations(context=context)
+    correlations = dict(get_correlations(context=context))
     correlations.pop(name, None)
 
     return set_value(_CORRELATION_CONTEXT_KEY, correlations, context=context)
diff --git a/opentelemetry-api/src/opentelemetry/correlationcontext/propagation/__init__.py b/opentelemetry-api/src/opentelemetry/correlationcontext/propagation/__init__.py
--- a/opentelemetry-api/src/opentelemetry/correlationcontext/propagation/__init__.py
+++ b/opentelemetry-api/src/opentelemetry/correlationcontext/propagation/__init__.py
@@ -94,7 +94,7 @@ def inject(
         )
 
 
-def _format_correlations(correlations: typing.Dict[str, object]) -> str:
+def _format_correlations(correlations: typing.Mapping[str, object]) -> str:
     return ",".join(
         key + "=" + urllib.parse.quote_plus(str(value))
         for key, value in correlations.items()


Test patch:
diff --git a/opentelemetry-api/tests/correlationcontext/test_correlation_context.py b/opentelemetry-api/tests/correlationcontext/test_correlation_context.py
--- a/opentelemetry-api/tests/correlationcontext/test_correlation_context.py
+++ b/opentelemetry-api/tests/correlationcontext/test_correlation_context.py
@@ -48,7 +48,8 @@ def test_modifying_correlations(self):
         ctx = cctx.set_correlation("test", "value")
         self.assertEqual(cctx.get_correlation("test", context=ctx), "value")
         correlations = cctx.get_correlations(context=ctx)
-        correlations["test"] = "mess-this-up"
+        with self.assertRaises(TypeError):
+            correlations["test"] = "mess-this-up"
         self.assertEqual(cctx.get_correlation("test", context=ctx), "value")
 
     def test_remove_correlations(self):