opentelemetry-instrumentation 0.49b2__tar.gz → 0.51b0__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.
Files changed (30) hide show
  1. {opentelemetry_instrumentation-0.49b2 → opentelemetry_instrumentation-0.51b0}/PKG-INFO +7 -4
  2. {opentelemetry_instrumentation-0.49b2 → opentelemetry_instrumentation-0.51b0}/pyproject.toml +3 -1
  3. {opentelemetry_instrumentation-0.49b2 → opentelemetry_instrumentation-0.51b0}/src/opentelemetry/instrumentation/_semconv.py +69 -44
  4. {opentelemetry_instrumentation-0.49b2 → opentelemetry_instrumentation-0.51b0}/src/opentelemetry/instrumentation/bootstrap_gen.py +69 -53
  5. {opentelemetry_instrumentation-0.49b2 → opentelemetry_instrumentation-0.51b0}/src/opentelemetry/instrumentation/dependencies.py +16 -13
  6. {opentelemetry_instrumentation-0.49b2 → opentelemetry_instrumentation-0.51b0}/src/opentelemetry/instrumentation/instrumentor.py +9 -7
  7. {opentelemetry_instrumentation-0.49b2 → opentelemetry_instrumentation-0.51b0}/src/opentelemetry/instrumentation/utils.py +12 -10
  8. {opentelemetry_instrumentation-0.49b2 → opentelemetry_instrumentation-0.51b0}/src/opentelemetry/instrumentation/version.py +1 -1
  9. {opentelemetry_instrumentation-0.49b2 → opentelemetry_instrumentation-0.51b0}/tests/test_dependencies.py +16 -0
  10. opentelemetry_instrumentation-0.51b0/tests/test_semconv.py +258 -0
  11. {opentelemetry_instrumentation-0.49b2 → opentelemetry_instrumentation-0.51b0}/.gitignore +0 -0
  12. {opentelemetry_instrumentation-0.49b2 → opentelemetry_instrumentation-0.51b0}/LICENSE +0 -0
  13. {opentelemetry_instrumentation-0.49b2 → opentelemetry_instrumentation-0.51b0}/README.rst +0 -0
  14. {opentelemetry_instrumentation-0.49b2 → opentelemetry_instrumentation-0.51b0}/src/opentelemetry/instrumentation/auto_instrumentation/__init__.py +0 -0
  15. {opentelemetry_instrumentation-0.49b2 → opentelemetry_instrumentation-0.51b0}/src/opentelemetry/instrumentation/auto_instrumentation/_load.py +0 -0
  16. {opentelemetry_instrumentation-0.49b2 → opentelemetry_instrumentation-0.51b0}/src/opentelemetry/instrumentation/auto_instrumentation/sitecustomize.py +0 -0
  17. {opentelemetry_instrumentation-0.49b2 → opentelemetry_instrumentation-0.51b0}/src/opentelemetry/instrumentation/bootstrap.py +0 -0
  18. {opentelemetry_instrumentation-0.49b2 → opentelemetry_instrumentation-0.51b0}/src/opentelemetry/instrumentation/distro.py +0 -0
  19. {opentelemetry_instrumentation-0.49b2 → opentelemetry_instrumentation-0.51b0}/src/opentelemetry/instrumentation/environment_variables.py +0 -0
  20. {opentelemetry_instrumentation-0.49b2 → opentelemetry_instrumentation-0.51b0}/src/opentelemetry/instrumentation/propagators.py +0 -0
  21. {opentelemetry_instrumentation-0.49b2 → opentelemetry_instrumentation-0.51b0}/src/opentelemetry/instrumentation/py.typed +0 -0
  22. {opentelemetry_instrumentation-0.49b2 → opentelemetry_instrumentation-0.51b0}/src/opentelemetry/instrumentation/sqlcommenter_utils.py +0 -0
  23. {opentelemetry_instrumentation-0.49b2 → opentelemetry_instrumentation-0.51b0}/tests/__init__.py +0 -0
  24. {opentelemetry_instrumentation-0.49b2 → opentelemetry_instrumentation-0.51b0}/tests/auto_instrumentation/test_load.py +0 -0
  25. {opentelemetry_instrumentation-0.49b2 → opentelemetry_instrumentation-0.51b0}/tests/auto_instrumentation/test_run.py +0 -0
  26. {opentelemetry_instrumentation-0.49b2 → opentelemetry_instrumentation-0.51b0}/tests/test_bootstrap.py +0 -0
  27. {opentelemetry_instrumentation-0.49b2 → opentelemetry_instrumentation-0.51b0}/tests/test_distro.py +0 -0
  28. {opentelemetry_instrumentation-0.49b2 → opentelemetry_instrumentation-0.51b0}/tests/test_instrumentor.py +0 -0
  29. {opentelemetry_instrumentation-0.49b2 → opentelemetry_instrumentation-0.51b0}/tests/test_propagators.py +0 -0
  30. {opentelemetry_instrumentation-0.49b2 → opentelemetry_instrumentation-0.51b0}/tests/test_utils.py +0 -0
@@ -1,10 +1,12 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: opentelemetry-instrumentation
3
- Version: 0.49b2
3
+ Version: 0.51b0
4
4
  Summary: Instrumentation Tools & Auto Instrumentation for OpenTelemetry Python
5
5
  Project-URL: Homepage, https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/opentelemetry-instrumentation
6
+ Project-URL: Repository, https://github.com/open-telemetry/opentelemetry-python-contrib
6
7
  Author-email: OpenTelemetry Authors <cncf-opentelemetry-contributors@lists.cncf.io>
7
- License: Apache-2.0
8
+ License-Expression: Apache-2.0
9
+ License-File: LICENSE
8
10
  Classifier: Development Status :: 4 - Beta
9
11
  Classifier: Intended Audience :: Developers
10
12
  Classifier: License :: OSI Approved :: Apache Software License
@@ -15,9 +17,10 @@ Classifier: Programming Language :: Python :: 3.9
15
17
  Classifier: Programming Language :: Python :: 3.10
16
18
  Classifier: Programming Language :: Python :: 3.11
17
19
  Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
18
21
  Requires-Python: >=3.8
