Skip to content
Merged
39 changes: 27 additions & 12 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ opentelemetry-api = { version = "^1.11.1", optional = true }
opentelemetry-sdk = { version = "^1.11.1", optional = true }
protobuf = "~4.21"
python = "^3.7"
python-dateutil = { version = "^2.8.2", python = "<3.11" }
types-protobuf = "~3.20"
typing-extensions = "^4.2.0"

Expand Down Expand Up @@ -58,6 +59,9 @@ twine = "^4.0.1"
opentelemetry = ["opentelemetry-api", "opentelemetry-sdk"]
grpc = ["grpc"]

[tool.poetry.group.dev.dependencies]
python-dateutil = "^2.8.2"
Comment thread
Oracen marked this conversation as resolved.
Outdated

[tool.poe.tasks]
build-develop = ["build-bridge-develop"]
build-bridge-develop = "python scripts/setup_bridge.py develop"
Expand Down
20 changes: 19 additions & 1 deletion temporalio/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
import dataclasses
import inspect
import json
import sys
from abc import ABC, abstractmethod
from dataclasses import dataclass
from datetime import datetime
from enum import IntEnum
from typing import (
Any,
Callable,
Dict,
List,
Mapping,
Expand Down Expand Up @@ -677,6 +679,21 @@ def encode_search_attribute_values(
return default().payload_converter.to_payloads([safe_vals])[0]


def get_iso_datetime_parser() -> Callable[[str], datetime]:
Comment thread
Oracen marked this conversation as resolved.
Outdated
"""Isolates system version check and returns relevant datetime passer

Returns:
A callable to parse date strings into datetimes.
"""
if sys.version_info >= (3, 11):
return datetime.fromisoformat # noqa
else:
# Isolate import for py > 3.11, as dependency only installed for < 3.11
from dateutil import parser

return parser.isoparse
Comment thread
Oracen marked this conversation as resolved.


def decode_search_attributes(
api: temporalio.api.common.v1.SearchAttributes,
) -> temporalio.common.SearchAttributes:
Expand All @@ -697,7 +714,8 @@ def decode_search_attributes(
val = [val]
# Convert each item to datetime if necessary
if v.metadata.get("type") == b"Datetime":
val = [datetime.fromisoformat(v) for v in val]
parser = get_iso_datetime_parser()
val = [parser(v) for v in val]
ret[k] = val
return ret

Expand Down
39 changes: 38 additions & 1 deletion tests/test_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import sys
from collections import deque
from dataclasses import dataclass
from datetime import datetime
from datetime import datetime, timezone
from enum import Enum, IntEnum
from typing import (
Any,
Expand Down Expand Up @@ -164,6 +164,43 @@ def test_encode_search_attribute_values():
temporalio.converter.encode_search_attribute_values(["foo", 123])


def test_decode_search_attributes():
"""Tests decode from protobuf for python types"""

def payload(key, dtype, data, encoding=None):
if encoding is None:
encoding = {"encoding": b"json/plain"}
check = temporalio.api.common.v1.Payload(
data=bytes(data, encoding="utf-8"),
metadata={"type": bytes(dtype, encoding="utf-8"), **encoding},
)
return temporalio.api.common.v1.SearchAttributes(indexed_fields={key: check})

# Check basic keyword parsing works
kw_check = temporalio.converter.decode_search_attributes(
payload("kw", "Keyword", '"test-id"')
)
assert kw_check["kw"][0] == "test-id"

# Ensure original DT functionality works
dt_check = temporalio.converter.decode_search_attributes(
payload("dt", "Datetime", '"2020-01-01T00:00:00"')
)
assert dt_check["dt"][0] == datetime(2020, 1, 1, 0, 0, 0)

# Check timezone aware works as server is using ISO 8601
dttz_check = temporalio.converter.decode_search_attributes(
payload("dt", "Datetime", '"2020-01-01T00:00:00Z"')
)
assert dttz_check["dt"][0] == datetime(2020, 1, 1, 0, 0, 0, tzinfo=timezone.utc)

# Check timezone aware, hour offset
dttz_check = temporalio.converter.decode_search_attributes(
payload("dt", "Datetime", '"2020-01-01T00:00:00+00:00"')
)
assert dttz_check["dt"][0] == datetime(2020, 1, 1, 0, 0, 0, tzinfo=timezone.utc)


NewIntType = NewType("NewIntType", int)
MyDataClassAlias = MyDataClass

Expand Down