1# Copyright The OpenTelemetry Authors
2# SPDX-License-Identifier: Apache-2.0
3
4import abc
5import typing
6
7from opentelemetry.context.context import Context
8
9CarrierT = typing.TypeVar("CarrierT")
10# pylint: disable=invalid-name
11CarrierValT = list[str] | str
12
13
14class Getter(abc.ABC, typing.Generic[CarrierT]):
15 """This class implements a Getter that enables extracting propagated
16 fields from a carrier.
17 """
18
19 @abc.abstractmethod
20 def get(self, carrier: CarrierT, key: str) -> list[str] | None:
21 """Function that can retrieve zero
22 or more values from the carrier. In the case that
23 the value does not exist, returns None.
24
25 Args:
26 carrier: An object which contains values that are used to
27 construct a Context.
28 key: key of a field in carrier.
29 Returns: first value of the propagation key or None if the key doesn't
30 exist.
31 """
32
33 @abc.abstractmethod
34 def keys(self, carrier: CarrierT) -> list[str]:
35 """Function that can retrieve all the keys in a carrier object.
36
37 Args:
38 carrier: An object which contains values that are
39 used to construct a Context.
40 Returns:
41 list of keys from the carrier.
42 """
43
44
45class Setter(abc.ABC, typing.Generic[CarrierT]):
46 """This class implements a Setter that enables injecting propagated
47 fields into a carrier.
48 """
49
50 @abc.abstractmethod
51 def set(self, carrier: CarrierT, key: str, value: str) -> None:
52 """Function that can set a value into a carrier""
53
54 Args:
55 carrier: An object which contains values that are used to
56 construct a Context.
57 key: key of a field in carrier.
58 value: value for a field in carrier.
59 """
60
61
62class DefaultGetter(Getter[typing.Mapping[str, CarrierValT]]):
63 def get(
64 self, carrier: typing.Mapping[str, CarrierValT], key: str
65 ) -> list[str] | None:
66 """Getter implementation to retrieve a value from a dictionary.
67
68 Args:
69 carrier: dictionary in which to get value
70 key: the key used to get the value
71 Returns:
72 A list with a single string with the value if it exists, else None.
73 """
74 val = carrier.get(key, None)
75 if val is None:
76 return None
77 if isinstance(val, typing.Iterable) and not isinstance(val, str):
78 return list(val)
79 return [val]
80
81 def keys(self, carrier: typing.Mapping[str, CarrierValT]) -> list[str]:
82 """Keys implementation that returns all keys from a dictionary."""
83 return list(carrier.keys())
84
85
86default_getter: Getter[CarrierT] = DefaultGetter() # type: ignore
87
88
89class DefaultSetter(Setter[typing.MutableMapping[str, CarrierValT]]):
90 def set(
91 self,
92 carrier: typing.MutableMapping[str, CarrierValT],
93 key: str,
94 value: CarrierValT,
95 ) -> None:
96 """Setter implementation to set a value into a dictionary.
97
98 Args:
99 carrier: dictionary in which to set value
100 key: the key used to set the value
101 value: the value to set
102 """
103 carrier[key] = value
104
105
106default_setter: Setter[CarrierT] = DefaultSetter() # type: ignore
107
108
109class TextMapPropagator(abc.ABC):
110 """This class provides an interface that enables extracting and injecting
111 context into headers of HTTP requests. HTTP frameworks and clients
112 can integrate with TextMapPropagator by providing the object containing the
113 headers, and a getter and setter function for the extraction and
114 injection of values, respectively.
115
116 """
117
118 @abc.abstractmethod
119 def extract(
120 self,
121 carrier: CarrierT,
122 context: Context | None = None,
123 getter: Getter[CarrierT] = default_getter,
124 ) -> Context:
125 """Create a Context from values in the carrier.
126
127 The extract function should retrieve values from the carrier
128 object using getter, and use values to populate a
129 Context value and return it.
130
131 Args:
132 getter: a function that can retrieve zero
133 or more values from the carrier. In the case that
134 the value does not exist, return an empty list.
135 carrier: and object which contains values that are
136 used to construct a Context. This object
137 must be paired with an appropriate getter
138 which understands how to extract a value from it.
139 context: an optional Context to use. Defaults to root
140 context if not set.
141 Returns:
142 A Context with configuration found in the carrier.
143
144 """
145
146 @abc.abstractmethod
147 def inject(
148 self,
149 carrier: CarrierT,
150 context: Context | None = None,
151 setter: Setter[CarrierT] = default_setter,
152 ) -> None:
153 """Inject values from a Context into a carrier.
154
155 inject enables the propagation of values into HTTP clients or
156 other objects which perform an HTTP request. Implementations
157 should use the `Setter` 's set method to set values on the
158 carrier.
159
160 Args:
161 carrier: An object that a place to define HTTP headers.
162 Should be paired with setter, which should
163 know how to set header values on the carrier.
164 context: an optional Context to use. Defaults to current
165 context if not set.
166 setter: An optional `Setter` object that can set values
167 on the carrier.
168
169 """
170
171 @property
172 @abc.abstractmethod
173 def fields(self) -> set[str]:
174 """
175 Gets the fields set in the carrier by the `inject` method.
176
177 If the carrier is reused, its fields that correspond with the ones
178 present in this attribute should be deleted before calling `inject`.
179
180 Returns:
181 A set with the fields set in `inject`.
182 """