We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
"Tracing Across Processes" example crashes
<!--
Thanks for taking the time for reporting an issue!

Before reporting an issue on dd-trace-py, please be sure to provide all
necessary information.

If you're hitting a bug, make sure that you're using the latest version of this
library.
-->

### Summary of problem

Hello, I cannot get "tracing across processes" to work. Python crashes while trying to pickle the tracer context RLock. The minimal reproduction example is [the example from the ddtrace advanced usage docs](https://ddtrace.readthedocs.io/en/stable/advanced_usage.html#tracing-across-processes). I also cannot get this to work with any prior ddtrace version.

### Which version of dd-trace-py are you using?

ddtrace: 1.5.3
python: 3.9.13

### How can we reproduce your problem?

Run the `Tracing Across Processes` example [from the docs](https://ddtrace.readthedocs.io/en/stable/advanced_usage.html#tracing-across-processes)

```python
from multiprocessing import Process
import time
from ddtrace import tracer

def _target(ctx):
    tracer.context_provider.activate(ctx)
    with tracer.trace("proc"):
        time.sleep(1)
    tracer.shutdown()

with tracer.trace("work"):
    proc = Process(target=_target, args=(tracer.current_trace_context(),))
    proc.start()
    time.sleep(1)
    proc.join()
```

### What is the result that you get?

```
Traceback (most recent call last):
  File "/Users/jboulanger/Library/Application Support/JetBrains/PyCharm2021.2/scratches/scratch.py", line 15, in <module>
    proc.start()
  File "/usr/local/Cellar/python@3.9/3.9.13_3/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/process.py", line 121, in start
    self._popen = self._Popen(self)
  File "/usr/local/Cellar/python@3.9/3.9.13_3/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/context.py", line 224, in _Popen
    return _default_context.get_context().Process._Popen(process_obj)
  File "/usr/local/Cellar/python@3.9/3.9.13_3/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/context.py", line 284, in _Popen
    return Popen(process_obj)
  File "/usr/local/Cellar/python@3.9/3.9.13_3/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/popen_spawn_posix.py", line 32, in __init__
    super().__init__(process_obj)
  File "/usr/local/Cellar/python@3.9/3.9.13_3/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/popen_fork.py", line 19, in __init__
    self._launch(process_obj)
  File "/usr/local/Cellar/python@3.9/3.9.13_3/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/popen_spawn_posix.py", line 47, in _launch
    reduction.dump(process_obj, fp)
  File "/usr/local/Cellar/python@3.9/3.9.13_3/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/reduction.py", line 60, in dump
    ForkingPickler(file, protocol).dump(obj)
TypeError: cannot pickle '_thread.RLock' object
Process finished with exit code 1
```

### What is the result that you expected?

I expected this to complete without crashing

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `ddtrace/context.py`
Content:
```
1 import base64
2 import threading
3 from typing import Any
4 from typing import Optional
5 from typing import TYPE_CHECKING
6 from typing import Text
7 
8 from .constants import ORIGIN_KEY
9 from .constants import SAMPLING_PRIORITY_KEY
10 from .constants import USER_ID_KEY
11 from .internal.compat import NumericType
12 from .internal.compat import PY2
13 from .internal.logger import get_logger
14 
15 
16 if TYPE_CHECKING:  # pragma: no cover
17     from .span import Span
18     from .span import _MetaDictType
19     from .span import _MetricDictType
20 
21 log = get_logger(__name__)
22 
23 
24 class Context(object):
25     """Represents the state required to propagate a trace across execution
26     boundaries.
27     """
28 
29     __slots__ = [
30         "trace_id",
31         "span_id",
32         "_lock",
33         "_meta",
34         "_metrics",
35     ]
36 
37     def __init__(
38         self,
39         trace_id=None,  # type: Optional[int]
40         span_id=None,  # type: Optional[int]
41         dd_origin=None,  # type: Optional[str]
42         sampling_priority=None,  # type: Optional[float]
43         meta=None,  # type: Optional[_MetaDictType]
44         metrics=None,  # type: Optional[_MetricDictType]
45         lock=None,  # type: Optional[threading.RLock]
46     ):
47         self._meta = meta if meta is not None else {}  # type: _MetaDictType
48         self._metrics = metrics if metrics is not None else {}  # type: _MetricDictType
49 
50         self.trace_id = trace_id  # type: Optional[int]
51         self.span_id = span_id  # type: Optional[int]
52 
53         if dd_origin is not None:
54             self._meta[ORIGIN_KEY] = dd_origin
55         if sampling_priority is not None:
56             self._metrics[SAMPLING_PRIORITY_KEY] = sampling_priority
57 
58         if lock is not None:
59             self._lock = lock
60         else:
61             # DEV: A `forksafe.RLock` is not necessary here since Contexts
62             # are recreated by the tracer after fork
63             # https://github.com/DataDog/dd-trace-py/blob/a1932e8ddb704d259ea8a3188d30bf542f59fd8d/ddtrace/tracer.py#L489-L508
64             self._lock = threading.RLock()
65 
66     def _with_span(self, span):
67         # type: (Span) -> Context
68         """Return a shallow copy of the context with the given span."""
69         return self.__class__(
70             trace_id=span.trace_id, span_id=span.span_id, meta=self._meta, metrics=self._metrics, lock=self._lock
71         )
72 
73     def _update_tags(self, span):
74         # type: (Span) -> None
75         with self._lock:
76             for tag in self._meta:
77                 span._meta.setdefault(tag, self._meta[tag])
78             for metric in self._metrics:
79                 span._metrics.setdefault(metric, self._metrics[metric])
80 
81     @property
82     def sampling_priority(self):
83         # type: () -> Optional[NumericType]
84         """Return the context sampling priority for the trace."""
85         return self._metrics.get(SAMPLING_PRIORITY_KEY)
86 
87     @sampling_priority.setter
88     def sampling_priority(self, value):
89         # type: (Optional[NumericType]) -> None
90         with self._lock:
91             if value is None:
92                 if SAMPLING_PRIORITY_KEY in self._metrics:
93                     del self._metrics[SAMPLING_PRIORITY_KEY]
94                 return
95             self._metrics[SAMPLING_PRIORITY_KEY] = value
96 
97     @property
98     def _traceparent(self):
99         # type: () -> str
100         if self.trace_id is None or self.span_id is None:
101             return ""
102 
103         sampled = 1 if self.sampling_priority and self.sampling_priority > 0 else 0
104         return "00-{:032x}-{:016x}-{:02x}".format(self.trace_id, self.span_id, sampled)
105 
106     @property
107     def dd_origin(self):
108         # type: () -> Optional[Text]
109         """Get the origin of the trace."""
110         return self._meta.get(ORIGIN_KEY)
111 
112     @dd_origin.setter
113     def dd_origin(self, value):
114         # type: (Optional[Text]) -> None
115         """Set the origin of the trace."""
116         with self._lock:
117             if value is None:
118                 if ORIGIN_KEY in self._meta:
119                     del self._meta[ORIGIN_KEY]
120                 return
121             self._meta[ORIGIN_KEY] = value
122 
123     @property
124     def dd_user_id(self):
125         # type: () -> Optional[Text]
126         """Get the user ID of the trace."""
127         user_id = self._meta.get(USER_ID_KEY)
128         if user_id:
129             if not PY2:
130                 return str(base64.b64decode(user_id), encoding="utf-8")
131             else:
132                 return str(base64.b64decode(user_id))
133         return None
134 
135     @dd_user_id.setter
136     def dd_user_id(self, value):
137         # type: (Optional[Text]) -> None
138         """Set the user ID of the trace."""
139         with self._lock:
140             if value is None:
141                 if USER_ID_KEY in self._meta:
142                     del self._meta[USER_ID_KEY]
143                 return
144             if not PY2:
145                 value = str(base64.b64encode(bytes(value, encoding="utf-8")), encoding="utf-8")
146             else:
147                 value = str(base64.b64encode(bytes(value)))
148             self._meta[USER_ID_KEY] = value
149 
150     def __eq__(self, other):
151         # type: (Any) -> bool
152         if isinstance(other, Context):
153             with self._lock:
154                 return (
155                     self.trace_id == other.trace_id
156                     and self.span_id == other.span_id
157                     and self._meta == other._meta
158                     and self._metrics == other._metrics
159                 )
160         return False
161 
162     def __repr__(self):
163         # type: () -> str
164         return "Context(trace_id=%s, span_id=%s, _meta=%s, _metrics=%s)" % (
165             self.trace_id,
166             self.span_id,
167             self._meta,
168             self._metrics,
169         )
170 
171     __str__ = __repr__
172
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/ddtrace/context.py b/ddtrace/context.py
--- a/ddtrace/context.py
+++ b/ddtrace/context.py
@@ -14,10 +14,20 @@
 
 
 if TYPE_CHECKING:  # pragma: no cover
+    from typing import Tuple
+
     from .span import Span
     from .span import _MetaDictType
     from .span import _MetricDictType
 
+    _ContextState = Tuple[
+        Optional[int],  # trace_id
+        Optional[int],  # span_id
+        _MetaDictType,  # _meta
+        _MetricDictType,  # _metrics
+    ]
+
+
 log = get_logger(__name__)
 
 
@@ -63,6 +73,22 @@
             # https://github.com/DataDog/dd-trace-py/blob/a1932e8ddb704d259ea8a3188d30bf542f59fd8d/ddtrace/tracer.py#L489-L508
             self._lock = threading.RLock()
 
+    def __getstate__(self):
+        # type: () -> _ContextState
+        return (
+            self.trace_id,
+            self.span_id,
+            self._meta,
+            self._metrics,
+            # Note: self._lock is not serializable
+        )
+
+    def __setstate__(self, state):
+        # type: (_ContextState) -> None
+        self.trace_id, self.span_id, self._meta, self._metrics = state
+        # We cannot serialize and lock, so we must recreate it unless we already have one
+        self._lock = threading.RLock()
+
     def _with_span(self, span):
         # type: (Span) -> Context
         """Return a shallow copy of the context with the given span."""

{"golden_diff": "diff --git a/ddtrace/context.py b/ddtrace/context.py\n--- a/ddtrace/context.py\n+++ b/ddtrace/context.py\n@@ -14,10 +14,20 @@\n \n \n if TYPE_CHECKING:  # pragma: no cover\n+    from typing import Tuple\n+\n     from .span import Span\n     from .span import _MetaDictType\n     from .span import _MetricDictType\n \n+    _ContextState = Tuple[\n+        Optional[int],  # trace_id\n+        Optional[int],  # span_id\n+        _MetaDictType,  # _meta\n+        _MetricDictType,  # _metrics\n+    ]\n+\n+\n log = get_logger(__name__)\n \n \n@@ -63,6 +73,22 @@\n             # https://github.com/DataDog/dd-trace-py/blob/a1932e8ddb704d259ea8a3188d30bf542f59fd8d/ddtrace/tracer.py#L489-L508\n             self._lock = threading.RLock()\n \n+    def __getstate__(self):\n+        # type: () -> _ContextState\n+        return (\n+            self.trace_id,\n+            self.span_id,\n+            self._meta,\n+            self._metrics,\n+            # Note: self._lock is not serializable\n+        )\n+\n+    def __setstate__(self, state):\n+        # type: (_ContextState) -> None\n+        self.trace_id, self.span_id, self._meta, self._metrics = state\n+        # We cannot serialize and lock, so we must recreate it unless we already have one\n+        self._lock = threading.RLock()\n+\n     def _with_span(self, span):\n         # type: (Span) -> Context\n         \"\"\"Return a shallow copy of the context with the given span.\"\"\"\n", "issue": "\"Tracing Across Processes\" example crashes\n<!--\r\nThanks for taking the time for reporting an issue!\r\n\r\nBefore reporting an issue on dd-trace-py, please be sure to provide all\r\nnecessary information.\r\n\r\nIf you're hitting a bug, make sure that you're using the latest version of this\r\nlibrary.\r\n-->\r\n\r\n### Summary of problem\r\n\r\nHello, I cannot get \"tracing across processes\" to work. Python crashes while trying to pickle the tracer context RLock. The minimal reproduction example is [the example from the ddtrace advanced usage docs](https://ddtrace.readthedocs.io/en/stable/advanced_usage.html#tracing-across-processes). I also cannot get this to work with any prior ddtrace version.\r\n\r\n### Which version of dd-trace-py are you using?\r\n\r\nddtrace: 1.5.3\r\npython: 3.9.13\r\n\r\n### How can we reproduce your problem?\r\n\r\nRun the `Tracing Across Processes` example [from the docs](https://ddtrace.readthedocs.io/en/stable/advanced_usage.html#tracing-across-processes)\r\n\r\n```python\r\nfrom multiprocessing import Process\r\nimport time\r\nfrom ddtrace import tracer\r\n\r\ndef _target(ctx):\r\n    tracer.context_provider.activate(ctx)\r\n    with tracer.trace(\"proc\"):\r\n        time.sleep(1)\r\n    tracer.shutdown()\r\n\r\nwith tracer.trace(\"work\"):\r\n    proc = Process(target=_target, args=(tracer.current_trace_context(),))\r\n    proc.start()\r\n    time.sleep(1)\r\n    proc.join()\r\n```\r\n\r\n### What is the result that you get?\r\n\r\n```\r\nTraceback (most recent call last):\r\n  File \"/Users/jboulanger/Library/Application Support/JetBrains/PyCharm2021.2/scratches/scratch.py\", line 15, in <module>\r\n    proc.start()\r\n  File \"/usr/local/Cellar/python@3.9/3.9.13_3/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/process.py\", line 121, in start\r\n    self._popen = self._Popen(self)\r\n  File \"/usr/local/Cellar/python@3.9/3.9.13_3/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/context.py\", line 224, in _Popen\r\n    return _default_context.get_context().Process._Popen(process_obj)\r\n  File \"/usr/local/Cellar/python@3.9/3.9.13_3/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/context.py\", line 284, in _Popen\r\n    return Popen(process_obj)\r\n  File \"/usr/local/Cellar/python@3.9/3.9.13_3/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/popen_spawn_posix.py\", line 32, in __init__\r\n    super().__init__(process_obj)\r\n  File \"/usr/local/Cellar/python@3.9/3.9.13_3/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/popen_fork.py\", line 19, in __init__\r\n    self._launch(process_obj)\r\n  File \"/usr/local/Cellar/python@3.9/3.9.13_3/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/popen_spawn_posix.py\", line 47, in _launch\r\n    reduction.dump(process_obj, fp)\r\n  File \"/usr/local/Cellar/python@3.9/3.9.13_3/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/reduction.py\", line 60, in dump\r\n    ForkingPickler(file, protocol).dump(obj)\r\nTypeError: cannot pickle '_thread.RLock' object\r\nProcess finished with exit code 1\r\n```\r\n\r\n### What is the result that you expected?\r\n\r\nI expected this to complete without crashing\n", "before_files": [{"content": "import base64\nimport threading\nfrom typing import Any\nfrom typing import Optional\nfrom typing import TYPE_CHECKING\nfrom typing import Text\n\nfrom .constants import ORIGIN_KEY\nfrom .constants import SAMPLING_PRIORITY_KEY\nfrom .constants import USER_ID_KEY\nfrom .internal.compat import NumericType\nfrom .internal.compat import PY2\nfrom .internal.logger import get_logger\n\n\nif TYPE_CHECKING:  # pragma: no cover\n    from .span import Span\n    from .span import _MetaDictType\n    from .span import _MetricDictType\n\nlog = get_logger(__name__)\n\n\nclass Context(object):\n    \"\"\"Represents the state required to propagate a trace across execution\n    boundaries.\n    \"\"\"\n\n    __slots__ = [\n        \"trace_id\",\n        \"span_id\",\n        \"_lock\",\n        \"_meta\",\n        \"_metrics\",\n    ]\n\n    def __init__(\n        self,\n        trace_id=None,  # type: Optional[int]\n        span_id=None,  # type: Optional[int]\n        dd_origin=None,  # type: Optional[str]\n        sampling_priority=None,  # type: Optional[float]\n        meta=None,  # type: Optional[_MetaDictType]\n        metrics=None,  # type: Optional[_MetricDictType]\n        lock=None,  # type: Optional[threading.RLock]\n    ):\n        self._meta = meta if meta is not None else {}  # type: _MetaDictType\n        self._metrics = metrics if metrics is not None else {}  # type: _MetricDictType\n\n        self.trace_id = trace_id  # type: Optional[int]\n        self.span_id = span_id  # type: Optional[int]\n\n        if dd_origin is not None:\n            self._meta[ORIGIN_KEY] = dd_origin\n        if sampling_priority is not None:\n            self._metrics[SAMPLING_PRIORITY_KEY] = sampling_priority\n\n        if lock is not None:\n            self._lock = lock\n        else:\n            # DEV: A `forksafe.RLock` is not necessary here since Contexts\n            # are recreated by the tracer after fork\n            # https://github.com/DataDog/dd-trace-py/blob/a1932e8ddb704d259ea8a3188d30bf542f59fd8d/ddtrace/tracer.py#L489-L508\n            self._lock = threading.RLock()\n\n    def _with_span(self, span):\n        # type: (Span) -> Context\n        \"\"\"Return a shallow copy of the context with the given span.\"\"\"\n        return self.__class__(\n            trace_id=span.trace_id, span_id=span.span_id, meta=self._meta, metrics=self._metrics, lock=self._lock\n        )\n\n    def _update_tags(self, span):\n        # type: (Span) -> None\n        with self._lock:\n            for tag in self._meta:\n                span._meta.setdefault(tag, self._meta[tag])\n            for metric in self._metrics:\n                span._metrics.setdefault(metric, self._metrics[metric])\n\n    @property\n    def sampling_priority(self):\n        # type: () -> Optional[NumericType]\n        \"\"\"Return the context sampling priority for the trace.\"\"\"\n        return self._metrics.get(SAMPLING_PRIORITY_KEY)\n\n    @sampling_priority.setter\n    def sampling_priority(self, value):\n        # type: (Optional[NumericType]) -> None\n        with self._lock:\n            if value is None:\n                if SAMPLING_PRIORITY_KEY in self._metrics:\n                    del self._metrics[SAMPLING_PRIORITY_KEY]\n                return\n            self._metrics[SAMPLING_PRIORITY_KEY] = value\n\n    @property\n    def _traceparent(self):\n        # type: () -> str\n        if self.trace_id is None or self.span_id is None:\n            return \"\"\n\n        sampled = 1 if self.sampling_priority and self.sampling_priority > 0 else 0\n        return \"00-{:032x}-{:016x}-{:02x}\".format(self.trace_id, self.span_id, sampled)\n\n    @property\n    def dd_origin(self):\n        # type: () -> Optional[Text]\n        \"\"\"Get the origin of the trace.\"\"\"\n        return self._meta.get(ORIGIN_KEY)\n\n    @dd_origin.setter\n    def dd_origin(self, value):\n        # type: (Optional[Text]) -> None\n        \"\"\"Set the origin of the trace.\"\"\"\n        with self._lock:\n            if value is None:\n                if ORIGIN_KEY in self._meta:\n                    del self._meta[ORIGIN_KEY]\n                return\n            self._meta[ORIGIN_KEY] = value\n\n    @property\n    def dd_user_id(self):\n        # type: () -> Optional[Text]\n        \"\"\"Get the user ID of the trace.\"\"\"\n        user_id = self._meta.get(USER_ID_KEY)\n        if user_id:\n            if not PY2:\n                return str(base64.b64decode(user_id), encoding=\"utf-8\")\n            else:\n                return str(base64.b64decode(user_id))\n        return None\n\n    @dd_user_id.setter\n    def dd_user_id(self, value):\n        # type: (Optional[Text]) -> None\n        \"\"\"Set the user ID of the trace.\"\"\"\n        with self._lock:\n            if value is None:\n                if USER_ID_KEY in self._meta:\n                    del self._meta[USER_ID_KEY]\n                return\n            if not PY2:\n                value = str(base64.b64encode(bytes(value, encoding=\"utf-8\")), encoding=\"utf-8\")\n            else:\n                value = str(base64.b64encode(bytes(value)))\n            self._meta[USER_ID_KEY] = value\n\n    def __eq__(self, other):\n        # type: (Any) -> bool\n        if isinstance(other, Context):\n            with self._lock:\n                return (\n                    self.trace_id == other.trace_id\n                    and self.span_id == other.span_id\n                    and self._meta == other._meta\n                    and self._metrics == other._metrics\n                )\n        return False\n\n    def __repr__(self):\n        # type: () -> str\n        return \"Context(trace_id=%s, span_id=%s, _meta=%s, _metrics=%s)\" % (\n            self.trace_id,\n            self.span_id,\n            self._meta,\n            self._metrics,\n        )\n\n    __str__ = __repr__\n", "path": "ddtrace/context.py"}], "after_files": [{"content": "import base64\nimport threading\nfrom typing import Any\nfrom typing import Optional\nfrom typing import TYPE_CHECKING\nfrom typing import Text\n\nfrom .constants import ORIGIN_KEY\nfrom .constants import SAMPLING_PRIORITY_KEY\nfrom .constants import USER_ID_KEY\nfrom .internal.compat import NumericType\nfrom .internal.compat import PY2\nfrom .internal.logger import get_logger\n\n\nif TYPE_CHECKING:  # pragma: no cover\n    from typing import Tuple\n\n    from .span import Span\n    from .span import _MetaDictType\n    from .span import _MetricDictType\n\n    _ContextState = Tuple[\n        Optional[int],  # trace_id\n        Optional[int],  # span_id\n        _MetaDictType,  # _meta\n        _MetricDictType,  # _metrics\n    ]\n\n\nlog = get_logger(__name__)\n\n\nclass Context(object):\n    \"\"\"Represents the state required to propagate a trace across execution\n    boundaries.\n    \"\"\"\n\n    __slots__ = [\n        \"trace_id\",\n        \"span_id\",\n        \"_lock\",\n        \"_meta\",\n        \"_metrics\",\n    ]\n\n    def __init__(\n        self,\n        trace_id=None,  # type: Optional[int]\n        span_id=None,  # type: Optional[int]\n        dd_origin=None,  # type: Optional[str]\n        sampling_priority=None,  # type: Optional[float]\n        meta=None,  # type: Optional[_MetaDictType]\n        metrics=None,  # type: Optional[_MetricDictType]\n        lock=None,  # type: Optional[threading.RLock]\n    ):\n        self._meta = meta if meta is not None else {}  # type: _MetaDictType\n        self._metrics = metrics if metrics is not None else {}  # type: _MetricDictType\n\n        self.trace_id = trace_id  # type: Optional[int]\n        self.span_id = span_id  # type: Optional[int]\n\n        if dd_origin is not None:\n            self._meta[ORIGIN_KEY] = dd_origin\n        if sampling_priority is not None:\n            self._metrics[SAMPLING_PRIORITY_KEY] = sampling_priority\n\n        if lock is not None:\n            self._lock = lock\n        else:\n            # DEV: A `forksafe.RLock` is not necessary here since Contexts\n            # are recreated by the tracer after fork\n            # https://github.com/DataDog/dd-trace-py/blob/a1932e8ddb704d259ea8a3188d30bf542f59fd8d/ddtrace/tracer.py#L489-L508\n            self._lock = threading.RLock()\n\n    def __getstate__(self):\n        # type: () -> _ContextState\n        return (\n            self.trace_id,\n            self.span_id,\n            self._meta,\n            self._metrics,\n            # Note: self._lock is not serializable\n        )\n\n    def __setstate__(self, state):\n        # type: (_ContextState) -> None\n        self.trace_id, self.span_id, self._meta, self._metrics = state\n        # We cannot serialize and lock, so we must recreate it unless we already have one\n        self._lock = threading.RLock()\n\n    def _with_span(self, span):\n        # type: (Span) -> Context\n        \"\"\"Return a shallow copy of the context with the given span.\"\"\"\n        return self.__class__(\n            trace_id=span.trace_id, span_id=span.span_id, meta=self._meta, metrics=self._metrics, lock=self._lock\n        )\n\n    def _update_tags(self, span):\n        # type: (Span) -> None\n        with self._lock:\n            for tag in self._meta:\n                span._meta.setdefault(tag, self._meta[tag])\n            for metric in self._metrics:\n                span._metrics.setdefault(metric, self._metrics[metric])\n\n    @property\n    def sampling_priority(self):\n        # type: () -> Optional[NumericType]\n        \"\"\"Return the context sampling priority for the trace.\"\"\"\n        return self._metrics.get(SAMPLING_PRIORITY_KEY)\n\n    @sampling_priority.setter\n    def sampling_priority(self, value):\n        # type: (Optional[NumericType]) -> None\n        with self._lock:\n            if value is None:\n                if SAMPLING_PRIORITY_KEY in self._metrics:\n                    del self._metrics[SAMPLING_PRIORITY_KEY]\n                return\n            self._metrics[SAMPLING_PRIORITY_KEY] = value\n\n    @property\n    def _traceparent(self):\n        # type: () -> str\n        if self.trace_id is None or self.span_id is None:\n            return \"\"\n\n        sampled = 1 if self.sampling_priority and self.sampling_priority > 0 else 0\n        return \"00-{:032x}-{:016x}-{:02x}\".format(self.trace_id, self.span_id, sampled)\n\n    @property\n    def dd_origin(self):\n        # type: () -> Optional[Text]\n        \"\"\"Get the origin of the trace.\"\"\"\n        return self._meta.get(ORIGIN_KEY)\n\n    @dd_origin.setter\n    def dd_origin(self, value):\n        # type: (Optional[Text]) -> None\n        \"\"\"Set the origin of the trace.\"\"\"\n        with self._lock:\n            if value is None:\n                if ORIGIN_KEY in self._meta:\n                    del self._meta[ORIGIN_KEY]\n                return\n            self._meta[ORIGIN_KEY] = value\n\n    @property\n    def dd_user_id(self):\n        # type: () -> Optional[Text]\n        \"\"\"Get the user ID of the trace.\"\"\"\n        user_id = self._meta.get(USER_ID_KEY)\n        if user_id:\n            if not PY2:\n                return str(base64.b64decode(user_id), encoding=\"utf-8\")\n            else:\n                return str(base64.b64decode(user_id))\n        return None\n\n    @dd_user_id.setter\n    def dd_user_id(self, value):\n        # type: (Optional[Text]) -> None\n        \"\"\"Set the user ID of the trace.\"\"\"\n        with self._lock:\n            if value is None:\n                if USER_ID_KEY in self._meta:\n                    del self._meta[USER_ID_KEY]\n                return\n            if not PY2:\n                value = str(base64.b64encode(bytes(value, encoding=\"utf-8\")), encoding=\"utf-8\")\n            else:\n                value = str(base64.b64encode(bytes(value)))\n            self._meta[USER_ID_KEY] = value\n\n    def __eq__(self, other):\n        # type: (Any) -> bool\n        if isinstance(other, Context):\n            with self._lock:\n                return (\n                    self.trace_id == other.trace_id\n                    and self.span_id == other.span_id\n                    and self._meta == other._meta\n                    and self._metrics == other._metrics\n                )\n        return False\n\n    def __repr__(self):\n        # type: () -> str\n        return \"Context(trace_id=%s, span_id=%s, _meta=%s, _metrics=%s)\" % (\n            self.trace_id,\n            self.span_id,\n            self._meta,\n            self._metrics,\n        )\n\n    __str__ = __repr__\n", "path": "ddtrace/context.py"}]}