otlp-test-data 0.9.4__tar.gz → 0.11.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: otlp-test-data
3
- Version: 0.9.4
3
+ Version: 0.11.0
4
4
  Summary: Produces OTLP data using OTEL instrumentation
5
5
  Classifier: Development Status :: 3 - Alpha
6
6
  Classifier: Framework :: OpenTelemetry
@@ -37,9 +37,10 @@ Produces OTLP data using OTEL instrumentation.
37
37
 
38
38
  ### Features
39
39
 
40
- - fixed, configurable timestamps
40
+ - Fixed, configurable timestamps
41
41
  - aims to cover as much of OTEL API as possible
42
- - aims to cover all valid data types
42
+ - Cover all valid data types
43
+ - Events
43
44
 
44
45
  ### Limitations
45
46
 
@@ -48,7 +49,8 @@ Produces OTLP data using OTEL instrumentation.
48
49
 
49
50
  ### TODO
50
51
 
51
- - Events
52
52
  - Links
53
53
  - Baggage
54
54
  - Schemata, when https://github.com/open-telemetry/opentelemetry-python/pull/4359 lands
55
+ - Attribute value type coercion, e.g. `class Str(str): ...` and objects with `__str__(self)`.
56
+ - Exceptions
@@ -0,0 +1,192 @@
1
+ from __future__ import annotations
2
+
3
+ import base64
4
+ import dataclasses
5
+ import json
6
+ import random
7
+ from typing import Sequence, TYPE_CHECKING
8
+ from typing_extensions import reveal_type as reveal_type # temp
9
+
10
+ import freezegun
11
+ import opentelemetry.trace
12
+ from google.protobuf.json_format import MessageToDict
13
+ from opentelemetry.exporter.otlp.proto.common._internal import trace_encoder
14
+ from opentelemetry.sdk.trace import TracerProvider
15
+ from opentelemetry.sdk.trace.export import SimpleSpanProcessor
16
+ from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter
17
+ from opentelemetry.sdk.resources import Resource
18
+ from opentelemetry.trace import set_tracer_provider, get_tracer_provider
19
+
20
+ if TYPE_CHECKING:
21
+ from google.protobuf.message import Message
22
+ from opentelemetry.sdk.trace import ReadableSpan
23
+ from opentelemetry.proto.collector.trace.v1.trace_service_pb2 import (
24
+ ExportTraceServiceRequest,
25
+ )
26
+
27
+
28
+ tracer = opentelemetry.trace.get_tracer(__name__)
29
+ provider = TracerProvider()
30
+ exporter = InMemorySpanExporter()
31
+ provider.add_span_processor(SimpleSpanProcessor(exporter))
32
+ set_tracer_provider(provider)
33
+
34
+
35
+ @dataclasses.dataclass
36
+ class Config:
37
+ start_time: str = "2020-01-01 00:00:00Z"
38
+ random_seed: int = 42
39
+
40
+
41
+ time: freezegun.api.FrozenDateTimeFactory = None # type: ignore
42
+
43
+
44
+ def sample_proto(config: Config | None = None) -> bytes:
45
+ return _proto_to_bytes(_spans_to_proto_object(sample_spans(config)))
46
+
47
+
48
+ def sample_json(config: Config | None = None) -> bytes:
49
+ return _proto_to_json(_spans_to_proto_object(sample_spans(config)))
50
+
51
+
52
+ def sample_spans(config: Config | None = None) -> Sequence[ReadableSpan]:
53
+ """Creates and finishes two spans, then returns them as a list."""
54
+ global time
55
+ config = config or Config()
56
+ resource = Resource.create(
57
+ attributes={
58
+ "service.namespace": "1234-1234", # a unique id
59
+ "service.name": "my-service",
60
+ "service.instance.id": "123",
61
+ "int": 42,
62
+ "float": 3.14,
63
+ "bool": False,
64
+ "str": "sss",
65
+ "ints": [42, 0],
66
+ "floats": [3.14, 2.72],
67
+ "strs": ["sss", "shh"],
68
+ }
69
+ )
70
+ del exporter._finished_spans[:]
71
+ get_tracer_provider()._resource = resource # type: ignore
72
+
73
+ random.seed(config.random_seed)
74
+
75
+ with freezegun.freeze_time(config.start_time) as time: # type: ignore
76
+ workload()
77
+
78
+ return exporter.get_finished_spans()
79
+
80
+
81
+ def _spans_to_proto_object(spans: Sequence[ReadableSpan]) -> ExportTraceServiceRequest:
82
+ return trace_encoder.encode_spans(spans)
83
+
84
+
85
+ def _proto_to_bytes(data: Message) -> bytes:
86
+ return data.SerializePartialToString()
87
+
88
+
89
+ # FIXME: there are probably 3 different enumerated types in the API
90
+ def _proto_to_json(data: Message) -> bytes:
91
+ dic = MessageToDict(data)
92
+
93
+ for rs in dic["resourceSpans"]:
94
+ for ss in rs["scopeSpans"]:
95
+ for sp in ss["spans"]:
96
+ for k in "parentSpanId spanId traceId".split():
97
+ if k in sp:
98
+ sp[k] = base64.b64decode(sp[k]).hex()
99
+ sp["kind"] = {
100
+ "SPAN_KIND_UNSPECIFIED": 0,
101
+ "SPAN_KIND_INTERNAL": 1,
102
+ "SPAN_KIND_SERVER": 2,
103
+ "SPAN_KIND_CLIENT": 3,
104
+ "SPAN_KIND_PRODUCER": 4,
105
+ "SPAN_KIND_CONSUMER": 5,
106
+ }[sp["kind"]]
107
+
108
+ return json.dumps(dic).encode("utf-8")
109
+
110
+
111
+ def workload():
112
+ series_of_spans()
113
+ nested_spans()
114
+ attribute_types()
115
+ repeated_attributes()
116
+ events()
117
+
118
+
119
+ def series_of_spans():
120
+ with tracer.start_as_current_span("span-one") as one:
121
+ one.set_attribute("count", "one")
122
+ time.tick()
123
+
124
+ with tracer.start_as_current_span("span-two") as two:
125
+ two.set_attribute("count", "two")
126
+ time.tick()
127
+
128
+
129
+ def nested_spans():
130
+ outer()
131
+
132
+
133
+ @tracer.start_as_current_span("outer")
134
+ def outer():
135
+ time.tick()
136
+ inner()
137
+ time.tick()
138
+
139
+
140
+ @tracer.start_as_current_span("inner")
141
+ def inner():
142
+ opentelemetry.trace.get_current_span().set_attribute("an-attribute", 42)
143
+ time.tick()
144
+
145
+
146
+ def attribute_types():
147
+ with tracer.start_as_current_span("attribute-types") as span:
148
+ span.set_attributes(
149
+ {"int": 42, "bool": False, "float": 3.14, "str": "string-cheese"}
150
+ )
151
+ span.set_attributes(
152
+ {
153
+ "ints": [1, 42],
154
+ "bools": [True, False],
155
+ "floats": [2.72, 3.14],
156
+ "strs": ["string-cheese", "strung-cheese"],
157
+ }
158
+ )
159
+
160
+
161
+ def repeated_attributes():
162
+ with tracer.start_as_current_span("attribute-types") as span:
163
+ span.set_attribute("int", 42)
164
+ span.set_attribute("int", 99)
165
+
166
+ with tracer.start_as_current_span("attribute-types") as span:
167
+ span.set_attributes({"int": 42})
168
+ span.set_attributes({"int": 99})
169
+
170
+
171
+ def events():
172
+ with tracer.start_as_current_span("attribute-types") as span:
173
+ span.add_event("first event")
174
+ time.tick()
175
+
176
+ span.add_event("second event")
177
+ time.tick()
178
+
179
+ span.add_event(
180
+ "event with attributes",
181
+ attributes={
182
+ "int": 42,
183
+ "bool": False,
184
+ "float": 3.14,
185
+ "str": "string-cheese",
186
+ "ints": [1, 42],
187
+ "bools": [True, False],
188
+ "floats": [2.72, 3.14],
189
+ "strs": ["string-cheese", "strung-cheese"],
190
+ },
191
+ )
192
+ time.tick()
File without changes
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "otlp-test-data"
3
- version = "0.9.4"
3
+ version = "0.11.0"
4
4
  description = "Produces OTLP data using OTEL instrumentation"