19
22
  Requires-Dist: opentelemetry-api~=1.4
20
- Requires-Dist: opentelemetry-semantic-conventions==0.49b2
23
+ Requires-Dist: opentelemetry-semantic-conventions==0.51b0
21
24
  Requires-Dist: packaging>=18.0
22
25
  Requires-Dist: wrapt<2.0.0,>=1.0.0
23
26
  Description-Content-Type: text/x-rst
@@ -23,10 +23,11 @@ classifiers = [
23
23
  "Programming Language :: Python :: 3.10",
24
24
  "Programming Language :: Python :: 3.11",
25
25
  "Programming Language :: Python :: 3.12",
26
+ "Programming Language :: Python :: 3.13",
26
27
  ]
27
28
  dependencies = [
28
29
  "opentelemetry-api ~= 1.4",
29
- "opentelemetry-semantic-conventions == 0.49b2",
30
+ "opentelemetry-semantic-conventions == 0.51b0",
30
31
  "wrapt >= 1.0.0, < 2.0.0",
31
32
  "packaging >= 18.0",
32
33
  ]
@@ -40,6 +41,7 @@ instrumentation = "opentelemetry.instrumentation.environment_variables"
40
41
 
41
42
  [project.urls]
42
43
  Homepage = "https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/opentelemetry-instrumentation"
44
+ Repository = "https://github.com/open-telemetry/opentelemetry-python-contrib"
43
45
 
44
46
  [tool.hatch.version]
45
47
  path = "src/opentelemetry/instrumentation/version.py"
@@ -109,23 +109,23 @@ OTEL_SEMCONV_STABILITY_OPT_IN = "OTEL_SEMCONV_STABILITY_OPT_IN"
109
109
 
110
110
  class _OpenTelemetryStabilitySignalType:
111
111
  HTTP = "http"
112
+ DATABASE = "database"
112
113
 
113
114
 
114
- class _HTTPStabilityMode(Enum):
115
- # http - emit the new, stable HTTP and networking conventions ONLY
115
+ class _StabilityMode(Enum):
116
+ DEFAULT = "default"
116
117
  HTTP = "http"
117
- # http/dup - emit both the old and the stable HTTP and networking conventions
118
118
  HTTP_DUP = "http/dup"
119
- # default - continue emitting old experimental HTTP and networking conventions
120
- DEFAULT = "default"
119
+ DATABASE = "database"
120
+ DATABASE_DUP = "database/dup"
121
121
 
122
122
 
123
- def _report_new(mode):
124
- return mode.name != _HTTPStabilityMode.DEFAULT.name
123
+ def _report_new(mode: _StabilityMode):
124
+ return mode != _StabilityMode.DEFAULT
125
125
 
126
126
 
127
- def _report_old(mode):
128
- return mode.name != _HTTPStabilityMode.HTTP.name
127
+ def _report_old(mode: _StabilityMode):
128
+ return mode not in (_StabilityMode.HTTP, _StabilityMode.DATABASE)
129
129
 
130
130
 
131
131
  class _OpenTelemetrySemanticConventionStability:
@@ -135,35 +135,61 @@ class _OpenTelemetrySemanticConventionStability:
135
135
 
136
136
  @classmethod
137
137
  def _initialize(cls):
138
- with _OpenTelemetrySemanticConventionStability._lock:
139
- if not _OpenTelemetrySemanticConventionStability._initialized:
140
- # Users can pass in comma delimited string for opt-in options
141
- # Only values for http stability are supported for now
142
- opt_in = os.environ.get(OTEL_SEMCONV_STABILITY_OPT_IN, "")
143
- opt_in_list = []
144
- if opt_in:
145
- opt_in_list = [s.strip() for s in opt_in.split(",")]
146
- http_opt_in = _HTTPStabilityMode.DEFAULT
147
- if opt_in_list:
148
- # Process http opt-in
149
- # http/dup takes priority over http
150
- if _HTTPStabilityMode.HTTP_DUP.value in opt_in_list:
151
- http_opt_in = _HTTPStabilityMode.HTTP_DUP
152
- elif _HTTPStabilityMode.HTTP.value in opt_in_list:
153
- http_opt_in = _HTTPStabilityMode.HTTP
154
- _OpenTelemetrySemanticConventionStability._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING[
155
- _OpenTelemetryStabilitySignalType.HTTP
156
- ] = http_opt_in
157
- _OpenTelemetrySemanticConventionStability._initialized = True
138
+ with cls._lock:
139
+ if cls._initialized:
140
+ return
141
+
142
+ # Users can pass in comma delimited string for opt-in options
143
+ # Only values for http and database stability are supported for now
144
+ opt_in = os.environ.get(OTEL_SEMCONV_STABILITY_OPT_IN)
145
+
146
+ if not opt_in:
147
+ # early return in case of default
148
+ cls._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING = {
149
+ _OpenTelemetryStabilitySignalType.HTTP: _StabilityMode.DEFAULT,
150
+ _OpenTelemetryStabilitySignalType.DATABASE: _StabilityMode.DEFAULT,
151
+ }
152
+ cls._initialized = True
153
+ return
154
+
155
+ opt_in_list = [s.strip() for s in opt_in.split(",")]
156
+
157
+ cls._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING[
158
+ _OpenTelemetryStabilitySignalType.HTTP
159
+ ] = cls._filter_mode(
160
+ opt_in_list, _StabilityMode.HTTP, _StabilityMode.HTTP_DUP
161
+ )
162
+
163
+ cls._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING[
164
+ _OpenTelemetryStabilitySignalType.DATABASE
165
+ ] = cls._filter_mode(
166
+ opt_in_list,
167
+ _StabilityMode.DATABASE,
168
+ _StabilityMode.DATABASE_DUP,
169
+ )
170
+
171
+ cls._initialized = True
172
+
173
+ @staticmethod
174
+ def _filter_mode(opt_in_list, stable_mode, dup_mode):
175
+ # Process semconv stability opt-in
176
+ # http/dup,database/dup has higher precedence over http,database
177
+ if dup_mode.value in opt_in_list:
178
+ return dup_mode
179
+
180
+ return (
181
+ stable_mode
182
+ if stable_mode.value in opt_in_list
183
+ else _StabilityMode.DEFAULT
184
+ )
158
185
 
