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.
FileSourcevsSnowflakeSource)returns
False, per Python's data model. In particular, changing a feature view's data sourcetype and re-running
feast applyover an existing registry succeeds.Current Behavior
__eq__raisesTypeErroron a cross-type comparison instead of returningFalse. Because theapply/diff path compares registered objects against incoming ones, changing a source type and
re-applying crashes:
Minimal repro:
Root cause: the affected
__eq__methods doif not isinstance(other, X): raise TypeError("Comparisons should only involve X class objects.").Python's data model requires
__eq__to returnNotImplementedfor an unrecognized operand (sothe 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:DataSourceand itsKafka/Request/Kinesissubclasses; all offline sources (file,snowflake,bigquery,redshift, and theray/couchbase/trino/oracle/athena/mssql/postgrescontrib sources); plusEntity,FeatureView,BaseFeatureView,OnDemandFeatureView,StreamFeatureView,FeatureService,SavedDataset,Project,ProjectMetadata, the fourtransformation/*classes,Aggregation,LabelView,Permission, and theRoleBasedPolicy/GroupBasedPolicy/NamespaceBasedPolicy/CombinedGroupNamespacePolicypolicies.PushSource.__eq__already does the right thing (return False), so the correct pattern isalready in-tree.
Steps to reproduce
Run the snippet above, or: define a feature view on a
FileSourceandfeast apply; change thatfeature view's source to a
SnowflakeSource(same name) andfeast applyagain → crash.Specifications
sdk/python/feast)Possible Solution
Replace each
raise TypeError("Comparisons should only involve …")in these__eq__methods withreturn False, matchingPushSource.__eq__, which already handles a cross-type comparison thisway.
PushSourceis the one__eq__that does this correctly today, and there is noreturn NotImplementedanywhere in the codebase, soreturn Falsekeeps a single consistentconvention rather than introducing a new one. (
NotImplementedwould be the stricter data-modelanswer and would also let a reflected/matcher
__eq__participate, but the codebase alreadyforgoes that everywhere.)
Add a unit test asserting cross-type equality returns
False(not a raise). This can be one sweepacross all 35 sites (uniform and mechanical) or scoped to the
DataSourcehierarchy (where thecrash reproduces) with a follow-up for the rest — happy to do whichever the maintainers prefer.
PR incoming.