3636from feast .protos .feast .core .FeatureView_pb2 import (
3737 MaterializationInterval as MaterializationIntervalProto ,
3838)
39+ from feast .protos .feast .core .Transformation_pb2 import (
40+ FeatureTransformationV2 as FeatureTransformationProto ,
41+ )
3942from feast .types import from_value_type
4043from feast .value_type import ValueType
4144
@@ -415,6 +418,27 @@ def to_proto_spec(
415418 source_view_protos = [
416419 view ._to_proto_internal (seen ).spec for view in self .source_views
417420 ]
421+
422+ feature_transformation_proto = None
423+ if hasattr (self , "feature_transformation" ) and self .feature_transformation :
424+ from feast .protos .feast .core .Transformation_pb2 import (
425+ SubstraitTransformationV2 as SubstraitTransformationProto ,
426+ )
427+ from feast .protos .feast .core .Transformation_pb2 import (
428+ UserDefinedFunctionV2 as UserDefinedFunctionProto ,
429+ )
430+
431+ transformation_proto = self .feature_transformation .to_proto ()
432+
433+ if isinstance (transformation_proto , UserDefinedFunctionProto ):
434+ feature_transformation_proto = FeatureTransformationProto (
435+ user_defined_function = transformation_proto ,
436+ )
437+ elif isinstance (transformation_proto , SubstraitTransformationProto ):
438+ feature_transformation_proto = FeatureTransformationProto (
439+ substrait_transformation = transformation_proto ,
440+ )
441+
418442 return FeatureViewSpecProto (
419443 name = self .name ,
420444 entities = self .entities ,
@@ -429,6 +453,7 @@ def to_proto_spec(
429453 batch_source = batch_source_proto ,
430454 stream_source = stream_source_proto ,
431455 source_views = source_view_protos ,
456+ feature_transformation = feature_transformation_proto ,
432457 )
433458
434459 def to_proto_meta (self ):
@@ -498,21 +523,61 @@ def _from_proto_internal(
498523 for view_spec in feature_view_proto .spec .source_views
499524 ]
500525
501- feature_view = cls (
502- name = feature_view_proto .spec .name ,
503- description = feature_view_proto .spec .description ,
504- tags = dict (feature_view_proto .spec .tags ),
505- owner = feature_view_proto .spec .owner ,
506- online = feature_view_proto .spec .online ,
507- offline = feature_view_proto .spec .offline ,
508- ttl = (
509- timedelta (days = 0 )
510- if feature_view_proto .spec .ttl .ToNanoseconds () == 0
511- else feature_view_proto .spec .ttl .ToTimedelta ()
512- ),
513- source = source_views if source_views else batch_source ,
514- sink_source = batch_source if source_views else None ,
515- )
526+ has_transformation = feature_view_proto .spec .HasField ("feature_transformation" )
527+
528+ if has_transformation and cls == FeatureView :
529+ from feast .batch_feature_view import BatchFeatureView
530+ from feast .transformation .python_transformation import PythonTransformation
531+ from feast .transformation .substrait_transformation import (
532+ SubstraitTransformation ,
533+ )
534+
535+ feature_transformation_proto = (
536+ feature_view_proto .spec .feature_transformation
537+ )
538+ transformation = None
539+
540+ if feature_transformation_proto .HasField ("user_defined_function" ):
541+ transformation = PythonTransformation .from_proto (
542+ feature_transformation_proto .user_defined_function
543+ )
544+ elif feature_transformation_proto .HasField ("substrait_transformation" ):
545+ transformation = SubstraitTransformation .from_proto (
546+ feature_transformation_proto .substrait_transformation
547+ )
548+
549+ feature_view : FeatureView = BatchFeatureView ( # type: ignore[assignment]
550+ name = feature_view_proto .spec .name ,
551+ description = feature_view_proto .spec .description ,
552+ tags = dict (feature_view_proto .spec .tags ),
553+ owner = feature_view_proto .spec .owner ,
554+ online = feature_view_proto .spec .online ,
555+ offline = feature_view_proto .spec .offline ,
556+ ttl = (
557+ timedelta (days = 0 )
558+ if feature_view_proto .spec .ttl .ToNanoseconds () == 0
559+ else feature_view_proto .spec .ttl .ToTimedelta ()
560+ ),
561+ source = source_views if source_views else batch_source , # type: ignore[arg-type]
562+ sink_source = batch_source if source_views else None ,
563+ feature_transformation = transformation ,
564+ )
565+ else :
566+ feature_view = cls ( # type: ignore[assignment]
567+ name = feature_view_proto .spec .name ,
568+ description = feature_view_proto .spec .description ,
569+ tags = dict (feature_view_proto .spec .tags ),
570+ owner = feature_view_proto .spec .owner ,
571+ online = feature_view_proto .spec .online ,
572+ offline = feature_view_proto .spec .offline ,
573+ ttl = (
574+ timedelta (days = 0 )
575+ if feature_view_proto .spec .ttl .ToNanoseconds () == 0
576+ else feature_view_proto .spec .ttl .ToTimedelta ()
577+ ),
578+ source = source_views if source_views else batch_source ,
579+ sink_source = batch_source if source_views else None ,
580+ )
516581 if stream_source :
517582 feature_view .stream_source = stream_source
518583
0 commit comments