159
186
  @classmethod
160
- # Get OpenTelemetry opt-in mode based off of signal type (http, messaging, etc.)
161
187
  def _get_opentelemetry_stability_opt_in_mode(
162
- cls,
163
- signal_type: _OpenTelemetryStabilitySignalType,
164
- ) -> _HTTPStabilityMode:
165
- return _OpenTelemetrySemanticConventionStability._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING.get(
166
- signal_type, _HTTPStabilityMode.DEFAULT
188
+ cls, signal_type: _OpenTelemetryStabilitySignalType
189
+ ) -> _StabilityMode:
190
+ # Get OpenTelemetry opt-in mode based off of signal type (http, messaging, etc.)
191
+ return cls._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING.get(
192
+ signal_type, _StabilityMode.DEFAULT
167
193
  )
168
194
 
169
195
 
@@ -171,14 +197,12 @@ def _filter_semconv_duration_attrs(
171
197
  attrs,
172
198
  old_attrs,
173
199
  new_attrs,
174
- sem_conv_opt_in_mode=_HTTPStabilityMode.DEFAULT,
200
+ sem_conv_opt_in_mode=_StabilityMode.DEFAULT,
175
201
  ):
176
202
  filtered_attrs = {}
177
203
  # duration is two different metrics depending on sem_conv_opt_in_mode, so no DUP attributes
178
204
  allowed_attributes = (
179
- new_attrs
180
- if sem_conv_opt_in_mode == _HTTPStabilityMode.HTTP
181
- else old_attrs
205
+ new_attrs if sem_conv_opt_in_mode == _StabilityMode.HTTP else old_attrs
182
206
  )
183
207
  for key, val in attrs.items():
184
208
  if key in allowed_attributes:
@@ -190,7 +214,7 @@ def _filter_semconv_active_request_count_attr(
190
214
  attrs,
191
215
  old_attrs,
192
216
  new_attrs,
193
- sem_conv_opt_in_mode=_HTTPStabilityMode.DEFAULT,
217
+ sem_conv_opt_in_mode=_StabilityMode.DEFAULT,
194
218
  ):
195
219
  filtered_attrs = {}
196
220
  if _report_old(sem_conv_opt_in_mode):
@@ -367,10 +391,11 @@ def _set_status(
367
391
  status_code: int,
368
392
  status_code_str: str,
369
393
  server_span: bool = True,
370
- sem_conv_opt_in_mode: _HTTPStabilityMode = _HTTPStabilityMode.DEFAULT,
394
+ sem_conv_opt_in_mode: _StabilityMode = _StabilityMode.DEFAULT,
371
395
  ):
372
396
  if status_code < 0:
373
- metrics_attributes[ERROR_TYPE] = status_code_str
397
+ if _report_new(sem_conv_opt_in_mode):
398
+ metrics_attributes[ERROR_TYPE] = status_code_str
374
399
  if span.is_recording():
375
400
  if _report_new(sem_conv_opt_in_mode):
376
401
  span.set_attribute(ERROR_TYPE, status_code_str)
