Skip to content

Commit 73ac3de

Browse files
authored
Improve GCP exception handling (#1561)
* Remove try/catch that fails silently Signed-off-by: Willem Pienaar <git@willem.co> * Remove unused import Signed-off-by: Willem Pienaar <git@willem.co> * Provided clearer exception handling for GCP dependencies Signed-off-by: Willem Pienaar <git@willem.co> * Ensure that GCP dependencies aren't loaded unnecessarily Signed-off-by: Willem Pienaar <git@willem.co> * Fix lint Signed-off-by: Willem Pienaar <git@willem.co>
1 parent cfab265 commit 73ac3de

5 files changed

Lines changed: 42 additions & 26 deletions

File tree

sdk/python/feast/errors.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
from colorama import Fore, Style
2+
3+
14
class FeastObjectNotFoundException(Exception):
25
pass
36

@@ -47,3 +50,13 @@ def __init__(self, module_name, class_name):
4750
super().__init__(
4851
f"Could not import provider '{class_name}' from module '{module_name}'"
4952
)
53+
54+
55+
class FeastExtrasDependencyImportError(Exception):
56+
def __init__(self, extras_type: str, nested_error: str):
57+
message = (
58+
nested_error
59+
+ "\n"
60+
+ f"You may need run {Style.BRIGHT + Fore.GREEN}pip install 'feast[{extras_type}]'{Style.RESET_ALL}"
61+
)
62+
super().__init__(message)

sdk/python/feast/infra/gcp.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import mmh3
77
import pandas
88
import pyarrow
9-
from google.auth.exceptions import DefaultCredentialsError
109
from tqdm import tqdm
1110

1211
from feast import FeatureTable, utils
@@ -27,6 +26,14 @@
2726
from feast.registry import Registry
2827
from feast.repo_config import DatastoreOnlineStoreConfig, RepoConfig
2928

29+
try:
30+
from google.auth.exceptions import DefaultCredentialsError
31+
from google.cloud import bigquery, datastore
32+
except ImportError as e:
33+
from feast.errors import FeastExtrasDependencyImportError
34+
35+
raise FeastExtrasDependencyImportError("gcp", str(e))
36+
3037

3138
class GcpProvider(Provider):
3239
_gcp_project_id: Optional[str]
@@ -39,8 +46,6 @@ def __init__(self, config: RepoConfig):
3946
self._gcp_project_id = None
4047

4148
def _initialize_client(self):
42-
from google.cloud import datastore
43-
4449
try:
4550
if self._gcp_project_id is not None:
4651
return datastore.Client(self._gcp_project_id)
@@ -49,7 +54,8 @@ def _initialize_client(self):
4954
except DefaultCredentialsError as e:
5055
raise FeastProviderLoginError(
5156
str(e)
52-
+ '\nIt may be necessary to run "gcloud auth application-default login" if you would like to use your local Google Cloud account'
57+
+ '\nIt may be necessary to run "gcloud auth application-default login" if you would like to use your '
58+
"local Google Cloud account "
5359
)
5460

5561
def update_infra(
@@ -61,7 +67,6 @@ def update_infra(
6167
entities_to_keep: Sequence[Entity],
6268
partial: bool,
6369
):
64-
from google.cloud import datastore
6570

6671
client = self._initialize_client()
6772

@@ -190,8 +195,6 @@ def materialize_single_feature_view(
190195

191196
@staticmethod
192197
def _pull_query(query: str) -> pyarrow.Table:
193-
from google.cloud import bigquery
194-
195198
client = bigquery.Client()
196199
query_job = client.query(query)
197200
return query_job.to_arrow()
@@ -248,8 +251,6 @@ def _write_minibatch(
248251
],
249252
progress: Optional[Callable[[int], Any]],
250253
):
251-
from google.cloud import datastore
252-
253254
entities = []
254255
for entity_key, features, timestamp, created_ts in data:
255256
document_id = compute_datastore_entity_id(entity_key)

sdk/python/feast/infra/offline_stores/bigquery.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
import pandas
77
import pyarrow
8-
from google.auth.exceptions import DefaultCredentialsError
98
from jinja2 import BaseLoader, Environment
109

1110
from feast.data_source import BigQuerySource, DataSource
@@ -20,6 +19,15 @@
2019
from feast.registry import Registry
2120
from feast.repo_config import RepoConfig
2221

22+
try:
23+
from google.auth.exceptions import DefaultCredentialsError
24+
from google.cloud import bigquery
25+
26+
except ImportError as e:
27+
from feast.errors import FeastExtrasDependencyImportError
28+
29+
raise FeastExtrasDependencyImportError("gcp", str(e))
30+
2331

2432
class BigQueryOfflineStore(OfflineStore):
2533
@staticmethod
@@ -192,7 +200,6 @@ class FeatureViewQueryContext:
192200

193201
def _upload_entity_df_into_bigquery(project, entity_df, client) -> str:
194202
"""Uploads a Pandas entity dataframe into a BigQuery table and returns a reference to the resulting table"""
195-
from google.cloud import bigquery
196203

197204
# First create the BigQuery dataset if it doesn't exist
198205
dataset = bigquery.Dataset(f"{client.project}.feast_{project}")
@@ -303,8 +310,6 @@ def build_point_in_time_query(
303310

304311
def _get_bigquery_client():
305312
try:
306-
from google.cloud import bigquery
307-
308313
client = bigquery.Client()
309314
except DefaultCredentialsError as e:
310315
raise FeastProviderLoginError(

sdk/python/feast/infra/offline_stores/helpers.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
from typing import List
22

33
from feast.data_source import BigQuerySource, DataSource, FileSource
4-
from feast.infra.offline_stores.bigquery import BigQueryOfflineStore
5-
from feast.infra.offline_stores.file import FileOfflineStore
64
from feast.infra.offline_stores.offline_store import OfflineStore
75

86

@@ -13,10 +11,14 @@ def get_offline_store_from_sources(sources: List[DataSource]) -> OfflineStore:
1311

1412
# Retrieve features from ParquetOfflineStore
1513
if all(source == FileSource for source in source_types):
14+
from feast.infra.offline_stores.file import FileOfflineStore
15+
1616
return FileOfflineStore()
1717

1818
# Retrieve features from BigQueryOfflineStore
1919
if all(source == BigQuerySource for source in source_types):
20+
from feast.infra.offline_stores.bigquery import BigQueryOfflineStore
21+
2022
return BigQueryOfflineStore()
2123

2224
# Could not map inputs to an OfflineStore implementation

sdk/python/feast/registry.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -431,18 +431,13 @@ def _write_registry(self, registry_proto: RegistryProto):
431431
class GCSRegistryStore(RegistryStore):
432432
def __init__(self, uri: str):
433433
try:
434-
from google.auth.exceptions import DefaultCredentialsError
435434
from google.cloud import storage
436-
except ImportError:
437-
# TODO: Ensure versioning depends on requirements.txt/setup.py and isn't hardcoded
438-
raise ImportError(
439-
"Install package google-cloud-storage==1.20.* for gcs support"
440-
"run ```pip install google-cloud-storage==1.20.*```"
441-
)
442-
try:
443-
self.gcs_client = storage.Client()
444-
except DefaultCredentialsError:
445-
self.gcs_client = storage.Client.create_anonymous_client()
435+
except ImportError as e:
436+
from feast.errors import FeastExtrasDependencyImportError
437+
438+
raise FeastExtrasDependencyImportError("gcp", str(e))
439+
440+
self.gcs_client = storage.Client()
446441
self._uri = urlparse(uri)
447442
self._bucket = self._uri.hostname
448443
self._blob = self._uri.path.lstrip("/")

0 commit comments

Comments
 (0)