opentelemetry-python-contrib-external-valkey 0.64b0__tar.gz → 0.66b0__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 (11) hide show
  1. {opentelemetry_python_contrib_external_valkey-0.64b0 → opentelemetry_python_contrib_external_valkey-0.66b0}/PKG-INFO +3 -3
  2. opentelemetry_python_contrib_external_valkey-0.66b0/pyproject.toml +43 -0
  3. opentelemetry_python_contrib_external_valkey-0.64b0/pyproject.toml → opentelemetry_python_contrib_external_valkey-0.66b0/pyproject.toml.orig +4 -4
  4. {opentelemetry_python_contrib_external_valkey-0.64b0 → opentelemetry_python_contrib_external_valkey-0.66b0}/src/opentelemetry/instrumentation/valkey/__init__.py +35 -98
  5. {opentelemetry_python_contrib_external_valkey-0.64b0 → opentelemetry_python_contrib_external_valkey-0.66b0}/src/opentelemetry/instrumentation/valkey/custom_types.py +5 -10
  6. {opentelemetry_python_contrib_external_valkey-0.64b0 → opentelemetry_python_contrib_external_valkey-0.66b0}/src/opentelemetry/instrumentation/valkey/util.py +39 -47
  7. {opentelemetry_python_contrib_external_valkey-0.64b0 → opentelemetry_python_contrib_external_valkey-0.66b0}/src/opentelemetry/instrumentation/valkey/version.py +1 -1
  8. {opentelemetry_python_contrib_external_valkey-0.64b0 → opentelemetry_python_contrib_external_valkey-0.66b0}/LICENSE +0 -0
  9. {opentelemetry_python_contrib_external_valkey-0.64b0 → opentelemetry_python_contrib_external_valkey-0.66b0}/README.md +0 -0
  10. {opentelemetry_python_contrib_external_valkey-0.64b0 → opentelemetry_python_contrib_external_valkey-0.66b0}/src/opentelemetry/instrumentation/valkey/package.py +0 -0
  11. {opentelemetry_python_contrib_external_valkey-0.64b0 → opentelemetry_python_contrib_external_valkey-0.66b0}/src/opentelemetry/instrumentation/valkey/py.typed +0 -0
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opentelemetry-python-contrib-external-valkey
3
- Version: 0.64b0
3
+ Version: 0.66b0
4
4
  Summary: OpenTelemetry Valkey instrumentation
5
5
  Author: Philip Meier
6
6
  Author-email: Philip Meier <github.pmeier@posteo.de>
7
7
  License-Expression: Apache-2.0
8
8
  License-File: LICENSE
9
9
  Requires-Dist: opentelemetry-api~=1.12
10
- Requires-Dist: opentelemetry-instrumentation==0.64b0
11
- Requires-Dist: opentelemetry-semantic-conventions==0.64b0
10
+ Requires-Dist: opentelemetry-instrumentation==0.66b0
11
+ Requires-Dist: opentelemetry-semantic-conventions==0.66b0
12
12
  Requires-Dist: wrapt>=1.12.1
13
13
  Requires-Python: >=3.10
14
14
  Description-Content-Type: text/markdown
