@@ -74,7 +74,7 @@ class FeatureView(BaseFeatureView):
74
74
75
75
name : str
76
76
entities : List [str ]
77
- ttl : timedelta
77
+ ttl : Optional [ timedelta ]
78
78
batch_source : DataSource
79
79
stream_source : Optional [DataSource ]
80
80
features : List [Feature ]
@@ -87,9 +87,10 @@ class FeatureView(BaseFeatureView):
87
87
@log_exceptions
88
88
def __init__ (
89
89
self ,
90
- name : str ,
91
- entities : List [str ],
92
- ttl : Union [Duration , timedelta ],
90
+ * args ,
91
+ name : Optional [str ] = None ,
92
+ entities : Optional [List [str ]] = None ,
93
+ ttl : Optional [Union [Duration , timedelta ]] = None ,
93
94
batch_source : Optional [DataSource ] = None ,
94
95
stream_source : Optional [DataSource ] = None ,
95
96
features : Optional [List [Feature ]] = None ,
@@ -121,6 +122,54 @@ def __init__(
121
122
Raises:
122
123
ValueError: A field mapping conflicts with an Entity or a Feature.
123
124
"""
125
+
126
+ positional_attributes = ["name, entities, ttl" ]
127
+
128
+ _name = name
129
+ _entities = entities
130
+ _ttl = ttl
131
+
132
+ if args :
133
+ warnings .warn (
134
+ (
135
+ "feature view parameters should be specified as a keyword argument instead of a positional arg."
136
+ "Feast 0.23+ will not support positional arguments to construct feature views"
137
+ ),
138
+ DeprecationWarning ,
139
+ )
140
+ if len (args ) > len (positional_attributes ):
141
+ raise ValueError (
142
+ f"Only { ', ' .join (positional_attributes )} are allowed as positional args when defining "
143
+ f"feature views, for backwards compatibility."
144
+ )
145
+ if len (args ) >= 1 :
146
+ _name = args [0 ]
147
+ if len (args ) >= 2 :
148
+ _entities = args [1 ]
149
+ if len (args ) >= 3 :
150
+ _ttl = args [2 ]
151
+
152
+ if not _name :
153
+ raise ValueError ("feature view name needs to be specified" )
154
+
155
+ self .name = _name
156
+ self .entities = _entities if _entities else [DUMMY_ENTITY_NAME ]
157
+
158
+ if isinstance (_ttl , Duration ):
159
+ self .ttl = timedelta (seconds = int (_ttl .seconds ))
160
+ warnings .warn (
161
+ (
162
+ "The option to pass a Duration object to the ttl parameter is being deprecated. "
163
+ "Please pass a timedelta object instead. Feast 0.21 and onwards will not support "
164
+ "Duration objects."
165
+ ),
166
+ DeprecationWarning ,
167
+ )
168
+ elif isinstance (_ttl , timedelta ) or _ttl is None :
169
+ self .ttl = _ttl
170
+ else :
171
+ raise ValueError (f"unknown value type specified for ttl { type (_ttl )} " )
172
+
124
173
_features = features or []
125
174
126
175
if stream_source is not None and isinstance (stream_source , PushSource ):
@@ -138,7 +187,7 @@ def __init__(
138
187
)
139
188
self .batch_source = batch_source
140
189
141
- cols = [entity for entity in entities ] + [feat .name for feat in _features ]
190
+ cols = [entity for entity in self . entities ] + [feat .name for feat in _features ]
142
191
for col in cols :
143
192
if (
144
193
self .batch_source .field_mapping is not None
@@ -150,22 +199,13 @@ def __init__(
150
199
f"Entity or Feature name."
151
200
)
152
201
153
- super ().__init__ (name , _features , description , tags , owner )
154
- self .entities = entities if entities else [DUMMY_ENTITY_NAME ]
155
-
156
- if isinstance (ttl , Duration ):
157
- self .ttl = timedelta (seconds = int (ttl .seconds ))
158
- warnings .warn (
159
- (
160
- "The option to pass a Duration object to the ttl parameter is being deprecated. "
161
- "Please pass a timedelta object instead. Feast 0.21 and onwards will not support "
162
- "Duration objects."
163
- ),
164
- DeprecationWarning ,
165
- )
166
- else :
167
- self .ttl = ttl
168
-
202
+ super ().__init__ (
203
+ name = name ,
204
+ features = _features ,
205
+ description = description ,
206
+ tags = tags ,
207
+ owner = owner ,
208
+ )
169
209
self .online = online
170
210
self .stream_source = stream_source
171
211
self .materialization_intervals = []
0 commit comments