otlp-test-data 0.9.3__tar.gz → 0.10.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.
@@ -0,0 +1,49 @@
1
+ on:
2
+ push:
3
+ branches: [main]
4
+ pull_request:
5
+ branches: [main]
6
+
7
+ jobs:
8
+ ci:
9
+ runs-on: ubuntu-latest
10
+ strategy:
11
+ fail-fast: false
12
+ matrix:
13
+ python-version: [3.8, 3.9, "3.10", 3.11, 3.12, 3.13, 3.13t, 3.14]
14
+ dep-versions: [--frozen, "--resolution lowest", "--resolution highest"]
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: astral-sh/setup-uv@v5
18
+ - run: |
19
+ uv python install ${{ matrix.python-version }}
20
+ uv sync --all-groups ${{ matrix.dep-versions }}
21
+ uv pip freeze
22
+ uv run pytest
23
+ uv run pyright
24
+ uv run ruff check
25
+ uv build
26
+
27
+ release:
28
+ needs: ci
29
+ runs-on: ubuntu-latest
30
+ environment: release
31
+ permissions:
32
+ id-token: write
33
+ attestations: write
34
+ steps:
35
+ - uses: actions/checkout@v4
36
+ - uses: astral-sh/setup-uv@v5
37
+ - run: |
38
+ uv build --sdist --wheel
39
+ rm -vf dist/.gitignore # https://github.com/astral-sh/uv/issues/11652
40
+ - uses: pypa/gh-action-pypi-publish@release/v1
41
+ with:
42
+ repository-url: https://test.pypi.org/legacy/
43
+ skip-existing: true
44
+ verbose: true
45
+ - run: rm -f dist/*.attestation # should I parametrise release instead?
46
+ - uses: pypa/gh-action-pypi-publish@release/v1
47
+ with:
48
+ skip-existing: true
49
+ verbose: true
@@ -0,0 +1,15 @@
1
+ on:
2
+ push:
3
+ branches: [--disabled--main]
4
+
5
+ jobs:
6
+ test-psr:
7
+ runs-on: ubuntu-latest
8
+ permissions:
9
+ contents: write
10
+ steps:
11
+ - uses: actions/checkout@v4
12
+ with:
13
+ fetch-depth: 0
14
+ - uses: astral-sh/setup-uv@v5
15
+ - run: uvx --from python-semantic-release psr -v -v -v version
@@ -0,0 +1,13 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Editors
10
+ .*.swp
11
+
12
+ # Virtual environments
13
+ .venv
@@ -0,0 +1,7 @@
1
+ repos:
2
+ - repo: git@github.com:astral-sh/ruff-pre-commit.git
3
+ rev: v0.9.6
4
+ hooks:
5
+ - id: ruff
6
+ args: [ --fix ]
7
+ - id: ruff-format
@@ -1,14 +1,17 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: otlp-test-data
3
- Version: 0.9.3
3
+ Version: 0.10.0
4
4
  Summary: Produces OTLP data using OTEL instrumentation
5
5
  Classifier: Development Status :: 3 - Alpha
6
+ Classifier: Framework :: OpenTelemetry
7
+ Classifier: Framework :: OpenTelemetry :: Exporters
6
8
  Classifier: Intended Audience :: Developers
7
9
  Classifier: License :: OSI Approved :: MIT License
8
10
  Classifier: Natural Language :: English
9
11
  Classifier: Operating System :: OS Independent
10
12
  Classifier: Programming Language :: Python
11
13
  Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3 :: Only
12
15
  Classifier: Programming Language :: Python :: 3.8
13
16
  Classifier: Programming Language :: Python :: 3.9
14
17
  Classifier: Programming Language :: Python :: 3.10
@@ -16,20 +19,17 @@ Classifier: Programming Language :: Python :: 3.11
16
19
  Classifier: Programming Language :: Python :: 3.12
17
20
  Classifier: Programming Language :: Python :: 3.13
18
21
  Classifier: Programming Language :: Python :: 3.14
19
- Classifier: Programming Language :: Python :: 3 :: Only
20
22
  Classifier: Programming Language :: Python :: Implementation :: CPython
21
23
  Classifier: Programming Language :: Python :: Implementation :: PyPy
22
24
  Classifier: Topic :: Software Development :: Testing
23
25
  Classifier: Topic :: Software Development :: Testing :: Traffic Generation
24
- Classifier: Framework :: OpenTelemetry
25
- Classifier: Framework :: OpenTelemetry :: Exporters
26
26
  Requires-Python: >=3.8
27
- Description-Content-Type: text/markdown
28
27
  Requires-Dist: freezegun~=1.5
28
+ Requires-Dist: grpcio<2.0.0,>=1.66.2; python_version >= '3.13'
29
29
  Requires-Dist: opentelemetry-api~=1.30
30
- Requires-Dist: opentelemetry-exporter-otlp~=1.30
31
30
  Requires-Dist: opentelemetry-exporter-otlp-proto-http~=1.30
32
- Requires-Dist: grpcio<2.0.0,>=1.66.2; python_version >= "3.13"
31
+ Requires-Dist: opentelemetry-exporter-otlp~=1.30
32
+ Description-Content-Type: text/markdown
33
33
 
34
34
  # otlp-test-data
35
35
 
@@ -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,3 @@
1
+ 0.9.0
2
+
3
+ * something
@@ -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.3"
3
+ version = "0.10.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
@@ -47,6 +47,9 @@ dev = [
47
47
  testing = [
48
48
  "pytest ~= 8.3.4",
49
49
  ]
50
+ [build-system]
51
+ requires = ["hatchling"]
52
+ build-backend = "hatchling.build"
50
53
 
51
54
  [tool.semantic_release]
52
55
  version_variables = ["pyproject.toml:project.version"]
@@ -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()