core/maxframe/serialization/serializables/field.py (448 lines of code) (raw):
# Copyright 1999-2025 Alibaba Group Holding Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import enum
import importlib
import inspect
import itertools
import os
from abc import ABC, ABCMeta, abstractmethod
from typing import Any, Callable, Optional, Type, Union
from ...utils import no_default
from .field_type import (
AbstractFieldType,
DictType,
FieldTypes,
ListType,
ReferenceType,
TupleType,
)
_is_ci = (os.environ.get("CI") or "0").lower() in ("1", "true")
class Field(ABC):
__slots__ = (
"_tag",
"_default_value",
"_default_factory",
"_on_serialize",
"_on_deserialize",
"name", # The __name__ of member_descriptor
"get", # The __get__ of member_descriptor
"set", # The __set__ of member_descriptor
"__delete__", # The __delete__ of member_descriptor
)
_tag: str
_default_value: Any
_default_factory: Optional[Callable]
def __init__(
self,
tag: str,
default: Any = no_default,
default_factory: Optional[Callable] = None,
on_serialize: Callable[[Any], Any] = None,
on_deserialize: Callable[[Any], Any] = None,
):
if (
default is not no_default and default_factory is not None
): # pragma: no cover
raise ValueError("default and default_factory can not be specified both")
self._tag = tag
self._default_value = default
self._default_factory = default_factory
self._on_serialize = on_serialize
self._on_deserialize = on_deserialize
@property
def tag(self):
return self._tag
@property
def on_serialize(self):
return self._on_serialize
@property
def on_deserialize(self):
return self._on_deserialize
@property
@abstractmethod
def field_type(self) -> AbstractFieldType:
"""
Field type.
Returns
-------
field_type : AbstractFieldType
Field type.
"""
def __get__(self, instance, owner=None):
try:
return self.get(instance, owner)
except AttributeError:
if self._default_value is not no_default:
val = self._default_value
self.set(instance, val)
return val
elif self._default_factory is not None:
val = self._default_factory()
self.set(instance, val)
return val
else:
raise
def __set__(self, instance, value) -> None:
if _is_ci: # pragma: no branch
from ...core import is_kernel_mode
if not is_kernel_mode():
field_type = self.field_type
try:
to_check_value = value
if to_check_value is not None and self._on_serialize:
to_check_value = self._on_serialize(to_check_value)
field_type.validate(to_check_value)
except (TypeError, ValueError) as e:
raise type(e)(
f"Failed to set `{self.name}` for {type(instance).__name__} "
f"when environ CI=true is set: {str(e)}"
)
self.set(instance, value)
class AnyField(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.any
class IdentityField(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.string
class BoolField(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.bool
class Int8Field(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.int8
class Int16Field(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.int16
class Int32Field(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.int32
class Int64Field(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.int64
class UInt8Field(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.uint8
class UInt16Field(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.uint16
class UInt32Field(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.uint32
class UInt64Field(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.uint64
class Float16Field(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.float16
class Float32Field(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.float32
class Float64Field(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.float64
class Complex64Field(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.complex64
class Complex128Field(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.complex128
class StringField(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.string
class BytesField(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.bytes
class KeyField(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.key
class NDArrayField(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.ndarray
class Datetime64Field(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.datetime
class Timedelta64Field(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.timedelta
class DataTypeField(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.dtype
class IndexField(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.index
class SeriesField(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.series
class DataFrameField(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.dataframe
class SliceField(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.slice
class FunctionField(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.function
class NamedTupleField(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.namedtuple
class TZInfoField(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.tzinfo
class IntervalArrayField(Field):
__slots__ = ()
@property
def field_type(self) -> AbstractFieldType:
return FieldTypes.interval_array
class EnumField(Field):
__slots__ = ("_enum_type", "_field_type")
def __init__(
self,
tag: str,
enum_type: Type[enum.Enum],
field_type: AbstractFieldType = None,
default: Any = no_default,
default_factory: Optional[Callable] = None,
on_serialize: Callable[[Any], Any] = None,
on_deserialize: Callable[[Any], Any] = None,
):
def _on_serialize(val):
if on_serialize is not None:
val = on_serialize(val)
return val.value if val is not None else None
def _on_deserialize(val):
val = enum_type(val) if val is not None else None
if on_deserialize is not None:
val = on_deserialize(val)
return val
super().__init__(
tag,
default=default,
default_factory=default_factory,
on_serialize=_on_serialize,
on_deserialize=_on_deserialize,
)
self._enum_type = enum_type
self._field_type = field_type or FieldTypes.any
@property
def field_type(self) -> Type[AbstractFieldType]:
return self._field_type
class _CollectionField(Field, metaclass=ABCMeta):
__slots__ = ("_field_type",)
def __init__(
self,
tag: str,
field_type: AbstractFieldType = None,
default: Any = no_default,
default_factory: Optional[Callable] = None,
on_serialize: Callable[[Any], Any] = None,
on_deserialize: Callable[[Any], Any] = None,
):
super().__init__(
tag,
default=default,
default_factory=default_factory,
on_serialize=on_serialize,
on_deserialize=on_deserialize,
)
if field_type is None:
field_type = FieldTypes.any
collection_type = self._collection_type()
if not isinstance(field_type, collection_type):
field_type = collection_type(field_type, ...)
self._field_type = field_type
@classmethod
@abstractmethod
def _collection_type(cls) -> AbstractFieldType:
"""
Collection type.
Returns
-------
collection_type
"""
@property
def field_type(self) -> Type[AbstractFieldType]:
return self._field_type
class ListField(_CollectionField):
__slots__ = ()
@classmethod
def _collection_type(cls) -> Type[AbstractFieldType]:
return ListType
class TupleField(_CollectionField):
__slots__ = ()
@classmethod
def _collection_type(cls) -> Type[AbstractFieldType]:
return TupleType
class DictField(Field):
__slots__ = ("_field_type",)
def __init__(
self,
tag: str,
key_type: AbstractFieldType = None,
value_type: AbstractFieldType = None,
default: Any = no_default,
default_factory: Optional[Callable] = None,
on_serialize: Callable[[Any], Any] = None,
on_deserialize: Callable[[Any], Any] = None,
):
super().__init__(
tag,
default=default,
default_factory=default_factory,
on_serialize=on_serialize,
on_deserialize=on_deserialize,
)
self._field_type = DictType(key_type, value_type)
@property
def field_type(self) -> AbstractFieldType:
return self._field_type
class ReferenceField(Field):
__slots__ = "_reference_type", "_field_type"
def __init__(
self,
tag: str,
reference_type: Union[str, Type] = None,
default: Any = no_default,
default_factory: Optional[Callable] = None,
on_serialize: Callable[[Any], Any] = None,
on_deserialize: Callable[[Any], Any] = None,
):
super().__init__(
tag,
default=default,
default_factory=default_factory,
on_serialize=on_serialize,
on_deserialize=on_deserialize,
)
self._reference_type = reference_type
if not isinstance(reference_type, str):
self._field_type = ReferenceType(reference_type)
else:
# need to bind dynamically
self._field_type = None
@property
def field_type(self) -> AbstractFieldType:
return self._field_type
def get_field_type(self, instance):
if self._field_type is None:
# bind dynamically
if self._reference_type == "self":
reference_type = type(instance)
elif isinstance(self._reference_type, str) and "." in self._reference_type:
module, name = self._reference_type.rsplit(".", 1)
reference_type = getattr(importlib.import_module(module), name)
else:
module = inspect.getmodule(instance)
reference_type = getattr(module, self._reference_type)
self._field_type = ReferenceType(reference_type)
return self._field_type
def __set__(self, instance, value):
if _is_ci:
from ...core import is_kernel_mode
if not is_kernel_mode():
field_type = self.get_field_type(instance)
try:
to_check_value = value
if to_check_value is not None and self._on_serialize:
to_check_value = self._on_serialize(to_check_value)
field_type.validate(to_check_value)
except (TypeError, ValueError) as e:
raise type(e)(
f"Failed to set `{self.name}` for {type(instance).__name__} "
f"when environ CI=true is set: {e}"
)
self.set(instance, value)
class OneOfField(Field):
__slots__ = "_reference_fields"
def __init__(
self,
tag: str,
default: Any = no_default,
on_serialize: Callable[[Any], Any] = None,
on_deserialize: Callable[[Any], Any] = None,
**tag_to_reference_types,
):
super().__init__(
tag,
default=default,
on_serialize=on_serialize,
on_deserialize=on_deserialize,
)
self._reference_fields = [
ReferenceField(t, ref_type)
for t, ref_type in tag_to_reference_types.items()
]
@property
def reference_fields(self):
return self._reference_fields
@property
def field_type(self) -> AbstractFieldType: # pragma: no cover
# takes no effect here, just return AnyType()
# we will do check in __set__ instead
return FieldTypes.any
def __set__(self, instance, value):
if not _is_ci: # pragma: no cover
return self.set(instance, value)
for reference_field in self._reference_fields:
try:
to_check_value = value
if to_check_value is not None and self._on_serialize:
to_check_value = self._on_serialize(to_check_value)
reference_field.get_field_type(instance).validate(to_check_value)
self.set(instance, value)
return
except TypeError:
continue
valid_types = list(
itertools.chain(
*[
r.get_field_type(instance).valid_types
for r in self._reference_fields
]
)
)
raise TypeError(
f"Failed to set `{self.name}` for {type(instance).__name__} "
f"when environ CI=true is set: type of instance cannot match any "
f"of {valid_types}, got {type(value).__name__}"
)