@@ -404,7 +429,7 @@ def _set_status(
404
429
 
405
430
 
406
431
  # Get schema version based off of opt-in mode
407
- def _get_schema_url(mode: _HTTPStabilityMode) -> str:
408
- if mode is _HTTPStabilityMode.DEFAULT:
432
+ def _get_schema_url(mode: _StabilityMode) -> str:
433
+ if mode is _StabilityMode.DEFAULT:
409
434
  return "https://opentelemetry.io/schemas/1.11.0"
410
435
  return SpanAttributes.SCHEMA_URL
@@ -18,187 +18,203 @@
18
18
  libraries = [
19
19
  {
20
20
  "library": "openai >= 1.26.0",
21
- "instrumentation": "opentelemetry-instrumentation-openai-v2==2.0.0.dev",
21
+ "instrumentation": "opentelemetry-instrumentation-openai-v2==2.2b0.dev",
22
+ },
23
+ {
24
+ "library": "google-cloud-aiplatform >= 1.64",
25
+ "instrumentation": "opentelemetry-instrumentation-vertexai==2.1b0.dev",
22
26
  },
23
27
  {
24
28
  "library": "aio_pika >= 7.2.0, < 10.0.0",
25
- "instrumentation": "opentelemetry-instrumentation-aio-pika==0.49b2",
29
+ "instrumentation": "opentelemetry-instrumentation-aio-pika==0.51b0",
26
30
  },
27
31
  {
28
32
  "library": "aiohttp ~= 3.0",
29
- "instrumentation": "opentelemetry-instrumentation-aiohttp-client==0.49b2",
33
+ "instrumentation": "opentelemetry-instrumentation-aiohttp-client==0.51b0",
30
34
  },
31
35
  {
32
36
  "library": "aiohttp ~= 3.0",
33
- "instrumentation": "opentelemetry-instrumentation-aiohttp-server==0.49b2",
37
+ "instrumentation": "opentelemetry-instrumentation-aiohttp-server==0.51b0",
34
38
  },
35
39
  {
36
40
  "library": "aiokafka >= 0.8, < 1.0",
37
- "instrumentation": "opentelemetry-instrumentation-aiokafka==0.49b2",
41
+ "instrumentation": "opentelemetry-instrumentation-aiokafka==0.51b0",
38
42
  },
39
43
  {
40
44
  "library": "aiopg >= 0.13.0, < 2.0.0",
41
- "instrumentation": "opentelemetry-instrumentation-aiopg==0.49b2",
45
+ "instrumentation": "opentelemetry-instrumentation-aiopg==0.51b0",
42
46
  },
43
47
  {
44
48
  "library": "asgiref ~= 3.0",
45
- "instrumentation": "opentelemetry-instrumentation-asgi==0.49b2",
49
+ "instrumentation": "opentelemetry-instrumentation-asgi==0.51b0",
46
50
  },
47
51
  {
48
52
  "library": "asyncpg >= 0.12.0",
49
- "instrumentation": "opentelemetry-instrumentation-asyncpg==0.49b2",
53
+ "instrumentation": "opentelemetry-instrumentation-asyncpg==0.51b0",
50
54
  },
51
55
  {
52
56
  "library": "boto~=2.0",
53
- "instrumentation": "opentelemetry-instrumentation-boto==0.49b2",
57
+ "instrumentation": "opentelemetry-instrumentation-boto==0.51b0",
54
58
  },
55
59
  {
56
60
  "library": "boto3 ~= 1.0",
57
- "instrumentation": "opentelemetry-instrumentation-boto3sqs==0.49b2",
61
+ "instrumentation": "opentelemetry-instrumentation-boto3sqs==0.51b0",
58
62
  },
59
63
  {
60
64
  "library": "botocore ~= 1.0",
61
- "instrumentation": "opentelemetry-instrumentation-botocore==0.49b2",
65
+ "instrumentation": "opentelemetry-instrumentation-botocore==0.51b0",
62
66
  },
63
67
  {
64
68
  "library": "cassandra-driver ~= 3.25",
65
- "instrumentation": "opentelemetry-instrumentation-cassandra==0.49b2",
69
+ "instrumentation": "opentelemetry-instrumentation-cassandra==0.51b0",
66
70
  },
67
71
  {
68
72
  "library": "scylla-driver ~= 3.25",
69
- "instrumentation": "opentelemetry-instrumentation-cassandra==0.49b2",
73
+ "instrumentation": "opentelemetry-instrumentation-cassandra==0.51b0",
70
74
  },
71
75
  {
72
76
  "library": "celery >= 4.0, < 6.0",
73
- "instrumentation": "opentelemetry-instrumentation-celery==0.49b2",
77
+ "instrumentation": "opentelemetry-instrumentation-celery==0.51b0",
78
+ },
79
+ {
80
+ "library": "click >= 8.1.3, < 9.0.0",
81
+ "instrumentation": "opentelemetry-instrumentation-click==0.51b0",
74
82
  },
75
83
  {
76
- "library": "confluent-kafka >= 1.8.2, <= 2.4.0",
77
- "instrumentation": "opentelemetry-instrumentation-confluent-kafka==0.49b2",
84
+ "library": "confluent-kafka >= 1.8.2, <= 2.7.0",
85
+ "instrumentation": "opentelemetry-instrumentation-confluent-kafka==0.51b0",
78
86
  },
79
87
  {
80
88
  "library": "django >= 1.10",
81
- "instrumentation": "opentelemetry-instrumentation-django==0.49b2",
89
+ "instrumentation": "opentelemetry-instrumentation-django==0.51b0",
82
90
  },
83
91
  {
84
92
  "library": "elasticsearch >= 6.0",
85
- "instrumentation": "opentelemetry-instrumentation-elasticsearch==0.49b2",
93
+ "instrumentation": "opentelemetry-instrumentation-elasticsearch==0.51b0",
86
94
  },
87
95
  {
88
- "library": "falcon >= 1.4.1, < 3.1.2",
89
- "instrumentation": "opentelemetry-instrumentation-falcon==0.49b2",
96
+ "library": "falcon >= 1.4.1, < 5.0.0",
97
+ "instrumentation": "opentelemetry-instrumentation-falcon==0.51b0",
90
98
  },
91
99
  {
92
100
  "library": "fastapi ~= 0.58",
93
- "instrumentation": "opentelemetry-instrumentation-fastapi==0.49b2",
101
+ "instrumentation": "opentelemetry-instrumentation-fastapi==0.51b0",
94
102
  },
95
103
  {
96
104
  "library": "flask >= 1.0",
97
- "instrumentation": "opentelemetry-instrumentation-flask==0.49b2",
105
+ "instrumentation": "opentelemetry-instrumentation-flask==0.51b0",
98
106
  },
99
107
  {
100
108
  "library": "grpcio >= 1.42.0",
101
- "instrumentation": "opentelemetry-instrumentation-grpc==0.49b2",
109
+ "instrumentation": "opentelemetry-instrumentation-grpc==0.51b0",
102
110
  },
103
111
  {
104
112
  "library": "httpx >= 0.18.0",
105
- "instrumentation": "opentelemetry-instrumentation-httpx==0.49b2",
113
+ "instrumentation": "opentelemetry-instrumentation-httpx==0.51b0",
106
114
  },
107
115
  {
108
116
  "library": "jinja2 >= 2.7, < 4.0",
109
- "instrumentation": "opentelemetry-instrumentation-jinja2==0.49b2",
117
+ "instrumentation": "opentelemetry-instrumentation-jinja2==0.51b0",
110
118
  },
111
119
  {
112
120
  "library": "kafka-python >= 2.0, < 3.0",
113
- "instrumentation": "opentelemetry-instrumentation-kafka-python==0.49b2",
121
+ "instrumentation": "opentelemetry-instrumentation-kafka-python==0.51b0",
114
122
  },
115
123
  {
116
124
  "library": "kafka-python-ng >= 2.0, < 3.0",
117
- "instrumentation": "opentelemetry-instrumentation-kafka-python==0.49b2",
125
+ "instrumentation": "opentelemetry-instrumentation-kafka-python==0.51b0",
118
126
  },
119
127
  {
120
128
  "library": "mysql-connector-python >= 8.0, < 10.0",
121
- "instrumentation": "opentelemetry-instrumentation-mysql==0.49b2",
129
+ "instrumentation": "opentelemetry-instrumentation-mysql==0.51b0",
122
130
  },
123
131
  {
124
132
  "library": "mysqlclient < 3",
125
- "instrumentation": "opentelemetry-instrumentation-mysqlclient==0.49b2",
133
+ "instrumentation": "opentelemetry-instrumentation-mysqlclient==0.51b0",
126
134
  },
127
135
  {
128
136
  "library": "pika >= 0.12.0",
129
- "instrumentation": "opentelemetry-instrumentation-pika==0.49b2",
137
+ "instrumentation": "opentelemetry-instrumentation-pika==0.51b0",
130
138
  },
131
139
  {
132
140
  "library": "psycopg >= 3.1.0",
133
- "instrumentation": "opentelemetry-instrumentation-psycopg==0.49b2",
141
+ "instrumentation": "opentelemetry-instrumentation-psycopg==0.51b0",
134
142
  },
135
143
  {
136
144
  "library": "psycopg2 >= 2.7.3.1",
137
- "instrumentation": "opentelemetry-instrumentation-psycopg2==0.49b2",
145
+ "instrumentation": "opentelemetry-instrumentation-psycopg2==0.51b0",
146
+ },
147
+ {
148
+ "library": "psycopg2-binary >= 2.7.3.1",
149
+ "instrumentation": "opentelemetry-instrumentation-psycopg2==0.51b0",
138
150
  },
139
151
  {
140
152
  "library": "pymemcache >= 1.3.5, < 5",
141
- "instrumentation": "opentelemetry-instrumentation-pymemcache==0.49b2",
153
+ "instrumentation": "opentelemetry-instrumentation-pymemcache==0.51b0",
142
154
  },
143
155
  {
144
156
  "library": "pymongo >= 3.1, < 5.0",
145
- "instrumentation": "opentelemetry-instrumentation-pymongo==0.49b2",
157
+ "instrumentation": "opentelemetry-instrumentation-pymongo==0.51b0",
158
+ },
159
+ {
160
+ "library": "pymssql >= 2.1.5, < 3",
161
+ "instrumentation": "opentelemetry-instrumentation-pymssql==0.51b0",
146
162
  },
147
163
  {
148
164
  "library": "PyMySQL < 2",
149
- "instrumentation": "opentelemetry-instrumentation-pymysql==0.49b2",
165
+ "instrumentation": "opentelemetry-instrumentation-pymysql==0.51b0",
150
166
  },
151
167
  {
152
168
  "library": "pyramid >= 1.7",
153
- "instrumentation": "opentelemetry-instrumentation-pyramid==0.49b2",
169
+ "instrumentation": "opentelemetry-instrumentation-pyramid==0.51b0",
154
170
  },
155
171
  {
156
172
  "library": "redis >= 2.6",
157
- "instrumentation": "opentelemetry-instrumentation-redis==0.49b2",
173
+ "instrumentation": "opentelemetry-instrumentation-redis==0.51b0",
158
174
  },
159
175
  {
160
176
  "library": "remoulade >= 0.50",
161
- "instrumentation": "opentelemetry-instrumentation-remoulade==0.49b2",
177
+ "instrumentation": "opentelemetry-instrumentation-remoulade==0.51b0",
162
178
  },
163
179
  {
164
180
  "library": "requests ~= 2.0",
165
- "instrumentation": "opentelemetry-instrumentation-requests==0.49b2",
181
+ "instrumentation": "opentelemetry-instrumentation-requests==0.51b0",
166
182
  },
167
183
  {
168
184
  "library": "sqlalchemy >= 1.0.0, < 2.1.0",
169
- "instrumentation": "opentelemetry-instrumentation-sqlalchemy==0.49b2",
185
+ "instrumentation": "opentelemetry-instrumentation-sqlalchemy==0.51b0",
170
186
  },
171
187
  {
172
188
  "library": "starlette ~= 0.13.0",
173
- "instrumentation": "opentelemetry-instrumentation-starlette==0.49b2",
189
+ "instrumentation": "opentelemetry-instrumentation-starlette==0.51b0",
174
190
  },
175
191
  {
176
192
  "library": "psutil >= 5",
177
- "instrumentation": "opentelemetry-instrumentation-system-metrics==0.49b2",
193
+ "instrumentation": "opentelemetry-instrumentation-system-metrics==0.51b0",
178
194
  },
179
195
  {
180
196
  "library": "tornado >= 5.1.1",
181
- "instrumentation": "opentelemetry-instrumentation-tornado==0.49b2",
197
+ "instrumentation": "opentelemetry-instrumentation-tornado==0.51b0",
182
198
  },
183
199
  {
184
200
  "library": "tortoise-orm >= 0.17.0",
185
- "instrumentation": "opentelemetry-instrumentation-tortoiseorm==0.49b2",
201
+ "instrumentation": "opentelemetry-instrumentation-tortoiseorm==0.51b0",
186
202
  },
187
203
  {
188
204
  "library": "pydantic >= 1.10.2",
189
- "instrumentation": "opentelemetry-instrumentation-tortoiseorm==0.49b2",
205
+ "instrumentation": "opentelemetry-instrumentation-tortoiseorm==0.51b0",
190
206
  },
191
207
  {
192
208
  "library": "urllib3 >= 1.0.0, < 3.0.0",
193
- "instrumentation": "opentelemetry-instrumentation-urllib3==0.49b2",
209
+ "instrumentation": "opentelemetry-instrumentation-urllib3==0.51b0",
194
210
  },
195
211
  ]
196
212
  default_instrumentations = [
197
- "opentelemetry-instrumentation-asyncio==0.49b2",
198
- "opentelemetry-instrumentation-dbapi==0.49b2",
199
- "opentelemetry-instrumentation-logging==0.49b2",
200
- "opentelemetry-instrumentation-sqlite3==0.49b2",
201
- "opentelemetry-instrumentation-threading==0.49b2",
202
- "opentelemetry-instrumentation-urllib==0.49b2",
203
- "opentelemetry-instrumentation-wsgi==0.49b2",
213
+ "opentelemetry-instrumentation-asyncio==0.51b0",
214
+ "opentelemetry-instrumentation-dbapi==0.51b0",
215
+ "opentelemetry-instrumentation-logging==0.51b0",
216
+ "opentelemetry-instrumentation-sqlite3==0.51b0",
217
+ "opentelemetry-instrumentation-threading==0.51b0",
218
+ "opentelemetry-instrumentation-urllib==0.51b0",
219
+ "opentelemetry-instrumentation-wsgi==0.51b0",
204
220
  ]
@@ -12,8 +12,10 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ from __future__ import annotations
16
+
15
17
  from logging import getLogger
16
- from typing import Collection, Optional, Union
18
+ from typing import Collection
17
19
 
18
20
  from packaging.requirements import InvalidRequirement, Requirement
19
21
 
@@ -27,10 +29,10 @@ logger = getLogger(__name__)
27
29
 
28
30
 
29
31
  class DependencyConflict:
30
- required: str = None
31
- found: Optional[str] = None
32
+ required: str | None = None
33
+ found: str | None = None
32
34
 
33
- def __init__(self, required, found=None):
35
+ def __init__(self, required: str | None, found: str | None = None):
34
36
  self.required = required
35
37
  self.found = found
36
38
 
@@ -40,25 +42,26 @@ class DependencyConflict:
40
42
 
41
43
  def get_dist_dependency_conflicts(
42
44
  dist: Distribution,
43
- ) -> Optional[DependencyConflict]:
45
+ ) -> DependencyConflict | None:
44
46
  instrumentation_deps = []
