-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Expand file tree
/
Copy pathtest_eq_cross_type.py
More file actions
122 lines (107 loc) · 4.99 KB
/
Copy pathtest_eq_cross_type.py
File metadata and controls
122 lines (107 loc) · 4.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
"""Cross-type ``__eq__`` regression tests (see #6636).
Comparing two Feast registry objects of different types must return ``False``
instead of raising ``TypeError``. This exercises the shared
``if not isinstance(other, X): return False`` guard across the importable core
object model in one place; the per-type tests for ``DataSource``, ``Entity``,
``LabelView``, and ``RoleBasedPolicy`` live in their own modules.
Most contrib offline sources (athena, couchbase, mssql, oracle, postgres, ray,
trino) and the optional-dependency transformations are intentionally omitted:
the unit environment does not install their drivers, so they cannot be imported
here. Their ``__eq__`` follows the identical, mechanical pattern. SparkSource
is the exception — its module imports without pyspark, and its ``__eq__``
accesses spark-only attributes after the shared ``DataSource`` base check, so
it gets a dedicated cross-subclass test below.
"""
from datetime import timedelta
import pytest
from feast import Entity, FeatureService, FeatureView, Project
from feast.aggregation import Aggregation
from feast.data_format import ParquetFormat, ProtoFormat
from feast.data_source import KafkaSource, KinesisSource, RequestSource
from feast.feature import Feature
from feast.field import Field
from feast.infra.offline_stores.bigquery_source import BigQuerySource
from feast.infra.offline_stores.file_source import FileSource
from feast.infra.offline_stores.redshift_source import RedshiftSource
from feast.infra.offline_stores.snowflake_source import SnowflakeSource
from feast.permissions.permission import Permission
from feast.permissions.policy import (
CombinedGroupNamespacePolicy,
GroupBasedPolicy,
NamespaceBasedPolicy,
RoleBasedPolicy,
)
from feast.types import Int64
from feast.value_type import ValueType
def _instances():
"""One instance of each importable type touched by the __eq__ sweep."""
return {
"Entity": Entity(name="e"),
"Feature": Feature(name="f", dtype=ValueType.INT64),
"ParquetFormat": ParquetFormat(),
"ProtoFormat": ProtoFormat("com.example.Msg"),
"Project": Project(name="proj"),
"Aggregation": Aggregation(column="c", function="sum"),
"Permission": Permission(name="perm"),
"RoleBasedPolicy": RoleBasedPolicy(roles=["reader"]),
"GroupBasedPolicy": GroupBasedPolicy(groups=["g"]),
"NamespaceBasedPolicy": NamespaceBasedPolicy(namespaces=["n"]),
"CombinedGroupNamespacePolicy": CombinedGroupNamespacePolicy(
groups=["g"], namespaces=["n"]
),
"FileSource": FileSource(
name="fs", path="/tmp/x.parquet", timestamp_field="ts"
),
"BigQuerySource": BigQuerySource(
name="bq", table="p.d.t", timestamp_field="ts"
),
"RedshiftSource": RedshiftSource(name="rs", table="t", timestamp_field="ts"),
"SnowflakeSource": SnowflakeSource(
name="sf", database="D", schema="S", table="T", timestamp_field="ts"
),
"KafkaSource": KafkaSource(
name="ks",
kafka_bootstrap_servers="s",
message_format=ProtoFormat("cp"),
topic="t",
timestamp_field="ts",
),
"KinesisSource": KinesisSource(
name="kn",
region="r",
record_format=ProtoFormat("cp"),
stream_name="s",
timestamp_field="ts",
),
"RequestSource": RequestSource(
name="rq", schema=[Field(name="f", dtype=Int64)]
),
"FeatureView": FeatureView(name="fv", ttl=timedelta(days=1)),
"FeatureService": FeatureService(name="svc", features=[]),
}
_CASES = list(_instances().items())
@pytest.mark.parametrize("name,obj", _CASES, ids=[n for n, _ in _CASES])
def test_eq_cross_type_returns_false(name, obj):
# A different-typed operand must compare False, never raise (#6636).
assert (obj == object()) is False
assert (obj == "not a feast object") is False
# __ne__ derives from __eq__, so it must be the inverse.
assert (obj != object()) is True
# The isinstance guard must not break same-object equality.
assert (obj == obj) is True
def test_spark_source_vs_file_source_eq():
# Reported on #6636: swapping a FeatureView's source from FileSource to
# SparkSource. The two sources share the base DataSource fields, so
# SparkSource.__eq__ used to pass the base check and then raise
# AttributeError on FileSource's missing `table`. Both directions must
# simply compare False.
spark_source = pytest.importorskip(
"feast.infra.offline_stores.contrib.spark_offline_store.spark_source"
)
SparkSource = spark_source.SparkSource
spark = SparkSource(name="src", table="t", timestamp_field="ts")
file = FileSource(name="src", path="/tmp/x.parquet", timestamp_field="ts")
assert (spark == file) is False
assert (file == spark) is False
assert (spark == object()) is False
assert (spark == SparkSource(name="src", table="t", timestamp_field="ts")) is True