-
Notifications
You must be signed in to change notification settings - Fork 82
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Search attributes #43
Changes from all commits
1b0fdc2
f3f6962
75d0e48
21b4f2d
8dad0fe
40cc1bb
5585bdc
4a10595
140df7f
182b195
1123afb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ | |
import sys | ||
from abc import ABC, abstractmethod | ||
from dataclasses import dataclass | ||
from datetime import datetime | ||
from typing import Any, Callable, Dict, Iterable, List, Mapping, Optional, Tuple, Type | ||
|
||
import dacite | ||
|
@@ -16,6 +17,7 @@ | |
import google.protobuf.symbol_database | ||
|
||
import temporalio.api.common.v1 | ||
import temporalio.common | ||
|
||
|
||
class PayloadConverter(ABC): | ||
|
@@ -594,6 +596,80 @@ def default() -> DataConverter: | |
return _default | ||
|
||
|
||
def encode_search_attributes( | ||
attrs: temporalio.common.SearchAttributes, | ||
api: temporalio.api.common.v1.SearchAttributes, | ||
) -> None: | ||
"""Convert search attributes into an API message. | ||
|
||
Args: | ||
attrs: Search attributes to convert. | ||
api: API message to set converted attributes on. | ||
""" | ||
for k, v in attrs.items(): | ||
api.indexed_fields[k].CopyFrom(encode_search_attribute_values(v)) | ||
|
||
|
||
def encode_search_attribute_values( | ||
vals: List[temporalio.common.SearchAttributeValue], | ||
) -> temporalio.api.common.v1.Payload: | ||
"""Convert search attribute values into a payload. | ||
|
||
Args: | ||
vals: List of values to convert. | ||
""" | ||
if not isinstance(vals, list): | ||
raise TypeError("Search attribute values must be lists") | ||
# Confirm all types are the same | ||
val_type: Optional[Type] = None | ||
# Convert dates to strings | ||
safe_vals = [] | ||
for v in vals: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should assert that vals is a homogenous list There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could also limit this at the type level. type SearchAttributeValueArray = string[] | number[] | boolean[] | Date[];
type SearchAttributes = Record<string, SearchAttributeVAlueArray>; There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good, will do. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense, will do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am afraid that |
||
if isinstance(v, datetime): | ||
if v.tzinfo is None: | ||
raise ValueError( | ||
"Timezone must be present on all search attribute dates" | ||
) | ||
v = v.isoformat() | ||
elif not isinstance(v, (str, int, float, bool)): | ||
raise TypeError( | ||
f"Search attribute value of type {type(v).__name__} not one of str, int, float, bool, or datetime" | ||
) | ||
elif val_type and type(v) is not val_type: | ||
raise TypeError( | ||
f"Search attribute values must have the same type for the same key" | ||
) | ||
elif not val_type: | ||
val_type = type(v) | ||
safe_vals.append(v) | ||
return default().payload_converter.to_payloads([safe_vals])[0] | ||
|
||
|
||
def decode_search_attributes( | ||
api: temporalio.api.common.v1.SearchAttributes, | ||
) -> temporalio.common.SearchAttributes: | ||
"""Decode API search attributes to values. | ||
|
||
Args: | ||
api: API message with search attribute values to convert. | ||
|
||
Returns: | ||
Converted search attribute values. | ||
""" | ||
conv = default().payload_converter | ||
ret = {} | ||
for k, v in api.indexed_fields.items(): | ||
val = conv.from_payloads([v])[0] | ||
# If a value did not come back as a list, make it a single-item list | ||
if not isinstance(val, list): | ||
val = [val] | ||
# Convert each item to datetime if necessary | ||
if v.metadata.get("type") == b"Datetime": | ||
val = [datetime.fromisoformat(v) for v in val] | ||
ret[k] = val | ||
return ret | ||
|
||
|
||
class _FunctionTypeLookup: | ||
def __init__(self, type_hint_eval_str: bool) -> None: | ||
# Keyed by callable __qualname__, value is optional arg types and | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this create the key
k
if it doesn't exist?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or do you always make sure it exists before sending to this method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a Protobuf oddity. They lazily create everything on first scalar (i.e. non-message) update. See https://developers.google.com/protocol-buffers/docs/reference/python-generated#map-fields. If I tried to do
payloads[k] = some_message
, I'd get an error because no Python Protobuf code allows assignment to a message.