45
47
  extra = "extra"
46
48
  instruments = "instruments"
47
49
  instruments_marker = {extra: instruments}
48
- for dep in dist.requires:
49
- if extra not in dep or instruments not in dep:
50
- continue
50
+ if dist.requires:
51
+ for dep in dist.requires:
52
+ if extra not in dep or instruments not in dep:
53
+ continue
51
54
 
52
- req = Requirement(dep)
53
- if req.marker.evaluate(instruments_marker):
54
- instrumentation_deps.append(req)
55
+ req = Requirement(dep)
56
+ if req.marker.evaluate(instruments_marker):
57
+ instrumentation_deps.append(req)
55
58
 
56
59
  return get_dependency_conflicts(instrumentation_deps)
57
60
 
58
61
 
59
62
  def get_dependency_conflicts(
60
- deps: Collection[Union[str, Requirement]],
61
- ) -> Optional[DependencyConflict]:
63
+ deps: Collection[str | Requirement],
64
+ ) -> DependencyConflict | None:
62
65
  for dep in deps:
63
66
  if isinstance(dep, Requirement):
64
67
  req = dep
@@ -17,9 +17,11 @@
17
17
  OpenTelemetry Base Instrumentor
18
18
  """
19
19
 
20
+ from __future__ import annotations
21
+
20
22
  from abc import ABC, abstractmethod
21
23
  from logging import getLogger
22
- from typing import Collection, Optional
24
+ from typing import Any, Collection
23
25
 
24
26
  from opentelemetry.instrumentation._semconv import (
25
27
  _OpenTelemetrySemanticConventionStability,
@@ -33,7 +35,7 @@ _LOG = getLogger(__name__)
33
35
 
34
36
 
35
37
  class BaseInstrumentor(ABC):
36
- """An ABC for instrumentors
38
+ """An ABC for instrumentors.
37
39
 
38
40
  Child classes of this ABC should instrument specific third
39
41
  party libraries or frameworks either by using the
@@ -74,18 +76,18 @@ class BaseInstrumentor(ABC):
74
76
  is present in the environment.
75
77
  """
