Skip to content

Commit dddcb00

Browse files
fix: Make pytest config compatible with newer pytest
Signed-off-by: Francisco Javier Arceo <farceo@redhat.com>
1 parent 1669661 commit dddcb00

11 files changed

Lines changed: 180 additions & 51 deletions

File tree

infra/feast-operator/api/v1/featurestore_types.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,7 @@ type OnlineStoreFilePersistence struct {
626626
// OnlineStoreDBStorePersistence configures the DB store persistence for the online store service
627627
type OnlineStoreDBStorePersistence struct {
628628
// Type of the persistence type you want to use.
629-
// +kubebuilder:validation:Enum=snowflake.online;redis;datastore;dynamodb;bigtable;postgres;cassandra;mysql;hazelcast;singlestore;hbase;elasticsearch;qdrant;couchbase.online;milvus;hybrid;mongodb;aerospike
629+
// +kubebuilder:validation:Enum=snowflake.online;redis;datastore;dynamodb;bigtable;postgres;cassandra;mysql;hazelcast;singlestore;hbase;elasticsearch;qdrant;couchbase.online;milvus;hybrid;mongodb;aerospike;scylladb
630630
Type string `json:"type"`
631631
// Data store parameters should be placed as-is from the "feature_store.yaml" under the secret key. "registry_type" & "type" fields should be removed.
632632
SecretRef corev1.LocalObjectReference `json:"secretRef"`
@@ -653,6 +653,7 @@ var ValidOnlineStoreDBStorePersistenceTypes = []string{
653653
"hybrid",
654654
"mongodb",
655655
"aerospike",
656+
"scylladb",
656657
}
657658

658659
// LocalRegistryConfig configures the registry service

infra/feast-operator/api/v1alpha1/featurestore_types.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ type OnlineStoreFilePersistence struct {
373373
// OnlineStoreDBStorePersistence configures the DB store persistence for the online store service
374374
type OnlineStoreDBStorePersistence struct {
375375
// Type of the persistence type you want to use.
376-
// +kubebuilder:validation:Enum=snowflake.online;redis;datastore;dynamodb;bigtable;postgres;cassandra;mysql;hazelcast;singlestore;hbase;elasticsearch;qdrant;couchbase.online;milvus;hybrid;mongodb;aerospike
376+
// +kubebuilder:validation:Enum=snowflake.online;redis;datastore;dynamodb;bigtable;postgres;cassandra;mysql;hazelcast;singlestore;hbase;elasticsearch;qdrant;couchbase.online;milvus;hybrid;mongodb;aerospike;scylladb
377377
Type string `json:"type"`
378378
// Data store parameters should be placed as-is from the "feature_store.yaml" under the secret key. "registry_type" & "type" fields should be removed.
379379
SecretRef corev1.LocalObjectReference `json:"secretRef"`
@@ -400,6 +400,7 @@ var ValidOnlineStoreDBStorePersistenceTypes = []string{
400400
"hybrid",
401401
"mongodb",
402402
"aerospike",
403+
"scylladb",
403404
}
404405

405406
// LocalRegistryConfig configures the registry service

infra/feast-operator/config/crd/bases/feast.dev_featurestores.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2400,6 +2400,7 @@ spec:
24002400
- hybrid
24012401
- mongodb
24022402
- aerospike
2403+
- scylladb
24032404
type: string
24042405
required:
24052406
- secretRef
@@ -8706,6 +8707,7 @@ spec:
87068707
- hybrid
87078708
- mongodb
87088709
- aerospike
8710+
- scylladb
87098711
type: string
87108712
required:
87118713
- secretRef
@@ -14230,6 +14232,7 @@ spec:
1423014232
- hybrid
1423114233
- mongodb
1423214234
- aerospike
14235+
- scylladb
1423314236
type: string
1423414237
required:
1423514238
- secretRef
@@ -18736,6 +18739,7 @@ spec:
1873618739
- hybrid
1873718740
- mongodb
1873818741
- aerospike
18742+
- scylladb
1873918743
type: string
1874018744
required:
1874118745
- secretRef

infra/feast-operator/dist/install.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2408,6 +2408,7 @@ spec:
24082408
- hybrid
24092409
- mongodb
24102410
- aerospike
2411+
- scylladb
24112412
type: string
24122413
required:
24132414
- secretRef
@@ -8714,6 +8715,7 @@ spec:
87148715
- hybrid
87158716
- mongodb
87168717
- aerospike
8718+
- scylladb
87178719
type: string
87188720
required:
87198721
- secretRef
@@ -14238,6 +14240,7 @@ spec:
1423814240
- hybrid
1423914241
- mongodb
1424014242
- aerospike
14243+
- scylladb
1424114244
type: string
1424214245
required:
1424314246
- secretRef
@@ -18744,6 +18747,7 @@ spec:
1874418747
- hybrid
1874518748
- mongodb
1874618749
- aerospike
18750+
- scylladb
1874718751
type: string
1874818752
required:
1874918753
- secretRef

sdk/python/feast/infra/online_stores/sqlite.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import os
1717
import sqlite3
1818
import sys
19+
import time
1920
from datetime import date, datetime, timezone
2021
from pathlib import Path
2122
from typing import (
@@ -353,10 +354,23 @@ def teardown(
353354
tables: Sequence[FeatureView],
354355
entities: Sequence[Entity],
355356
):
356-
try:
357-
os.unlink(self._get_db_path(config))
358-
except FileNotFoundError:
359-
pass
357+
if self._conn is not None:
358+
try:
359+
self._conn.close()
360+
finally:
361+
self._conn = None
362+
363+
db_path = self._get_db_path(config)
364+
for attempt in range(10):
365+
try:
366+
os.unlink(db_path)
367+
return
368+
except FileNotFoundError:
369+
return
370+
except PermissionError:
371+
if attempt == 9:
372+
raise
373+
time.sleep(0.25)
360374

361375
def retrieve_online_documents(
362376
self,

sdk/python/feast/infra/registry/registry.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,12 @@ def get_registry_store_class_from_type(registry_store_type: str):
155155

156156
def get_registry_store_class_from_scheme(registry_path: str):
157157
uri = urlparse(registry_path)
158+
if uri.scheme == "" or (
159+
len(uri.scheme) == 1 and registry_path[1:3] in (":\\", ":/")
160+
):
161+
registry_store_type = REGISTRY_STORE_CLASS_FOR_SCHEME["file"]
162+
return get_registry_store_class_from_type(registry_store_type)
163+
158164
if uri.scheme not in REGISTRY_STORE_CLASS_FOR_SCHEME:
159165
raise Exception(
160166
f"Registry path {registry_path} has unsupported scheme {uri.scheme}. "

sdk/python/pytest.ini

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ asyncio_mode = auto
33
env =
44
IS_TEST=True
55
filterwarnings =
6-
error::_pytest.warning_types.PytestConfigWarning
7-
error::_pytest.warning_types.PytestUnhandledCoroutineWarning
6+
error::pytest.PytestConfigWarning
7+
error:.*was never awaited.*:RuntimeWarning
88
ignore::DeprecationWarning:pyspark.sql.pandas.*:
99
ignore::DeprecationWarning:pyspark.sql.connect.*:
1010
ignore::DeprecationWarning:httpx.*:

sdk/python/tests/conftest.py

Lines changed: 121 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14+
import importlib
1415
import logging
1516
import multiprocessing
1617
import os
@@ -20,7 +21,7 @@
2021
from multiprocessing import Process
2122
from sys import platform
2223
from textwrap import dedent
23-
from typing import Any, Dict, List, Tuple, no_type_check
24+
from typing import Any, Dict, List, Optional, Tuple, no_type_check
2425
from unittest import mock
2526

2627
import pandas as pd
@@ -36,34 +37,90 @@
3637
create_document_dataset,
3738
create_image_dataset,
3839
)
39-
from tests.universal.feature_repos.integration_test_repo_config import ( # noqa: E402
40-
IntegrationTestRepoConfig,
41-
)
42-
from tests.universal.feature_repos.repo_configuration import ( # noqa: E402
43-
AVAILABLE_OFFLINE_STORES,
44-
AVAILABLE_ONLINE_STORES,
45-
OFFLINE_STORE_TO_PROVIDER_CONFIG,
46-
Environment,
47-
TestData,
48-
construct_test_environment,
49-
construct_universal_feature_views,
50-
construct_universal_test_data,
51-
)
52-
from tests.universal.feature_repos.universal.data_sources.file import ( # noqa: E402
53-
FileDataSourceCreator,
54-
)
55-
from tests.universal.feature_repos.universal.entities import ( # noqa: E402
56-
customer,
57-
driver,
58-
location,
59-
)
60-
from tests.utils.auth_permissions_util import default_store
6140
from tests.utils.http_server import check_port_open, free_port # noqa: E402
62-
from tests.utils.ssl_certifcates_util import (
63-
combine_trust_stores,
64-
create_ca_trust_store,
65-
generate_self_signed_cert,
66-
)
41+
42+
IntegrationTestRepoConfig: Any = None
43+
Environment = Any
44+
TestData = Any
45+
AVAILABLE_OFFLINE_STORES: Any = None
46+
AVAILABLE_ONLINE_STORES: Any = None
47+
OFFLINE_STORE_TO_PROVIDER_CONFIG: Any = None
48+
construct_test_environment: Any = None
49+
construct_universal_feature_views: Any = None
50+
construct_universal_test_data: Any = None
51+
FileDataSourceCreator: Any = None
52+
customer: Any = None
53+
driver: Any = None
54+
location: Any = None
55+
_universal_deps_missing_reason: Optional[str] = None
56+
57+
58+
def _load_universal_feature_repo_deps() -> bool:
59+
global IntegrationTestRepoConfig
60+
global Environment
61+
global TestData
62+
global AVAILABLE_OFFLINE_STORES
63+
global AVAILABLE_ONLINE_STORES
64+
global OFFLINE_STORE_TO_PROVIDER_CONFIG
65+
global construct_test_environment
66+
global construct_universal_feature_views
67+
global construct_universal_test_data
68+
global FileDataSourceCreator
69+
global customer
70+
global driver
71+
global location
72+
global _universal_deps_missing_reason
73+
74+
if IntegrationTestRepoConfig is not None:
75+
return True
76+
77+
try:
78+
integration_config = importlib.import_module(
79+
"tests.universal.feature_repos.integration_test_repo_config"
80+
)
81+
repo_configuration = importlib.import_module(
82+
"tests.universal.feature_repos.repo_configuration"
83+
)
84+
file_data_sources = importlib.import_module(
85+
"tests.universal.feature_repos.universal.data_sources.file"
86+
)
87+
entities = importlib.import_module(
88+
"tests.universal.feature_repos.universal.entities"
89+
)
90+
except ModuleNotFoundError as e:
91+
_universal_deps_missing_reason = (
92+
f"Optional integration test dependency is not installed: {e.name}"
93+
)
94+
return False
95+
96+
IntegrationTestRepoConfig = integration_config.IntegrationTestRepoConfig
97+
Environment = repo_configuration.Environment
98+
TestData = repo_configuration.TestData
99+
AVAILABLE_OFFLINE_STORES = repo_configuration.AVAILABLE_OFFLINE_STORES
100+
AVAILABLE_ONLINE_STORES = repo_configuration.AVAILABLE_ONLINE_STORES
101+
OFFLINE_STORE_TO_PROVIDER_CONFIG = (
102+
repo_configuration.OFFLINE_STORE_TO_PROVIDER_CONFIG
103+
)
104+
construct_test_environment = repo_configuration.construct_test_environment
105+
construct_universal_feature_views = (
106+
repo_configuration.construct_universal_feature_views
107+
)
108+
construct_universal_test_data = repo_configuration.construct_universal_test_data
109+
FileDataSourceCreator = file_data_sources.FileDataSourceCreator
110+
customer = entities.customer
111+
driver = entities.driver
112+
location = entities.location
113+
_universal_deps_missing_reason = None
114+
return True
115+
116+
117+
def _skip_missing_universal_feature_repo_deps() -> None:
118+
if not _load_universal_feature_repo_deps():
119+
pytest.skip(
120+
_universal_deps_missing_reason
121+
or "Optional integration test dependencies are not installed"
122+
)
123+
67124

68125
logger = logging.getLogger(__name__)
69126

@@ -85,7 +142,7 @@
85142

86143

87144
def pytest_configure(config):
88-
if platform in ["darwin", "windows"]:
145+
if platform == "darwin" or platform.startswith("win"):
89146
multiprocessing.set_start_method("spawn", force=True)
90147
else:
91148
multiprocessing.set_start_method("fork")
@@ -192,6 +249,7 @@ def start_test_local_server(repo_path: str, port: int):
192249

193250
@pytest.fixture
194251
def environment(request, worker_id):
252+
_skip_missing_universal_feature_repo_deps()
195253
e = construct_test_environment(
196254
request.param,
197255
worker_id=worker_id,
@@ -211,6 +269,7 @@ def environment(request, worker_id):
211269

212270
@pytest.fixture
213271
def vectordb_environment(request, worker_id):
272+
_skip_missing_universal_feature_repo_deps()
214273
e = construct_test_environment(
215274
request.param,
216275
worker_id=worker_id,
@@ -251,6 +310,23 @@ def pytest_generate_tests(metafunc: pytest.Metafunc):
251310
parameter should point to the same Python object (hence, we use _config_cache dict to store those objects).
252311
"""
253312
if "environment" in metafunc.fixturenames:
313+
if not _load_universal_feature_repo_deps():
314+
metafunc.parametrize(
315+
"environment",
316+
[
317+
pytest.param(
318+
None,
319+
marks=pytest.mark.skip(
320+
reason=_universal_deps_missing_reason
321+
or "Optional integration test dependencies are not installed"
322+
),
323+
)
324+
],
325+
indirect=True,
326+
ids=["missing_optional_integration_deps"],
327+
)
328+
return
329+
254330
markers = {m.name: m for m in metafunc.definition.own_markers}
255331
offline_stores = None
256332
if "universal_offline_stores" in markers:
@@ -371,6 +447,7 @@ def feature_server_endpoint(environment):
371447

372448
@pytest.fixture
373449
def universal_data_sources(environment) -> TestData:
450+
_skip_missing_universal_feature_repo_deps()
374451
return construct_universal_test_data(environment)
375452

376453

@@ -394,6 +471,7 @@ def feature_store_for_online_retrieval(
394471
Returns a feature store that is ready for online retrieval, along with entity rows and feature
395472
refs that can be used to query for online features.
396473
"""
474+
_skip_missing_universal_feature_repo_deps()
397475
fs = environment.feature_store
398476
entities, datasets, data_sources = universal_data_sources
399477
feature_views = construct_universal_feature_views(data_sources)
@@ -478,6 +556,11 @@ def server_port():
478556

479557
@pytest.fixture
480558
def feature_store(temp_dir, auth_config, applied_permissions):
559+
try:
560+
from tests.utils.auth_permissions_util import default_store
561+
except ModuleNotFoundError as e:
562+
pytest.skip(f"Optional auth test dependency is not installed: {e.name}")
563+
481564
print(f"Creating store at {temp_dir}")
482565
return default_store(str(temp_dir), auth_config, applied_permissions)
483566

@@ -542,6 +625,15 @@ def auth_config(request, is_integration_test):
542625

543626
@pytest.fixture(scope="module")
544627
def tls_mode(request):
628+
try:
629+
from tests.utils.ssl_certifcates_util import (
630+
combine_trust_stores,
631+
create_ca_trust_store,
632+
generate_self_signed_cert,
633+
)
634+
except ModuleNotFoundError as e:
635+
pytest.skip(f"Optional TLS test dependency is not installed: {e.name}")
636+
545637
is_tls_mode = request.param[0]
546638
output_combined_truststore_path = ""
547639

0 commit comments

Comments
 (0)