1313 Protocol ,
1414 TypeAlias ,
1515 TypeVar ,
16- cast ,
1716 overload ,
1817 runtime_checkable ,
1918)
@@ -209,26 +208,6 @@ def table_name(self) -> str:
209208) # bound, should be used for generic
210209
211210
212- def _coerce_metadata (value : Any ) -> dict [str , JsonValue ] | None :
213- if value is None :
214- return None
215- if not isinstance (value , Mapping ):
216- raise ValueError ("metadata must be a mapping" )
217- try :
218- serialized = json .dumps (value )
219- except (TypeError , ValueError ) as exc :
220- raise ValueError (
221- "metadata must be JSON-serializable. Found non-serializable value"
222- ) from exc
223- return cast (dict [str , JsonValue ], json .loads (serialized ))
224-
225-
226- MetadataField = Annotated [
227- dict [str , JsonValue ] | None ,
228- BeforeValidator (_coerce_metadata ),
229- ]
230-
231-
232211class _BaseFeatureSpec (FrozenBaseModel ):
233212 key : Annotated [FeatureKey , BeforeValidator (FeatureKeyAdapter .validate_python )]
234213 deps : list [FeatureDep ] | None = None
@@ -241,8 +220,8 @@ class _BaseFeatureSpec(FrozenBaseModel):
241220 )
242221 ]
243222 )
244- metadata : MetadataField = pydantic .Field (
245- default = None ,
223+ metadata : dict [ str , JsonValue ] = pydantic .Field (
224+ default_factory = dict ,
246225 description = "Metadata attached to this feature." ,
247226 )
248227
@@ -258,7 +237,7 @@ def __init__(
258237 deps : list [FeatureDep ] | None = None ,
259238 fields : list [FieldSpec ] | None = None ,
260239 id_columns : list [str ] | None = None ,
261- metadata : Mapping [str , Any ] | JsonValue | None = None ,
240+ metadata : Mapping [str , JsonValue ] | None = None ,
262241 ) -> None :
263242 """Initialize from string key."""
264243 ...
@@ -271,7 +250,7 @@ def __init__(
271250 deps : list [FeatureDep ] | None = None ,
272251 fields : list [FieldSpec ] | None = None ,
273252 id_columns : list [str ] | None = None ,
274- metadata : Mapping [str , Any ] | JsonValue | None = None ,
253+ metadata : Mapping [str , JsonValue ] | None = None ,
275254 ) -> None :
276255 """Initialize from sequence of parts."""
277256 ...
@@ -284,7 +263,7 @@ def __init__(
284263 deps : list [FeatureDep ] | None = None ,
285264 fields : list [FieldSpec ] | None = None ,
286265 id_columns : list [str ] | None = None ,
287- metadata : Mapping [str , Any ] | JsonValue | None = None ,
266+ metadata : Mapping [str , JsonValue ] | None = None ,
288267 ) -> None :
289268 """Initialize from FeatureKey instance."""
290269 ...
@@ -297,11 +276,12 @@ def __init__(
297276 deps : list [FeatureDep ] | None = None ,
298277 fields : list [FieldSpec ] | None = None ,
299278 id_columns : list [str ] | None = None ,
279+ metadata : Mapping [str , JsonValue ] | None = None ,
300280 ) -> None :
301281 """Initialize from BaseFeatureSpec instance."""
302282 ...
303283
304- def __init__ (self , key : CoercibleToFeatureKey | Self , ** kwargs ):
284+ def __init__ (self , key : CoercibleToFeatureKey | Self , ** kwargs : Any ):
305285 if isinstance (key , type (self )):
306286 key = key .key
307287 else :
@@ -384,8 +364,6 @@ def feature_spec_version(self) -> str:
384364 # Use model_dump with mode="json" for deterministic serialization
385365 # This ensures all types (like FeatureKey) are properly serialized
386366 spec_dict = self .model_dump (mode = "json" )
387- if spec_dict .get ("metadata" ) == {}:
388- spec_dict .pop ("metadata" , None )
389367
390368 # Sort keys to ensure deterministic ordering
391369 spec_json = json .dumps (spec_dict , sort_keys = True )
@@ -422,6 +400,7 @@ def __init__(
422400 deps : list [FeatureDep ] | None = None ,
423401 fields : list [FieldSpec ] | None = None ,
424402 id_columns : Sequence [str ] | None = None ,
403+ metadata : Mapping [str , JsonValue ] | None = None ,
425404 ) -> None :
426405 """Initialize from string key."""
427406 ...
@@ -434,6 +413,7 @@ def __init__(
434413 deps : list [FeatureDep ] | None = None ,
435414 fields : list [FieldSpec ] | None = None ,
436415 id_columns : Sequence [str ] | None = None ,
416+ metadata : Mapping [str , JsonValue ] | None = None ,
437417 ) -> None :
438418 """Initialize from sequence of parts."""
439419 ...
@@ -446,6 +426,7 @@ def __init__(
446426 deps : list [FeatureDep ] | None = None ,
447427 fields : list [FieldSpec ] | None = None ,
448428 id_columns : Sequence [str ] | None = None ,
429+ metadata : Mapping [str , JsonValue ] | None = None ,
449430 ) -> None :
450431 """Initialize from FeatureKey instance."""
451432 ...
@@ -458,11 +439,12 @@ def __init__(
458439 deps : list [FeatureDep ] | None = None ,
459440 fields : list [FieldSpec ] | None = None ,
460441 id_columns : Sequence [str ] | None = None ,
442+ metadata : Mapping [str , JsonValue ] | None = None ,
461443 ) -> None :
462444 """Initialize from FeatureSpec instance."""
463445 ...
464446
465- def __init__ (self , key : CoercibleToFeatureKey | Self , ** kwargs ):
447+ def __init__ (self , key : CoercibleToFeatureKey | Self , ** kwargs : Any ):
466448 # id_columns is always set for FeatureSpec
467449 super ().__init__ (key = key , ** kwargs )
468450
0 commit comments