76
78
 
77
- def _instrument(self, **kwargs):
79
+ def _instrument(self, **kwargs: Any):
78
80
  """Instrument the library"""
79
81
 
80
82
  @abstractmethod
81
- def _uninstrument(self, **kwargs):
83
+ def _uninstrument(self, **kwargs: Any):
82
84
  """Uninstrument the library"""
83
85
 
84
- def _check_dependency_conflicts(self) -> Optional[DependencyConflict]:
86
+ def _check_dependency_conflicts(self) -> DependencyConflict | None:
85
87
  dependencies = self.instrumentation_dependencies()
86
88
  return get_dependency_conflicts(dependencies)
87
89
 
88
- def instrument(self, **kwargs):
90
+ def instrument(self, **kwargs: Any):
89
91
  """Instrument the library
90
92
 
91
93
  This method will be called without any optional arguments by the
@@ -117,7 +119,7 @@ class BaseInstrumentor(ABC):
117
119
  self._is_instrumented_by_opentelemetry = True
118
120
  return result
119
121
 
120
- def uninstrument(self, **kwargs):
122
+ def uninstrument(self, **kwargs: Any):
121
123
  """Uninstrument the library
122
124
 
123
125
  See ``BaseInstrumentor.instrument`` for more information regarding the
@@ -12,11 +12,13 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ from __future__ import annotations
16
+
15
17
  import urllib.parse
16
18
  from contextlib import contextmanager
17
19
  from importlib import import_module
18
20
  from re import escape, sub
19
- from typing import Dict, Iterable, Sequence, Union
21
+ from typing import Any, Dict, Generator, Sequence
20
22
 
21
23
  from wrapt import ObjectProxy
22
24
 
