Expected Behavior
Comparing two Feast objects of different concrete types (e.g. FileSource vs SnowflakeSource)
returns False, per Python's data model. In particular, changing a feature view's data source
type and re-running feast apply over an existing registry succeeds.
Current Behavior
__eq__ raises TypeError on a cross-type comparison instead of returning False. Because the
apply/diff path compares registered objects against incoming ones, changing a source type and
re-applying crashes:
TypeError: Comparisons should only involve FileSource class objects.
Minimal repro:
from feast import FileSource
from feast.infra.offline_stores.snowflake_source import SnowflakeSource
a = FileSource(name="x", path="/tmp/x.parquet", timestamp_field="ts")
b = SnowflakeSource(name="x", database="D", schema="S", table="T", timestamp_field="ts")
a == b # TypeError — should be False
Root cause: the affected __eq__ methods do
if not isinstance(other, X): raise TypeError("Comparisons should only involve X class objects.").
Python's data model requires __eq__ to return NotImplemented for an unrecognized operand (so
the interpreter can try the reflected op and fall back to identity → False), never raise.
This is systemic — the pattern appears in ~35 __eq__ methods across the object model:
DataSource and its Kafka/Request/Kinesis subclasses; all offline sources (file,
snowflake, bigquery, redshift, and the ray/couchbase/trino/oracle/athena/mssql/
postgres contrib sources); plus Entity, FeatureView, BaseFeatureView, OnDemandFeatureView,
StreamFeatureView, FeatureService, SavedDataset, Project, ProjectMetadata, the four
transformation/* classes, Aggregation, LabelView, Permission, and the RoleBasedPolicy /
GroupBasedPolicy / NamespaceBasedPolicy / CombinedGroupNamespacePolicy policies.
PushSource.__eq__ already does the right thing (return False), so the correct pattern is
already in-tree.
Steps to reproduce
Run the snippet above, or: define a feature view on a FileSource and feast apply; change that
feature view's source to a SnowflakeSource (same name) and feast apply again → crash.
Specifications
- Version: 0.64.0 and 0.65.0 / master (verified; pattern unchanged)
- Subsystem: core object model (sdk/python/feast)
Possible Solution
Replace each raise TypeError("Comparisons should only involve …") in these __eq__ methods with
return False, matching PushSource.__eq__, which already handles a cross-type comparison this
way. PushSource is the one __eq__ that does this correctly today, and there is no
return NotImplemented anywhere in the codebase, so return False keeps a single consistent
convention rather than introducing a new one. (NotImplemented would be the stricter data-model
answer and would also let a reflected/matcher __eq__ participate, but the codebase already
forgoes that everywhere.)
Add a unit test asserting cross-type equality returns False (not a raise). This can be one sweep
across all 35 sites (uniform and mechanical) or scoped to the DataSource hierarchy (where the
crash reproduces) with a follow-up for the rest — happy to do whichever the maintainers prefer.
PR incoming.
Expected Behavior
Comparing two Feast objects of different concrete types (e.g. FileSource vs SnowflakeSource)
returns False, per Python's data model. In particular, changing a feature view's data source
type and re-running feast apply over an existing registry succeeds.
Current Behavior
__eq__ raises TypeError on a cross-type comparison instead of returning False. Because the
apply/diff path compares registered objects against incoming ones, changing a source type and
re-applying crashes:
Minimal repro:
Root cause: the affected __eq__ methods do
if not isinstance(other, X): raise TypeError("Comparisons should only involve X class objects.").
Python's data model requires __eq__ to return NotImplemented for an unrecognized operand (so
the interpreter can try the reflected op and fall back to identity → False), never raise.
This is systemic — the pattern appears in ~35 __eq__ methods across the object model:
DataSource and its Kafka/Request/Kinesis subclasses; all offline sources (file,
snowflake, bigquery, redshift, and the ray/couchbase/trino/oracle/athena/mssql/
postgres contrib sources); plus Entity, FeatureView, BaseFeatureView, OnDemandFeatureView,
StreamFeatureView, FeatureService, SavedDataset, Project, ProjectMetadata, the four
transformation/* classes, Aggregation, LabelView, Permission, and the RoleBasedPolicy /
GroupBasedPolicy / NamespaceBasedPolicy / CombinedGroupNamespacePolicy policies.
PushSource.__eq__ already does the right thing (return False), so the correct pattern is
already in-tree.
Steps to reproduce
Run the snippet above, or: define a feature view on a FileSource and feast apply; change that
feature view's source to a SnowflakeSource (same name) and feast apply again → crash.
Specifications
Possible Solution
Replace each raise TypeError("Comparisons should only involve …") in these __eq__ methods with
return False, matching PushSource.__eq__, which already handles a cross-type comparison this
way. PushSource is the one __eq__ that does this correctly today, and there is no
return NotImplemented anywhere in the codebase, so return False keeps a single consistent
convention rather than introducing a new one. (NotImplemented would be the stricter data-model
answer and would also let a reflected/matcher __eq__ participate, but the codebase already
forgoes that everywhere.)
Add a unit test asserting cross-type equality returns False (not a raise). This can be one sweep
across all 35 sites (uniform and mechanical) or scoped to the DataSource hierarchy (where the
crash reproduces) with a follow-up for the rest — happy to do whichever the maintainers prefer.
PR incoming.