From 0070a81f4081b3c48cf481644645919b64aa864e Mon Sep 17 00:00:00 2001 From: Felix Wang Date: Thu, 31 Mar 2022 16:44:12 -0700 Subject: [PATCH 1/5] Fix import Signed-off-by: Felix Wang --- sdk/python/feast/feature.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/python/feast/feature.py b/sdk/python/feast/feature.py index b37e0f562b2..57f75c90d76 100644 --- a/sdk/python/feast/feature.py +++ b/sdk/python/feast/feature.py @@ -15,7 +15,7 @@ from typing import Dict, Optional from feast.protos.feast.core.Feature_pb2 import FeatureSpecV2 as FeatureSpecProto -from feast.protos.feast.types import Value_pb2 as ValueTypeProto +from feast.protos.feast.types.Value_pb2 import ValueType as ValueTypeProto from feast.value_type import ValueType @@ -88,7 +88,7 @@ def to_proto(self) -> FeatureSpecProto: Returns: A FeatureSpecProto protobuf. """ - value_type = ValueTypeProto.ValueType.Enum.Value(self.dtype.name) + value_type = ValueTypeProto.Enum.Value(self.dtype.name) return FeatureSpecProto( name=self.name, value_type=value_type, labels=self.labels, From 7d944fcd0ebe2a4bf81932c4587b383cfc2da27f Mon Sep 17 00:00:00 2001 From: Felix Wang Date: Thu, 31 Mar 2022 18:56:43 -0700 Subject: [PATCH 2/5] Add types Signed-off-by: Felix Wang --- sdk/python/feast/types.py | 154 ++++++++++++++++++++++++++++ sdk/python/tests/unit/test_types.py | 17 +++ 2 files changed, 171 insertions(+) create mode 100644 sdk/python/feast/types.py create mode 100644 sdk/python/tests/unit/test_types.py diff --git a/sdk/python/feast/types.py b/sdk/python/feast/types.py new file mode 100644 index 00000000000..f25c754f508 --- /dev/null +++ b/sdk/python/feast/types.py @@ -0,0 +1,154 @@ +# Copyright 2022 The Feast Authors +# +# 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 +# +# https://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. +from abc import ABC, abstractmethod + +from feast.protos.feast.types.Value_pb2 import ValueType as ValueTypeProto + +PRIMITIVE_FEAST_TYPES_TO_VALUE_TYPES = { + "Invalid": "INVALID", + "String": "STRING", + "Bytes": "BYTES", + "Bool": "BOOL", + "Int32": "INT32", + "Int64": "INT64", + "Float32": "FLOAT", + "Float64": "DOUBLE", + "UnixTimestamp": "UNIX_TIMESTAMP", +} + + +class FeastType(ABC): + """ + A FeastType represents a structured type that is recognized by Feast. + """ + + def __init__(self): + """Creates a FeastType object.""" + pass + + @abstractmethod + def to_proto(self) -> ValueTypeProto: + """ + Converts a FeastType object to a protobuf representation. + """ + raise NotImplementedError + + +class PrimitiveFeastType(FeastType): + """ + A PrimitiveFeastType represents a primitive type in Feast. + """ + + def __init__(self): + """Creates a PrimitiveFeastType object.""" + pass + + @classmethod + def value_type(cls) -> str: + """ + Returns the value type of the FeastType in string format. + """ + return cls.__name__ + + @classmethod + def to_proto(cls) -> ValueTypeProto: + """ + Converts a PrimitiveFeastType object to a protobuf representation. + """ + value_type_name = PRIMITIVE_FEAST_TYPES_TO_VALUE_TYPES[cls.value_type()] + return ValueTypeProto.Enum.Value(value_type_name) + + +class Invalid(PrimitiveFeastType): + """ + An Invalid represents an invalid type. + """ + + +class String(PrimitiveFeastType): + """ + A String represents a string type. + """ + + +class Bytes(PrimitiveFeastType): + """ + A Bytes represents a bytes type. + """ + + +class Bool(PrimitiveFeastType): + """ + A Bool represents a bool type. + """ + + +class Int32(PrimitiveFeastType): + """ + An Int32 represents an int32 type. + """ + + +class Int64(PrimitiveFeastType): + """ + An Int64 represents an int64 type. + """ + + +class Float32(PrimitiveFeastType): + """ + A Float32 represents a float32 type. + """ + + +class Float64(PrimitiveFeastType): + """ + A Float64 represents a float64 type. + """ + + +class UnixTimestamp(PrimitiveFeastType): + """ + A UnixTimestamp represents a unix timestamp type. + """ + + +SUPPORTED_BASE_TYPES = [Invalid, String, Bytes, Bool, Int32, Int64, Float32, Float64] + + +class Array(FeastType): + """ + An Array represents a list of types. + + Attributes: + base_type: The base type of the array. + """ + + base_type: type + + def __init__(self, base_type: type): + if base_type not in SUPPORTED_BASE_TYPES: + raise ValueError( + f"Type {type(base_type)} is currently not supported as a base type for Array." + ) + + self.base_type = base_type + + def to_proto(self) -> ValueTypeProto: + assert issubclass(self.base_type, PrimitiveFeastType) + value_type_name = PRIMITIVE_FEAST_TYPES_TO_VALUE_TYPES[ + self.base_type.value_type() + ] + value_type_list_name = value_type_name + "_LIST" + return ValueTypeProto.Enum.Value(value_type_list_name) diff --git a/sdk/python/tests/unit/test_types.py b/sdk/python/tests/unit/test_types.py new file mode 100644 index 00000000000..f593a894a90 --- /dev/null +++ b/sdk/python/tests/unit/test_types.py @@ -0,0 +1,17 @@ +from feast.protos.feast.types.Value_pb2 import ValueType as ValueTypeProto +from feast.types import Array, Float32, String + + +def test_primitive_feast_type(): + assert String.value_type() == "String" + assert String.to_proto() == ValueTypeProto.Enum.Value("STRING") + assert Float32.value_type() == "Float32" + assert Float32.to_proto() == ValueTypeProto.Enum.Value("FLOAT") + + +def test_array_feast_type(): + array_float_32 = Array(Float32) + assert array_float_32.to_proto() == ValueTypeProto.Enum.Value("FLOAT_LIST") + + array_string = Array(String) + assert array_string.to_proto() == ValueTypeProto.Enum.Value("STRING_LIST") From dc6948f732160fbf00a98a802cb674a88592296c Mon Sep 17 00:00:00 2001 From: Felix Wang Date: Fri, 1 Apr 2022 12:48:05 -0700 Subject: [PATCH 3/5] Fix types Signed-off-by: Felix Wang --- sdk/python/feast/types.py | 139 ++++++++++------------------ sdk/python/tests/unit/test_types.py | 10 +- 2 files changed, 55 insertions(+), 94 deletions(-) diff --git a/sdk/python/feast/types.py b/sdk/python/feast/types.py index f25c754f508..9f74ea3b98e 100644 --- a/sdk/python/feast/types.py +++ b/sdk/python/feast/types.py @@ -12,19 +12,21 @@ # See the License for the specific language governing permissions and # limitations under the License. from abc import ABC, abstractmethod +from enum import Enum +from typing import Union from feast.protos.feast.types.Value_pb2 import ValueType as ValueTypeProto PRIMITIVE_FEAST_TYPES_TO_VALUE_TYPES = { - "Invalid": "INVALID", - "String": "STRING", - "Bytes": "BYTES", - "Bool": "BOOL", - "Int32": "INT32", - "Int64": "INT64", - "Float32": "FLOAT", - "Float64": "DOUBLE", - "UnixTimestamp": "UNIX_TIMESTAMP", + "INVALID": "INVALID", + "STRING": "STRING", + "BYTES": "BYTES", + "BOOL": "BOOL", + "INT32": "INT32", + "INT64": "INT64", + "FLOAT32": "FLOAT", + "FLOAT64": "DOUBLE", + "UNIX_TIMESTAMP": "UNIX_TIMESTAMP", } @@ -38,93 +40,56 @@ def __init__(self): pass @abstractmethod - def to_proto(self) -> ValueTypeProto: + def to_int(self) -> int: """ - Converts a FeastType object to a protobuf representation. + Converts a FeastType object to the appropriate int value corresponding to + the correct ValueTypeProto.Enum value. """ raise NotImplementedError -class PrimitiveFeastType(FeastType): +class PrimitiveFeastType(Enum): """ A PrimitiveFeastType represents a primitive type in Feast. """ - def __init__(self): - """Creates a PrimitiveFeastType object.""" - pass - - @classmethod - def value_type(cls) -> str: - """ - Returns the value type of the FeastType in string format. - """ - return cls.__name__ + INVALID = 0 + BYTES = 1 + STRING = 2 + BOOL = 3 + INT32 = 4 + INT64 = 5 + FLOAT32 = 6 + FLOAT64 = 7 + UNIX_TIMESTAMP = 8 - @classmethod - def to_proto(cls) -> ValueTypeProto: - """ - Converts a PrimitiveFeastType object to a protobuf representation. - """ - value_type_name = PRIMITIVE_FEAST_TYPES_TO_VALUE_TYPES[cls.value_type()] + def to_int(self) -> int: + value_type_name = PRIMITIVE_FEAST_TYPES_TO_VALUE_TYPES[self.name] return ValueTypeProto.Enum.Value(value_type_name) -class Invalid(PrimitiveFeastType): - """ - An Invalid represents an invalid type. - """ - - -class String(PrimitiveFeastType): - """ - A String represents a string type. - """ - - -class Bytes(PrimitiveFeastType): - """ - A Bytes represents a bytes type. - """ - - -class Bool(PrimitiveFeastType): - """ - A Bool represents a bool type. - """ - - -class Int32(PrimitiveFeastType): - """ - An Int32 represents an int32 type. - """ - - -class Int64(PrimitiveFeastType): - """ - An Int64 represents an int64 type. - """ - - -class Float32(PrimitiveFeastType): - """ - A Float32 represents a float32 type. - """ - - -class Float64(PrimitiveFeastType): - """ - A Float64 represents a float64 type. - """ - - -class UnixTimestamp(PrimitiveFeastType): - """ - A UnixTimestamp represents a unix timestamp type. - """ +Invalid = PrimitiveFeastType.INVALID +Bytes = PrimitiveFeastType.BYTES +String = PrimitiveFeastType.STRING +Bool = PrimitiveFeastType.BOOL +Int32 = PrimitiveFeastType.INT32 +Int64 = PrimitiveFeastType.INT64 +Float32 = PrimitiveFeastType.FLOAT32 +Float64 = PrimitiveFeastType.FLOAT64 +UnixTimestamp = PrimitiveFeastType.UNIX_TIMESTAMP -SUPPORTED_BASE_TYPES = [Invalid, String, Bytes, Bool, Int32, Int64, Float32, Float64] +SUPPORTED_BASE_TYPES = [ + Invalid, + String, + Bytes, + Bool, + Int32, + Int64, + Float32, + Float64, + UnixTimestamp, +] class Array(FeastType): @@ -135,9 +100,9 @@ class Array(FeastType): base_type: The base type of the array. """ - base_type: type + base_type: Union[PrimitiveFeastType, FeastType] - def __init__(self, base_type: type): + def __init__(self, base_type: Union[PrimitiveFeastType, FeastType]): if base_type not in SUPPORTED_BASE_TYPES: raise ValueError( f"Type {type(base_type)} is currently not supported as a base type for Array." @@ -145,10 +110,8 @@ def __init__(self, base_type: type): self.base_type = base_type - def to_proto(self) -> ValueTypeProto: - assert issubclass(self.base_type, PrimitiveFeastType) - value_type_name = PRIMITIVE_FEAST_TYPES_TO_VALUE_TYPES[ - self.base_type.value_type() - ] + def to_int(self) -> int: + assert isinstance(self.base_type, PrimitiveFeastType) + value_type_name = PRIMITIVE_FEAST_TYPES_TO_VALUE_TYPES[self.base_type.name] value_type_list_name = value_type_name + "_LIST" return ValueTypeProto.Enum.Value(value_type_list_name) diff --git a/sdk/python/tests/unit/test_types.py b/sdk/python/tests/unit/test_types.py index f593a894a90..a9ab8f767ec 100644 --- a/sdk/python/tests/unit/test_types.py +++ b/sdk/python/tests/unit/test_types.py @@ -3,15 +3,13 @@ def test_primitive_feast_type(): - assert String.value_type() == "String" - assert String.to_proto() == ValueTypeProto.Enum.Value("STRING") - assert Float32.value_type() == "Float32" - assert Float32.to_proto() == ValueTypeProto.Enum.Value("FLOAT") + assert String.to_int() == ValueTypeProto.Enum.Value("STRING") + assert Float32.to_int() == ValueTypeProto.Enum.Value("FLOAT") def test_array_feast_type(): array_float_32 = Array(Float32) - assert array_float_32.to_proto() == ValueTypeProto.Enum.Value("FLOAT_LIST") + assert array_float_32.to_int() == ValueTypeProto.Enum.Value("FLOAT_LIST") array_string = Array(String) - assert array_string.to_proto() == ValueTypeProto.Enum.Value("STRING_LIST") + assert array_string.to_int() == ValueTypeProto.Enum.Value("STRING_LIST") From fbfd2518204277b43337399514a8f796624fdfe1 Mon Sep 17 00:00:00 2001 From: Felix Wang Date: Fri, 1 Apr 2022 12:54:18 -0700 Subject: [PATCH 4/5] Test type errors Signed-off-by: Felix Wang --- sdk/python/tests/unit/test_types.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sdk/python/tests/unit/test_types.py b/sdk/python/tests/unit/test_types.py index a9ab8f767ec..8252a0e181c 100644 --- a/sdk/python/tests/unit/test_types.py +++ b/sdk/python/tests/unit/test_types.py @@ -1,3 +1,5 @@ +import pytest + from feast.protos.feast.types.Value_pb2 import ValueType as ValueTypeProto from feast.types import Array, Float32, String @@ -13,3 +15,9 @@ def test_array_feast_type(): array_string = Array(String) assert array_string.to_int() == ValueTypeProto.Enum.Value("STRING_LIST") + + with pytest.raises(ValueError): + _ = Array(Array) + + with pytest.raises(ValueError): + _ = Array(Array(String)) From d592c465f2f916dd2d52d4829537d8da67e44382 Mon Sep 17 00:00:00 2001 From: Felix Wang Date: Fri, 1 Apr 2022 13:36:08 -0700 Subject: [PATCH 5/5] Rename FeastType to ComplexFeastType Signed-off-by: Felix Wang --- sdk/python/feast/types.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/sdk/python/feast/types.py b/sdk/python/feast/types.py index 9f74ea3b98e..fe74bd38bd8 100644 --- a/sdk/python/feast/types.py +++ b/sdk/python/feast/types.py @@ -30,19 +30,19 @@ } -class FeastType(ABC): +class ComplexFeastType(ABC): """ - A FeastType represents a structured type that is recognized by Feast. + A ComplexFeastType represents a structured type that is recognized by Feast. """ def __init__(self): - """Creates a FeastType object.""" + """Creates a ComplexFeastType object.""" pass @abstractmethod def to_int(self) -> int: """ - Converts a FeastType object to the appropriate int value corresponding to + Converts a ComplexFeastType object to the appropriate int value corresponding to the correct ValueTypeProto.Enum value. """ raise NotImplementedError @@ -56,11 +56,11 @@ class PrimitiveFeastType(Enum): INVALID = 0 BYTES = 1 STRING = 2 - BOOL = 3 - INT32 = 4 - INT64 = 5 - FLOAT32 = 6 - FLOAT64 = 7 + INT32 = 3 + INT64 = 4 + FLOAT32 = 5 + FLOAT64 = 6 + BOOL = 7 UNIX_TIMESTAMP = 8 def to_int(self) -> int: @@ -92,7 +92,7 @@ def to_int(self) -> int: ] -class Array(FeastType): +class Array(ComplexFeastType): """ An Array represents a list of types. @@ -100,9 +100,9 @@ class Array(FeastType): base_type: The base type of the array. """ - base_type: Union[PrimitiveFeastType, FeastType] + base_type: Union[PrimitiveFeastType, ComplexFeastType] - def __init__(self, base_type: Union[PrimitiveFeastType, FeastType]): + def __init__(self, base_type: Union[PrimitiveFeastType, ComplexFeastType]): if base_type not in SUPPORTED_BASE_TYPES: raise ValueError( f"Type {type(base_type)} is currently not supported as a base type for Array."