5
5
  requires-python = ">=3.8"
6
6
  # https://github.com/astral-sh/uv/issues/4204
@@ -4,9 +4,10 @@ Produces OTLP data using OTEL instrumentation.
4
4
 
5
5
  ### Features
6
6
 
7
- - fixed, configurable timestamps
7
+ - Fixed, configurable timestamps
8
8
  - aims to cover as much of OTEL API as possible
9
- - aims to cover all valid data types
9
+ - Cover all valid data types
10
+ - Events
10
11
 
11
12
  ### Limitations
12
13
 
@@ -15,7 +16,8 @@ Produces OTLP data using OTEL instrumentation.
15
16
 
16
17
  ### TODO
17
18
 
18
- - Events
19
19
  - Links
20
20
  - Baggage
21
21
  - Schemata, when https://github.com/open-telemetry/opentelemetry-python/pull/4359 lands
22
+ - Attribute value type coercion, e.g. `class Str(str): ...` and objects with `__str__(self)`.
23
+ - Exceptions
@@ -0,0 +1,15 @@
1
+ import json
2
+
3
+ from otlp_test_data import sample_proto, sample_json
4
+
5
+
6
+ def test_same_json():
7
+ assert json.loads(sample_json()) == json.loads(sample_json())
8
+
9
+
10
+ def test_same_json_verbatim():
11
+ assert sample_json() == sample_json()
12
+
13
+
14
+ def test_same_proto_verbatim():
15
+ assert sample_proto() == sample_proto()
@@ -0,0 +1,15 @@
1
+ import json
2
+
3
+ from otlp_test_data import sample_proto, sample_spans, sample_json
4
+
5
+
6
+ def test_spans():
7
+ assert sample_spans()
8
+
9
+
10
+ def test_json():
11
+ assert json.loads(sample_json())
12
+
13
+
14
+ def test_proto():
15
+ assert sample_proto()
@@ -374,7 +374,7 @@ wheels = [
374
374
 
375
375
  [[package]]
376
376
  name = "otlp-test-data"
377
- version = "0.9.3"
377
+ version = "0.10.0"
378
378
  source = { editable = "." }
379
379
  dependencies = [
380
380
  { name = "freezegun" },
@@ -1,95 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import base64
4
- import dataclasses
5
- import json
6
- import random
7
- from typing import Sequence, TYPE_CHECKING
8
- from typing_extensions import reveal_type as reveal_type # temp
9
-
10
- import freezegun
11
- from google.protobuf.json_format import MessageToDict
12
- from opentelemetry.exporter.otlp.proto.common._internal import trace_encoder
13
- from opentelemetry.sdk.trace import TracerProvider
14
- from opentelemetry.sdk.trace.export import SimpleSpanProcessor
15
- from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter
16
-
17
- if TYPE_CHECKING:
18
- from google.protobuf.message import Message
19
- from opentelemetry.sdk.trace import ReadableSpan
20
- from opentelemetry.proto.collector.trace.v1.trace_service_pb2 import (
21
- ExportTraceServiceRequest,
22
- )
23
-
24
-
25
- @dataclasses.dataclass
26
- class Config:
27
- start_time: str = "2020-01-01 00:00:00Z"
28
- random_seed: int = 42
29
-
30
-
31
- time = None
32
-
33
-
34
- def sample_proto(config: Config | None = None) -> bytes:
35
- return _proto_to_bytes(_spans_to_proto_object(sample_spans(config)))
36
-
37
-
38
- def sample_json(config: Config | None = None) -> bytes:
39
- return _proto_to_json(_spans_to_proto_object(sample_spans(config)))
40
-
41
-
42
- def sample_spans(config: Config | None = None) -> Sequence[ReadableSpan]:
43
- """Creates and finishes two spans, then returns them as a list."""
44
- global time
45
- config = config or Config()
46
- tracer_provider = TracerProvider()
47
- exporter = InMemorySpanExporter()
48
- tracer_provider.add_span_processor(SimpleSpanProcessor(exporter))
49
- tracer = tracer_provider.get_tracer(__name__)
50
-
51
- with freezegun.freeze_time(config.start_time) as time:
52
- random.seed(config.random_seed)
53
-
54
- # FIXME the workload section is expected to grow a lot
55
- # TODO: attributes
56
- # bool, int, float, str
57
- # list[bool], ...
58
- # tuple[bool], ...
59
- # Sequence[bool], ... (maybe)
60
- with tracer.start_as_current_span("span-one"):
61
- time.tick()
62
- with tracer.start_as_current_span("span-two"):
63
- time.tick()
64
-
65
- return exporter.get_finished_spans()
66
-
67
-
68
- def _spans_to_proto_object(spans: Sequence[ReadableSpan]) -> ExportTraceServiceRequest:
69
- return trace_encoder.encode_spans(spans)
70
-
71
-
72
- def _proto_to_bytes(data: Message) -> bytes:
73
- return data.SerializePartialToString()
74
-
75
-
76
- # FIXME: there are probably 3 different enumerated types in the API
77
- def _proto_to_json(data: Message) -> bytes:
78
- dic = MessageToDict(data)
79
-
80
- for rs in dic["resourceSpans"]:
81
- for ss in rs["scopeSpans"]:
82
- for sp in ss["spans"]:
83
- for k in "parentSpanId spanId traceId".split():
84
- if k in sp:
85
- sp[k] = base64.b64decode(sp[k]).hex()
86
- sp["kind"] = {
87
- "SPAN_KIND_UNSPECIFIED": 0,
88
- "SPAN_KIND_INTERNAL": 1,
89
- "SPAN_KIND_SERVER": 2,
90
- "SPAN_KIND_CLIENT": 3,
91
- "SPAN_KIND_PRODUCER": 4,
92
- "SPAN_KIND_CONSUMER": 5,
93
- }[sp["kind"]]
94
-
95
- return json.dumps(dic).encode("utf-8")
@@ -1,15 +0,0 @@
1
- import json
2
-
3
- from otlp_test_data import Config, sample_proto, sample_json
4
-
5
-
6
- def test_same_json():
7
- assert json.loads(sample_json(Config())) == json.loads(sample_json(Config()))
8
-
9
-
10
- def test_same_json_verbatim():
11
- assert sample_json(Config()) == sample_json(Config())
12
-
13
-
14
- def test_same_proto_verbatim():
15
- assert sample_proto(Config()) == sample_proto(Config())