From c3f495f873bade35e4886184b6b866bcb7a6cd17 Mon Sep 17 00:00:00 2001 From: Raphael Fakhri <153192858+RaphaelFakhri@users.noreply.github.com> Date: Sat, 10 Oct 2026 19:33:42 +0000 Subject: [PATCH] Keep whole-number Double values in typed search attributes --- changelog/fixed/rounded-pebble-skipping.md | 1 + temporalio/converter/_search_attributes.py | 10 +++++++++- tests/test_converter.py | 17 +++++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 changelog/fixed/rounded-pebble-skipping.md diff --git a/changelog/fixed/rounded-pebble-skipping.md b/changelog/fixed/rounded-pebble-skipping.md new file mode 100644 index 0000000000..f7efc0c9a2 --- /dev/null +++ b/changelog/fixed/rounded-pebble-skipping.md @@ -0,0 +1 @@ +Keep whole-number `Double` values in typed search attributes returned by `list_workflows()` and other visibility results, instead of silently dropping them. diff --git a/temporalio/converter/_search_attributes.py b/temporalio/converter/_search_attributes.py index 4a8191bbda..08cb187097 100644 --- a/temporalio/converter/_search_attributes.py +++ b/temporalio/converter/_search_attributes.py @@ -4,6 +4,7 @@ from collections.abc import Sequence from datetime import datetime +from typing import Any import temporalio.api.common.v1 import temporalio.common @@ -182,7 +183,7 @@ def decode_typed_search_attributes( ) if not key: continue - val = conv.from_payload(v) + val: Any = conv.from_payload(v) # If the value is a list but the type is not keyword list, pull out # single item or consider this an invalid value and ignore if ( @@ -200,6 +201,13 @@ def decode_typed_search_attributes( parser = _get_iso_datetime_parser() # We will let this throw val = parser(val) + elif ( + key.indexed_value_type + == temporalio.common.SearchAttributeIndexedValueType.DOUBLE + and type(val) is int + ): + # Visibility returns a whole-number Double as a JSON integer + val = float(val) # If the value isn't the right type, we need to ignore if isinstance(val, key.origin_value_type): pairs.append(temporalio.common.SearchAttributePair(key, val)) diff --git a/tests/test_converter.py b/tests/test_converter.py index 930c92d09e..3237b71a75 100644 --- a/tests/test_converter.py +++ b/tests/test_converter.py @@ -51,6 +51,7 @@ TransferTypeConverter, create_payload_validation_error, decode_search_attributes, + decode_typed_search_attributes, encode_search_attribute_values, encode_typed_search_attribute_value, transfer_type_convertible, @@ -488,6 +489,22 @@ def test_encode_typed_search_attribute_value_datetime_requires_timezone(): assert payload.metadata["type"] == b"Datetime" +def test_decode_typed_search_attributes_whole_number_double(): + # Visibility returns a whole-number Double as a JSON integer + key = temporalio.common.SearchAttributeKey.for_float("score") + api = temporalio.api.common.v1.SearchAttributes( + indexed_fields={ + "score": temporalio.api.common.v1.Payload( + data=b"5", + metadata={"type": b"Double", "encoding": b"json/plain"}, + ) + } + ) + val = decode_typed_search_attributes(api).get(key) + assert val == 5.0 + assert type(val) is float + + def test_decode_search_attributes(): """Tests decode from protobuf for python types"""