@@ -0,0 +1,43 @@
1
+ [project]
2
+ name = "opentelemetry-python-contrib-external-valkey"
3
+ description = "OpenTelemetry Valkey instrumentation"
4
+ license = "Apache-2.0"
5
+ license-files = ["LICENSE"]
6
+ readme = "README.md"
7
+ requires-python = ">=3.10"
8
+ version = "0.66b0"
9
+ dependencies = [
10
+ "opentelemetry-api~=1.12",
11
+ "opentelemetry-instrumentation==0.66b0",
12
+ "opentelemetry-semantic-conventions==0.66b0",
13
+ "wrapt>=1.12.1",
14
+ ]
15
+
16
+ [[project.authors]]
17
+ name = "Philip Meier"
18
+ email = "github.pmeier@posteo.de"
19
+
20
+ [project.entry-points.opentelemetry_instrumentor]
21
+ redis = "opentelemetry.instrumentation.valkey:ValkeyInstrumentor"
22
+
23
+ [dependency-groups]
24
+ instruments = ["valkey>=6.1.0"]
25
+ dev = [
26
+ { include-group = "instruments" },
27
+ "fakeredis>=2.25",
28
+ "opentelemetry-test-utils==0.66b0",
29
+ "pytest>=7.4.4",
30
+ "pytest-asyncio>=1.2.0",
31
+ ]
32
+
33
+ [build-system]
34
+ requires = ["uv_build>=0.9.11,<0.10.0"]
35
+ build-backend = "uv_build"
36
+
37
+ [tool.uv.build-backend]
38
+ module-name = "opentelemetry.instrumentation.valkey"
39
+
40
+ [tool.pytest.ini_options]
41
+ minversion = "6.0"
42
+ addopts = "-ra --tb=short"
43
+ testpaths = ["tests"]
@@ -8,12 +8,12 @@ authors = [
8
8
  ]
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
11
- version = "0.64b0"
11
+ version = "0.66b0"
12
12
 
13
13
  dependencies = [
14
14
  "opentelemetry-api~=1.12",
15
- "opentelemetry-instrumentation==0.64b0",
16
- "opentelemetry-semantic-conventions==0.64b0",
15
+ "opentelemetry-instrumentation==0.66b0",
16
+ "opentelemetry-semantic-conventions==0.66b0",
17
17
  "wrapt>=1.12.1",
18
18
  ]
19
19
 
@@ -22,7 +22,7 @@ instruments = ["valkey>=6.1.0"]
22
22
  dev = [
23
23
  {include-group = "instruments"},
24
24
  "fakeredis>=2.25",
25
- "opentelemetry-test-utils==0.64b0",
25
+ "opentelemetry-test-utils==0.66b0",
26
26
  "pytest>=7.4.4",
27
27
  "pytest-asyncio>=1.2.0",
28
28
  ]
@@ -136,7 +136,8 @@ API
136
136
  from __future__ import annotations
137
137
 
138
138
  import logging
139
- from typing import TYPE_CHECKING, Any, Callable, Collection
139
+ from collections.abc import Callable, Collection
140
+ from typing import TYPE_CHECKING, Any
140
141
 
141
142
  import valkey
142
143
  from wrapt import wrap_function_wrapper
@@ -172,7 +173,7 @@ from opentelemetry.trace import (
172
173
  )
173
174
 
174
175
  if TYPE_CHECKING:
175
- from typing import Awaitable
176
+ from collections.abc import Awaitable
176
177
 
177
178
  import valkey.asyncio.client
178
179
  import valkey.asyncio.cluster
@@ -199,9 +200,7 @@ _REDIS_ASYNCIO_CLUSTER_VERSION = (4, 3, 2)
199
200
 
200
201
 
201
202
  _CLIENT_ASYNCIO_SUPPORT = valkey.VERSION >= _REDIS_ASYNCIO_VERSION
202
- _CLIENT_ASYNCIO_CLUSTER_SUPPORT = (
203
- valkey.VERSION >= _REDIS_ASYNCIO_CLUSTER_VERSION
204
- )
203
+ _CLIENT_ASYNCIO_CLUSTER_SUPPORT = valkey.VERSION >= _REDIS_ASYNCIO_CLUSTER_VERSION
205
204
  _CLIENT_CLUSTER_SUPPORT = valkey.VERSION >= _REDIS_CLUSTER_VERSION
206
205
  _CLIENT_BEFORE_V3 = valkey.VERSION < (3, 0, 0)
207
206
 
@@ -230,12 +229,8 @@ def _traced_execute_factory(
230
229
  _OpenTelemetryStabilitySignalType.HTTP,
231
230
  )
