from .pyridis_message import *
import pyarrow as pa
import numpy as np
from typing import Union
from enum import Enum
from typing import TypeVar, Type, Optional
T = TypeVar("T", bound="ArrowMessage")
class ArrowMessage:
@classmethod
def from_arrow(cls: Type[T], data: pa.Array) -> Optional[T]:
if data.type == pa.null():
return None
if issubclass(cls, Enum):
value = data.to_numpy(zero_copy_only=False)[0]
for encoding in cls:
if encoding.value == value:
return encoding
return None
else:
names = [field.name for field in data.type]
indices = [index for index in data.type.type_codes]
map = dict(zip(names, indices))
kwargs = {}
for name in names:
field_type = cls.__dataclass_fields__[name].type
if hasattr(field_type, "__origin__") and field_type.__origin__ is Union:
field_type = field_type.__args__[0]
if issubclass(field_type, np.ndarray):
kwargs[name] = data.field(map[name]).to_numpy(zero_copy_only=False)
elif issubclass(field_type, ArrowMessage):
kwargs[name] = field_type.from_arrow(data.field(map[name]))
else:
kwargs[name] = data.field(map[name]).to_numpy(zero_copy_only=False)[
0
]
return cls(**kwargs)
def to_arrow(self) -> pa.Array:
if self is None:
return pa.array([], type=pa.null())
if isinstance(self, Enum):
return pa.array([self.value], type=pa.utf8())
fields = list(self.__dataclass_fields__.keys())
children = []
for field in fields:
value = getattr(self, field)
if value is None:
children.append(pa.array([], type=pa.null()))
elif isinstance(value, np.ndarray):
value.flags.writeable = False
children.append(pa.array(value))
elif isinstance(value, ArrowMessage):
children.append(value.to_arrow())
else:
children.append(pa.array([value]))
return pa.UnionArray.from_dense(
types=pa.array([], type=pa.int8()),
value_offsets=pa.array([], type=pa.int32()),
children=children,
field_names=fields,
)