@@ -44,9 +46,9 @@ _SUPPRESS_INSTRUMENTATION_KEY_PLAIN = (
44
46
 
45
47
 
46
48
  def extract_attributes_from_object(
47
- obj: any, attributes: Sequence[str], existing: Dict[str, str] = None
49
+ obj: Any, attributes: Sequence[str], existing: Dict[str, str] | None = None
48
50
  ) -> Dict[str, str]:
49
- extracted = {}
51
+ extracted: dict[str, str] = {}
50
52
  if existing:
51
53
  extracted.update(existing)
52
54
  for attr in attributes:
@@ -81,7 +83,7 @@ def http_status_to_status_code(
81
83
  return StatusCode.ERROR
82
84
 
83
85
 
84
- def unwrap(obj: Union[object, str], attr: str):
86
+ def unwrap(obj: object, attr: str):
85
87
  """Given a function that was wrapped by wrapt.wrap_function_wrapper, unwrap it
86
88
 
87
89
  The object containing the function to unwrap may be passed as dotted module path string.
@@ -152,7 +154,7 @@ def _start_internal_or_server_span(
152
154
  return span, token
153
155
 
154
156
 
155
- def _url_quote(s) -> str: # pylint: disable=invalid-name
157
+ def _url_quote(s: Any) -> str: # pylint: disable=invalid-name
156
158
  if not isinstance(s, (str, bytes)):
157
159
  return s
158
160
  quoted = urllib.parse.quote(s)
@@ -163,13 +165,13 @@ def _url_quote(s) -> str: # pylint: disable=invalid-name
163
165
  return quoted.replace("%", "%%")
164
166
 
165
167
 
166
- def _get_opentelemetry_values() -> dict:
168
+ def _get_opentelemetry_values() -> dict[str, Any]:
167
169
  """
168
170
  Return the OpenTelemetry Trace and Span IDs if Span ID is set in the
169
171
  OpenTelemetry execution context.
170
172
  """
171
173
  # Insert the W3C TraceContext generated
172
- _headers = {}
174
+ _headers: dict[str, Any] = {}
173
175
  propagator.inject(_headers)
174
176
  return _headers
175
177
 
@@ -196,7 +198,7 @@ def is_http_instrumentation_enabled() -> bool:
196
198
 
197
199
 
198
200
  @contextmanager
199
- def _suppress_instrumentation(*keys: str) -> Iterable[None]:
201
+ def _suppress_instrumentation(*keys: str) -> Generator[None]:
200
202
  """Suppress instrumentation within the context."""
201
203
  ctx = context.get_current()
202
204
  for key in keys:
@@ -209,7 +211,7 @@ def _suppress_instrumentation(*keys: str) -> Iterable[None]:
209
211
 
210
212
 
211
213
  @contextmanager
212
- def suppress_instrumentation() -> Iterable[None]:
214
+ def suppress_instrumentation() -> Generator[None]:
213
215
  """Suppress instrumentation within the context."""
214
216
  with _suppress_instrumentation(
215
217
  _SUPPRESS_INSTRUMENTATION_KEY, _SUPPRESS_INSTRUMENTATION_KEY_PLAIN
@@ -218,7 +220,7 @@ def suppress_instrumentation() -> Iterable[None]:
218
220
 
219
221
 
220
222
  @contextmanager
221
- def suppress_http_instrumentation() -> Iterable[None]:
223
+ def suppress_http_instrumentation() -> Generator[None]:
222
224
  """Suppress instrumentation within the context."""
223
225
  with _suppress_instrumentation(_SUPPRESS_HTTP_INSTRUMENTATION_KEY):
224
226
  yield
@@ -12,4 +12,4 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- __version__ = "0.49b2"
15
+ __version__ = "0.51b0"
@@ -86,3 +86,19 @@ class TestDependencyConflicts(TestBase):
86
86
  str(conflict),
87
87
  'DependencyConflict: requested: "test-pkg~=1.0; extra == "instruments"" but found: "None"',
88
88
  )
89
+
90
+ def test_get_dist_dependency_conflicts_requires_none(self):
91
+ class MockDistribution(Distribution):
92
+ def locate_file(self, path):
93
+ pass
94
+
95
+ def read_text(self, filename):
96
+ pass
97
+
98
+ @property
99
+ def requires(self):
100
+ return None
101
+
102
+ dist = MockDistribution()
103
+ conflict = get_dist_dependency_conflicts(dist)
104
+ self.assertTrue(conflict is None)
@@ -0,0 +1,258 @@
1
+ # Copyright The OpenTelemetry Authors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import os
16
+ from unittest import TestCase
17
+ from unittest.mock import Mock, patch
18
+
19
+ from opentelemetry.instrumentation._semconv import (
20
+ OTEL_SEMCONV_STABILITY_OPT_IN,
21
+ _OpenTelemetrySemanticConventionStability,
22
+ _OpenTelemetryStabilitySignalType,
23
+ _set_status,
24
+ _StabilityMode,
25
+ )
26
+ from opentelemetry.trace.status import StatusCode
27
+
28
+
29
+ def stability_mode(mode):
30
+ def decorator(test_case):
31
+ @patch.dict(os.environ, {OTEL_SEMCONV_STABILITY_OPT_IN: mode})
32
+ def wrapper(*args, **kwargs):
33
+ _OpenTelemetrySemanticConventionStability._initialized = False
34
+ _OpenTelemetrySemanticConventionStability._initialize()
35
+ return test_case(*args, **kwargs)
36
+
37
+ return wrapper
38
+
39
+ return decorator
40
+
41
+
42
+ class TestOpenTelemetrySemConvStability(TestCase):
43
+ @stability_mode("")
44
+ def test_default_mode(self):
45
+ self.assertEqual(
46
+ _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
47
+ _OpenTelemetryStabilitySignalType.HTTP
48
+ ),
49
+ _StabilityMode.DEFAULT,
50
+ )
51
+ self.assertEqual(
52
+ _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
53
+ _OpenTelemetryStabilitySignalType.DATABASE
54
+ ),
55
+ _StabilityMode.DEFAULT,
56
+ )
57
+
58
+ @stability_mode("http")
59
+ def test_http_stable_mode(self):
60
+ self.assertEqual(
61
+ _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
62
+ _OpenTelemetryStabilitySignalType.HTTP
63
+ ),
64
+ _StabilityMode.HTTP,
65
+ )
66
+
67
+ @stability_mode("http/dup")
68
+ def test_http_dup_mode(self):
69
+ self.assertEqual(
70
+ _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
71
+ _OpenTelemetryStabilitySignalType.HTTP
72
+ ),
73
+ _StabilityMode.HTTP_DUP,
74
+ )
75
+
76
+ @stability_mode("database")
77
+ def test_database_stable_mode(self):
78
+ self.assertEqual(
79
+ _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
80
+ _OpenTelemetryStabilitySignalType.DATABASE
81
+ ),
82
+ _StabilityMode.DATABASE,
83
+ )
84
+
85
+ @stability_mode("database/dup")
86
+ def test_database_dup_mode(self):
87
+ self.assertEqual(
88
+ _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
89
+ _OpenTelemetryStabilitySignalType.DATABASE
90
+ ),
91
+ _StabilityMode.DATABASE_DUP,
92
+ )
93
+
94
+ @stability_mode("database,http")
95
+ def test_multiple_stability_database_http_modes(self):
96
+ self.assertEqual(
97
+ _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
98
+ _OpenTelemetryStabilitySignalType.DATABASE
99
+ ),
100
+ _StabilityMode.DATABASE,
101
+ )
102
+ self.assertEqual(
103
+ _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
104
+ _OpenTelemetryStabilitySignalType.HTTP
105
+ ),
106
+ _StabilityMode.HTTP,
107
+ )
108
+
109
+ @stability_mode("database,http/dup")
110
+ def test_multiple_stability_database_http_dup_modes(self):
111
+ self.assertEqual(
112
+ _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
113
+ _OpenTelemetryStabilitySignalType.DATABASE
114
+ ),
115
+ _StabilityMode.DATABASE,
116
+ )
117
+ self.assertEqual(
118
+ _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
119
+ _OpenTelemetryStabilitySignalType.HTTP
120
+ ),
121
+ _StabilityMode.HTTP_DUP,
122
+ )
123
+
124
+ @stability_mode("database/dup,http")
125
+ def test_multiple_stability_database_dup_http_stable_modes(self):
126
+ self.assertEqual(
127
+ _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
128
+ _OpenTelemetryStabilitySignalType.DATABASE
129
+ ),
130
+ _StabilityMode.DATABASE_DUP,
131
+ )
132
+ self.assertEqual(
133
+ _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
134
+ _OpenTelemetryStabilitySignalType.HTTP
135
+ ),
136
+ _StabilityMode.HTTP,
137
+ )
138
+
139
+ @stability_mode("database,database/dup,http,http/dup")
140
+ def test_stability_mode_dup_precedence(self):
141
+ self.assertEqual(
142
+ _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
143
+ _OpenTelemetryStabilitySignalType.DATABASE
144
+ ),
145
+ _StabilityMode.DATABASE_DUP,
146
+ )
147
+ self.assertEqual(
148
+ _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
149
+ _OpenTelemetryStabilitySignalType.HTTP
150
+ ),
151
+ _StabilityMode.HTTP_DUP,
152
+ )
153
+
154
+
155
+ class TestOpenTelemetrySemConvStabilityHTTP(TestCase):
156
+ def test_set_status_for_non_http_code_with_recording_span(self):
157
+ span = Mock()
158
+ span.is_recording.return_value = True
159
+ metric_attributes = {}
160
+ _set_status(
161
+ span,
162
+ metric_attributes,
163
+ -1,
164
+ "Exception",
165
+ sem_conv_opt_in_mode=_StabilityMode.DEFAULT,
166
+ )
167
+
168
+ self.assertIsNone(metric_attributes.get("error.type"))
169
+ span.set_attribute.assert_not_called()
170
+ status_call = span.set_status.call_args[0][0]
171
+ self.assertEqual(status_call.status_code, StatusCode.ERROR)
172
+ self.assertEqual(
173
+ status_call.description, "Non-integer HTTP status: " + "Exception"
174
+ )
175
+
176
+ def test_status_code_http_default(self):
177
+ span = Mock()
178
+ metrics_attributes = {}
179
+ _set_status(
180
+ span=span,
181
+ metrics_attributes=metrics_attributes,
182
+ status_code=404,
183
+ status_code_str="404",
184
+ server_span=True,
185
+ sem_conv_opt_in_mode=_StabilityMode.DEFAULT,
186
+ )
187
+ # Verify only old conventions are emitted
188
+ span.set_attribute.assert_called_with("http.status_code", 404)
189
+ self.assertIn("http.status_code", metrics_attributes)
190
+ self.assertNotIn("http.response_status_code", metrics_attributes)
191
+
192
+ def test_status_code_http_stable(self):
193
+ span = Mock()
194
+ metrics_attributes = {}
195
+ _set_status(
196
+ span=span,
197
+ metrics_attributes=metrics_attributes,
198
+ status_code=200,
199
+ status_code_str="200",
200
+ server_span=True,
201
+ sem_conv_opt_in_mode=_StabilityMode.HTTP,
202
+ )
203
+ # Verify only new conventions are emitted
204
+ span.set_attribute.assert_called_with("http.response.status_code", 200)
205
+ self.assertIn("http.response.status_code", metrics_attributes)
206
+ self.assertNotIn("http.status_code", metrics_attributes)
207
+
208
+ def test_status_code_http_dup(self):
209
+ span = Mock()
210
+ metrics_attributes = {}
211
+ _set_status(
212
+ span=span,
213
+ metrics_attributes=metrics_attributes,
214
+ status_code=500,
215
+ status_code_str="500",
216
+ server_span=True,
217
+ sem_conv_opt_in_mode=_StabilityMode.HTTP_DUP,
218
+ )
219
+ # Verify both old and new conventions are emitted
220
+ span.set_attribute.assert_any_call("http.status_code", 500)
221
+ span.set_attribute.assert_any_call("http.response.status_code", 500)
222
+ self.assertIn("http.status_code", metrics_attributes)
223
+ self.assertIn("http.response.status_code", metrics_attributes)
224
+
225
+ def test_error_status_code_new_mode(self):
226
+ span = Mock()
227
+ metrics_attributes = {}
228
+ _set_status(
229
+ span=span,
230
+ metrics_attributes=metrics_attributes,
231
+ status_code=500,
232
+ status_code_str="500",
233
+ server_span=True,
234
+ sem_conv_opt_in_mode=_StabilityMode.HTTP,
235
+ )
236
+ # Verify error type is set for new conventions
237
+ span.set_attribute.assert_any_call("error.type", "500")
238
+ self.assertIn("error.type", metrics_attributes)
239
+ self.assertEqual(metrics_attributes["error.type"], "500")
240
+
241
+ def test_non_recording_span(self):
242
+ span = Mock()
243
+ span.is_recording.return_value = False
244
+ metrics_attributes = {}
245
+ _set_status(
246
+ span=span,
247
+ metrics_attributes=metrics_attributes,
248
+ status_code=200,
249
+ status_code_str="200",
250
+ server_span=True,
251
+ sem_conv_opt_in_mode=_StabilityMode.HTTP_DUP,
252
+ )
253
+ # Verify no span attributes are set if not recording
254
+ span.set_attribute.assert_not_called()
255
+ span.set_status.assert_not_called()
256
+ # Verify status code set for metrics independent of tracing decision
257
+ self.assertIn("http.status_code", metrics_attributes)
258
+ self.assertIn("http.response.status_code", metrics_attributes)