232
231
  )
233
- db_sem_conv_opt_in_mode = sem_conv_opt_in_modes[
234
- _OpenTelemetryStabilitySignalType.DATABASE
235
- ]
236
- http_sem_conv_opt_in_mode = sem_conv_opt_in_modes[
237
- _OpenTelemetryStabilitySignalType.HTTP
238
- ]
232
+ db_sem_conv_opt_in_mode = sem_conv_opt_in_modes[_OpenTelemetryStabilitySignalType.DATABASE]
233
+ http_sem_conv_opt_in_mode = sem_conv_opt_in_modes[_OpenTelemetryStabilitySignalType.HTTP]
239
234
 
240
235
  def _traced_execute_command(
241
236
  func: Callable[..., R],
@@ -248,9 +243,7 @@ def _traced_execute_factory(
248
243
 
249
244
  query = _format_command_args(args)
250
245
  name = _build_span_name(instance, args)
251
- with tracer.start_as_current_span(
252
- name, kind=trace.SpanKind.CLIENT
253
- ) as span:
246
+ with tracer.start_as_current_span(name, kind=trace.SpanKind.CLIENT) as span:
254
247
  if span.is_recording():
255
248
  span_attrs = {}
256
249
  _set_db_statement(span_attrs, query, db_sem_conv_opt_in_mode)
@@ -292,12 +285,8 @@ def _traced_execute_pipeline_factory(
292
285
  _OpenTelemetryStabilitySignalType.HTTP,
293
286
  )
294
287
  )
295
- db_sem_conv_opt_in_mode = sem_conv_opt_in_modes[
296
- _OpenTelemetryStabilitySignalType.DATABASE
297
- ]
298
- http_sem_conv_opt_in_mode = sem_conv_opt_in_modes[
299
- _OpenTelemetryStabilitySignalType.HTTP
300
- ]
288
+ db_sem_conv_opt_in_mode = sem_conv_opt_in_modes[_OpenTelemetryStabilitySignalType.DATABASE]
289
+ http_sem_conv_opt_in_mode = sem_conv_opt_in_modes[_OpenTelemetryStabilitySignalType.HTTP]
301
290
 
302
291
  def _traced_execute_pipeline(
303
292
  func: Callable[..., R],
@@ -314,14 +303,10 @@ def _traced_execute_pipeline_factory(
314
303
  span_name,
315
304
  ) = _build_span_meta_data_for_pipeline(instance)
316
305
  exception = None
317
- with tracer.start_as_current_span(
318
- span_name, kind=trace.SpanKind.CLIENT
319
- ) as span:
306
+ with tracer.start_as_current_span(span_name, kind=trace.SpanKind.CLIENT) as span:
320
307
  if span.is_recording():
321
308
  span_attrs = {}
322
- _set_db_statement(
323
- span_attrs, resource, db_sem_conv_opt_in_mode
324
- )
309
+ _set_db_statement(span_attrs, resource, db_sem_conv_opt_in_mode)
325
310
  span_attrs["db.valkey.pipeline_length"] = len(command_stack)
326
311
 
327
312
  # Set all DB attributes
@@ -364,12 +349,8 @@ def _async_traced_execute_factory(
364
349
  _OpenTelemetryStabilitySignalType.HTTP,
365
350
  )
366
351
  )
367
- db_sem_conv_opt_in_mode = sem_conv_opt_in_modes[
368
- _OpenTelemetryStabilitySignalType.DATABASE
369
- ]
370
- http_sem_conv_opt_in_mode = sem_conv_opt_in_modes[
371
- _OpenTelemetryStabilitySignalType.HTTP
372
- ]
352
+ db_sem_conv_opt_in_mode = sem_conv_opt_in_modes[_OpenTelemetryStabilitySignalType.DATABASE]
353
+ http_sem_conv_opt_in_mode = sem_conv_opt_in_modes[_OpenTelemetryStabilitySignalType.HTTP]
373
354
 
374
355
  async def _async_traced_execute_command(
375
356
  func: Callable[..., Awaitable[R]],
@@ -383,9 +364,7 @@ def _async_traced_execute_factory(
383
364
  query = _format_command_args(args)
384
365
  name = _build_span_name(instance, args)
385
366
 
386
- with tracer.start_as_current_span(
387
- name, kind=trace.SpanKind.CLIENT
388
- ) as span:
367
+ with tracer.start_as_current_span(name, kind=trace.SpanKind.CLIENT) as span:
389
368
  if span.is_recording():
390
369
  span_attrs = {}
391
370
  _set_db_statement(span_attrs, query, db_sem_conv_opt_in_mode)
@@ -422,12 +401,8 @@ def _async_traced_execute_pipeline_factory(
422
401
  _OpenTelemetryStabilitySignalType.HTTP,
423
402
  )
424
403
  )
425
- db_sem_conv_opt_in_mode = sem_conv_opt_in_modes[
426
- _OpenTelemetryStabilitySignalType.DATABASE
427
- ]
428
- http_sem_conv_opt_in_mode = sem_conv_opt_in_modes[
429
- _OpenTelemetryStabilitySignalType.HTTP
430
- ]
404
+ db_sem_conv_opt_in_mode = sem_conv_opt_in_modes[_OpenTelemetryStabilitySignalType.DATABASE]
405
+ http_sem_conv_opt_in_mode = sem_conv_opt_in_modes[_OpenTelemetryStabilitySignalType.HTTP]
431
406
 
432
407
  async def _async_traced_execute_pipeline(
433
408
  func: Callable[..., Awaitable[R]],
@@ -446,14 +421,10 @@ def _async_traced_execute_pipeline_factory(
446
421
 
447
422
  exception = None
448
423
 
449
- with tracer.start_as_current_span(
450
- span_name, kind=trace.SpanKind.CLIENT
451
- ) as span:
424
+ with tracer.start_as_current_span(span_name, kind=trace.SpanKind.CLIENT) as span:
452
425
  if span.is_recording():
453
426
  span_attrs = {}
454
- _set_db_statement(
455
- span_attrs, resource, db_sem_conv_opt_in_mode
456
- )
427
+ _set_db_statement(span_attrs, resource, db_sem_conv_opt_in_mode)
457
428
  span_attrs["db.valkey.pipeline_length"] = len(command_stack)
458
429
 
459
430
  # Set all DB attributes
@@ -491,18 +462,12 @@ def _instrument(
491
462
  request_hook: RequestHook | None = None,
492
463
  response_hook: ResponseHook | None = None,
493
464
  ):
494
- _traced_execute_command = _traced_execute_factory(
495
- tracer, request_hook, response_hook
496
- )
497
- _traced_execute_pipeline = _traced_execute_pipeline_factory(
498
- tracer, request_hook, response_hook
499
- )
465
+ _traced_execute_command = _traced_execute_factory(tracer, request_hook, response_hook)
466
+ _traced_execute_pipeline = _traced_execute_pipeline_factory(tracer, request_hook, response_hook)
500
467
  pipeline_class = "BasePipeline" if _CLIENT_BEFORE_V3 else "Pipeline"
501
468
  valkey_class = "StrictValkey" if _CLIENT_BEFORE_V3 else "Valkey"
502
469
 
503
- wrap_function_wrapper(
504
- "valkey", f"{valkey_class}.execute_command", _traced_execute_command
505
- )
470
+ wrap_function_wrapper("valkey", f"{valkey_class}.execute_command", _traced_execute_command)
506
471
  wrap_function_wrapper(
507
472
  "valkey.client",
508
473
  f"{pipeline_class}.execute",
@@ -525,12 +490,8 @@ def _instrument(
525
490
  _traced_execute_pipeline,
526
491
  )
527
492
 
528
- _async_traced_execute_command = _async_traced_execute_factory(
529
- tracer, request_hook, response_hook
530
- )
531
- _async_traced_execute_pipeline = _async_traced_execute_pipeline_factory(
532
- tracer, request_hook, response_hook
533
- )
493
+ _async_traced_execute_command = _async_traced_execute_factory(tracer, request_hook, response_hook)
494
+ _async_traced_execute_pipeline = _async_traced_execute_pipeline_factory(tracer, request_hook, response_hook)
534
495
  if _CLIENT_ASYNCIO_SUPPORT:
535
496
  wrap_function_wrapper(
536
497
  "valkey.asyncio",
@@ -567,60 +528,40 @@ def _instrument_client(
567
528
  response_hook: ResponseHook | None = None,
568
529
  ):
569
530
  # first, handle async clients and cluster clients
570
- _async_traced_execute = _async_traced_execute_factory(
571
- tracer, request_hook, response_hook
572
- )
573
- _async_traced_execute_pipeline = _async_traced_execute_pipeline_factory(
574
- tracer, request_hook, response_hook
575
- )
531
+ _async_traced_execute = _async_traced_execute_factory(tracer, request_hook, response_hook)
532
+ _async_traced_execute_pipeline = _async_traced_execute_pipeline_factory(tracer, request_hook, response_hook)
576
533
 
577
534
  if _CLIENT_ASYNCIO_SUPPORT and isinstance(client, valkey.asyncio.Valkey):
578
535
 
579
536
  def _async_pipeline_wrapper(func, instance, args, kwargs):
580
537
  result = func(*args, **kwargs)
581
- wrap_function_wrapper(
582
- result, "execute", _async_traced_execute_pipeline
583
- )
584
- wrap_function_wrapper(
585
- result, "immediate_execute_command", _async_traced_execute
586
- )
538
+ wrap_function_wrapper(result, "execute", _async_traced_execute_pipeline)
539
+ wrap_function_wrapper(result, "immediate_execute_command", _async_traced_execute)
587
540
  return result
588
541
 
589
542
  wrap_function_wrapper(client, "execute_command", _async_traced_execute)
590
543
  wrap_function_wrapper(client, "pipeline", _async_pipeline_wrapper)
591
544
  return
592
545
 
593
- if _CLIENT_ASYNCIO_CLUSTER_SUPPORT and isinstance(
594
- client, valkey.asyncio.ValkeyCluster
595
- ):
546
+ if _CLIENT_ASYNCIO_CLUSTER_SUPPORT and isinstance(client, valkey.asyncio.ValkeyCluster):
596
547
 
597
548
  def _async_cluster_pipeline_wrapper(func, instance, args, kwargs):
598
549
  result = func(*args, **kwargs)
599
- wrap_function_wrapper(
600
- result, "execute", _async_traced_execute_pipeline
601
- )
550
+ wrap_function_wrapper(result, "execute", _async_traced_execute_pipeline)
602
551
  return result
603
552
 
604
553
  wrap_function_wrapper(client, "execute_command", _async_traced_execute)
605
- wrap_function_wrapper(
606
- client, "pipeline", _async_cluster_pipeline_wrapper
607
- )
554
+ wrap_function_wrapper(client, "pipeline", _async_cluster_pipeline_wrapper)
608
555
  return
609
556
  # for valkey.client.Valkey, valkey.Cluster and v3.0.0 valkey.client.StrictValkey
610
557
  # the wrappers are the same
611
- _traced_execute = _traced_execute_factory(
612
- tracer, request_hook, response_hook
613
- )
614
- _traced_execute_pipeline = _traced_execute_pipeline_factory(
615
- tracer, request_hook, response_hook
616
- )
558
+ _traced_execute = _traced_execute_factory(tracer, request_hook, response_hook)
559
+ _traced_execute_pipeline = _traced_execute_pipeline_factory(tracer, request_hook, response_hook)
617
560
 
618
561
  def _pipeline_wrapper(func, instance, args, kwargs):
619
562
  result = func(*args, **kwargs)
620
563
  wrap_function_wrapper(result, "execute", _traced_execute_pipeline)
621
- wrap_function_wrapper(
622
- result, "immediate_execute_command", _traced_execute
623
- )
564
+ wrap_function_wrapper(result, "immediate_execute_command", _traced_execute)
624
565
  return result
625
566
 
626
567
  wrap_function_wrapper(
@@ -776,9 +717,7 @@ class ValkeyInstrumentor(BaseInstrumentor):
776
717
  )
777
718
  setattr(client, _INSTRUMENTATION_ATTR, True)
778
719
  else:
779
- _logger.warning(
780
- "Attempting to instrument Valkey connection while already instrumented"
781
- )
720
+ _logger.warning("Attempting to instrument Valkey connection while already instrumented")
782
721
 
783
722
  @staticmethod
784
723
  def uninstrument_client(
@@ -801,9 +740,7 @@ class ValkeyInstrumentor(BaseInstrumentor):
801
740
  # remain instrumented (pipelines should usually have a short span)
802
741
  unwrap(client, "pipeline")
803
742
  else:
804
- _logger.warning(
805
- "Attempting to un-instrument Valkey connection that wasn't instrumented"
806
- )
743
+ _logger.warning("Attempting to un-instrument Valkey connection that wasn't instrumented")
807
744
 
808
745
  def instrumentation_dependencies(self) -> Collection[str]:
809
746
  """Return a list of python packages with versions that the will be instrumented."""
@@ -4,7 +4,8 @@
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
- from typing import Any, Callable, TypeVar
7
+ from collections.abc import Callable
8
+ from typing import Any, TypeVar
8
9
 
9
10
  import valkey.asyncio.client
10
11
  import valkey.asyncio.cluster
@@ -14,9 +15,7 @@ import valkey.connection
14
15
 
15
16
  from opentelemetry.trace import Span
16
17
 
17
- RequestHook = Callable[
18
- [Span, valkey.connection.Connection, list[Any], dict[str, Any]], None
19
- ]
18
+ RequestHook = Callable[[Span, valkey.connection.Connection, list[Any], dict[str, Any]], None]
20
19
  ResponseHook = Callable[[Span, valkey.connection.Connection, Any], None]
21
20
 
22
21
  AsyncPipelineInstance = TypeVar(
@@ -24,15 +23,11 @@ AsyncPipelineInstance = TypeVar(
24
23
  valkey.asyncio.client.Pipeline,
25
24
  valkey.asyncio.cluster.ClusterPipeline,
26
25
  )
27
- AsyncValkeyInstance = TypeVar(
28
- "AsyncValkeyInstance", valkey.asyncio.Valkey, valkey.asyncio.ValkeyCluster
29
- )
26
+ AsyncValkeyInstance = TypeVar("AsyncValkeyInstance", valkey.asyncio.Valkey, valkey.asyncio.ValkeyCluster)
30
27
  PipelineInstance = TypeVar(
31
28
  "PipelineInstance",
32
29
  valkey.client.Pipeline,
33
30
  valkey.cluster.ClusterPipeline,
34
31
  )
35
- ValkeyInstance = TypeVar(
36
- "ValkeyInstance", valkey.client.Valkey, valkey.cluster.ValkeyCluster
37
- )
32
+ ValkeyInstance = TypeVar("ValkeyInstance", valkey.client.Valkey, valkey.cluster.ValkeyCluster)
38
33
  R = TypeVar("R")
@@ -37,21 +37,18 @@ if TYPE_CHECKING:
37
37
  _FIELD_TYPES = ["NUMERIC", "TEXT", "GEO", "TAG", "VECTOR"]
38
38
 
39
39
 
40
- def _extract_conn_attributes(
41
- conn_kwargs, db_sem_conv_opt_in_mode, http_sem_conv_opt_in_mode
42
- ):
40
+ def _extract_conn_attributes(conn_kwargs, db_sem_conv_opt_in_mode, http_sem_conv_opt_in_mode):
43
41
  """Transform valkey conn info into dict"""
44
42
  attributes = {}
45
- _set_db_system(
46
- attributes, DbSystemValues.REDIS.value, db_sem_conv_opt_in_mode
47
- )
43
+ _set_db_system(attributes, DbSystemValues.REDIS.value, db_sem_conv_opt_in_mode)
48
44
 
49
- db = conn_kwargs.get("db", 0)
45
+ # valkey-py may pass db=None explicitly (e.g. from_url); treat that as 0.
46
+ db = conn_kwargs.get("db")
47
+ if db is None:
48
+ db = 0
50
49
  _set_db_redis_database_index(attributes, db, db_sem_conv_opt_in_mode)
51
50
  if "path" in conn_kwargs:
52
- _set_http_net_peer_name_client(
53
- attributes, conn_kwargs.get("path", ""), http_sem_conv_opt_in_mode
54
- )
51
+ _set_http_net_peer_name_client(attributes, conn_kwargs.get("path", ""), http_sem_conv_opt_in_mode)
55
52
  _set_net_transport(
56
53
  attributes,
57
54
  NetTransportValues.OTHER.value,
@@ -91,10 +88,7 @@ def _format_command_args(args: list[str]):
91
88
  out_str = " ".join(out)
92
89
 
93
90
  if len(out_str) > cmd_max_len:
94
- out_str = (
95
- out_str[: cmd_max_len - len(value_too_long_mark)]
96
- + value_too_long_mark
97
- )
91
+ out_str = out_str[: cmd_max_len - len(value_too_long_mark)] + value_too_long_mark
98
92
  else:
99
93
  out_str = ""
100
94
 
@@ -119,7 +113,11 @@ def _set_connection_attributes(
119
113
  db_sem_conv_opt_in_mode,
120
114
  http_sem_conv_opt_in_mode,
121
115
  ) -> None:
122
- if not span.is_recording() or not hasattr(conn, "connection_pool"):
116
+ if (
117
+ not span.is_recording()
118
+ or not hasattr(conn, "connection_pool")
119
+ or not hasattr(conn.connection_pool, "connection_kwargs")
120
+ ):
123
121
  return
124
122
  for key, value in _extract_conn_attributes(
125
123
  conn.connection_pool.connection_kwargs,
@@ -129,9 +127,7 @@ def _set_connection_attributes(
129
127
  span.set_attribute(key, value)
130
128
 
131
129
 
132
- def _build_span_name(
133
- instance: ValkeyInstance | AsyncValkeyInstance, cmd_args: tuple[Any, ...]
134
- ) -> str:
130
+ def _build_span_name(instance: ValkeyInstance | AsyncValkeyInstance, cmd_args: tuple[Any, ...]) -> str:
135
131
  if len(cmd_args) > 0 and cmd_args[0]:
136
132
  if cmd_args[0] == "FT.SEARCH":
137
133
  name = "valkey.search"
@@ -140,14 +136,14 @@ def _build_span_name(
140
136
  else:
141
137
  name = cmd_args[0]
142
138
  else:
143
- name = instance.connection_pool.connection_kwargs.get("db", 0)
144
- return name
139
+ name = instance.connection_pool.connection_kwargs.get("db")
140
+ if name is None:
141
+ name = 0
142
+ return str(name)
145
143
 
146
144
 
147
145
  def _add_create_attributes(span: Span, args: tuple[Any, ...]):
148
- _set_span_attribute_if_value(
149
- span, "valkey.create_index.index", _value_or_none(args, 1)
150
- )
146
+ _set_span_attribute_if_value(span, "valkey.create_index.index", _value_or_none(args, 1))
151
147
  # According to: https://github.com/valkey/valkey-py/blob/master/valkey/commands/search/commands.py#L155 schema is last argument for execute command
152
148
  try:
153
149
  schema_index = args.index("SCHEMA")
@@ -170,12 +166,8 @@ def _add_create_attributes(span: Span, args: tuple[Any, ...]):
170
166
 
171
167
 
172
168
  def _add_search_attributes(span: Span, response, args):
173
- _set_span_attribute_if_value(
174
- span, "valkey.search.index", _value_or_none(args, 1)
175
- )
176
- _set_span_attribute_if_value(
177
- span, "valkey.search.query", _value_or_none(args, 2)
178
- )
169
+ _set_span_attribute_if_value(span, "valkey.search.index", _value_or_none(args, 1))
170
+ _set_span_attribute_if_value(span, "valkey.search.query", _value_or_none(args, 2))
179
171
  # Parse response from search
180
172
  # https://valkey.io/docs/latest/commands/ft.search/
181
173
  # Response in format:
@@ -183,9 +175,7 @@ def _add_search_attributes(span: Span, response, args):
183
175
  # Returned documents in array format:
184
176
  # [first_field_name, first_field_value, second_field_name, second_field_value ...]
185
177
  number_of_returned_documents = _value_or_none(response, 0)
186
- _set_span_attribute_if_value(
187
- span, "valkey.search.total", number_of_returned_documents
188
- )
178
+ _set_span_attribute_if_value(span, "valkey.search.total", number_of_returned_documents)
189
179
  if "NOCONTENT" in args or not number_of_returned_documents:
190
180
  return
191
181
  for document_number in range(number_of_returned_documents):
@@ -204,24 +194,26 @@ def _build_span_meta_data_for_pipeline(
204
194
  instance: PipelineInstance | AsyncPipelineInstance,
205
195
  ) -> tuple[list[Any], str, str]:
206
196
  try:
207
- command_stack = (
208
- instance.command_stack
209
- if hasattr(instance, "command_stack")
210
- else instance._command_stack
211
- )
197
+ # valkey-py 6+ ClusterPipeline no longer updates ``command_stack``;
198
+ # queued commands are tracked on the execution strategy instead. The
199
+ # sync cluster strategy exposes them via a public ``command_queue``
200
+ # property, while the async cluster strategy only has the private
201
+ # ``_command_queue`` attribute. Fall back to ``command_stack`` /
202
+ # ``_command_stack`` for non-cluster pipelines and older valkey-py.
203
+ execution_strategy = getattr(instance, "_execution_strategy", None)
204
+ if execution_strategy is not None and hasattr(execution_strategy, "command_queue"):
205
+ command_stack = execution_strategy.command_queue
206
+ elif execution_strategy is not None and hasattr(execution_strategy, "_command_queue"):
207
+ command_stack = execution_strategy._command_queue
208
+ elif hasattr(instance, "command_stack"):
209
+ command_stack = instance.command_stack
210
+ else:
211
+ command_stack = instance._command_stack
212
212
 
213
- cmds = [
214
- _format_command_args(c.args if hasattr(c, "args") else c[0])
215
- for c in command_stack
216
- ]
213
+ cmds = [_format_command_args(c.args if hasattr(c, "args") else c[0]) for c in command_stack]
217
214
  resource = "\n".join(cmds)
218
215
 
219
- span_name = " ".join(
220
- [
221
- (c.args[0] if hasattr(c, "args") else c[0][0])
222
- for c in command_stack
223
- ]
224
- )
216
+ span_name = " ".join([(c.args[0] if hasattr(c, "args") else c[0][0]) for c in command_stack])
225
217
  except (AttributeError, IndexError):
226
218
  command_stack = []
227
219
  resource = ""
@@ -2,4 +2,4 @@
2
2
  # Copyright 2025 Philip Meier
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
- __version__ = "0.64b0"
5
+ __version__ = "0.66b0"