diff --git a/temporalio/converter.py b/temporalio/converter.py index cd7c2bbf5..190fda0e6 100644 --- a/temporalio/converter.py +++ b/temporalio/converter.py @@ -1590,7 +1590,9 @@ def value_to_type( elif key_type is type(None): key = {"null": None}[key] - if not isinstance(key, key_type): + # Can't call isinstance if key_type is a newtype + is_newtype = getattr(key_type, "__supertype__", None) + if is_newtype or not isinstance(key, key_type): key = value_to_type(key_type, key, custom_converters) except Exception as err: raise TypeError( diff --git a/tests/test_converter.py b/tests/test_converter.py index 94fff5725..e274b10d9 100644 --- a/tests/test_converter.py +++ b/tests/test_converter.py @@ -53,6 +53,7 @@ _JSONTypeConverterUnhandled, decode_search_attributes, encode_search_attribute_values, + value_to_type, ) from temporalio.exceptions import ApplicationError, FailureError @@ -91,6 +92,14 @@ class DatetimeClass: datetime: datetime +MyNewTypeStr = NewType("MyNewTypeStr", str) + + +@dataclass +class NewTypeMessage: + data: dict[MyNewTypeStr, str] + + async def test_converter_default(): async def assert_payload( input, @@ -215,6 +224,22 @@ async def assert_payload( type_hint=DatetimeClass, ) + # Newtype String + await assert_payload( + MyNewTypeStr("somestr"), + "json/plain", + '"somestr"', + type_hint=MyNewTypeStr, + ) + + # Newtype String key + await assert_payload( + NewTypeMessage({MyNewTypeStr("key"): "value"}), + "json/plain", + '{"data":{"key":"value"}}', + type_hint=NewTypeMessage, + ) + def test_binary_proto(): # We have to test this separately because by default it never encodes