Skip to content

Commit a9ca048

Browse files
authored
Fix environment variable read issues and potential bugs (#980)
* Fixing env read issues and setup pacakges * revert changes in version fix * Decouple Redis from Feathr client * Update client.py
1 parent 8bdb390 commit a9ca048

File tree

3 files changed

+38
-23
lines changed

3 files changed

+38
-23
lines changed

feathr_project/feathr/client.py

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
from feathr.utils.feature_printer import FeaturePrinter
4040
from feathr.utils.spark_job_params import FeatureGenerationJobParams, FeatureJoinJobParams
4141
from feathr.version import get_version
42+
import importlib.util
43+
4244

4345

4446
class FeathrClient(object):
@@ -71,7 +73,7 @@ def __init__(
7173
config_path (optional): Config yaml file path. See [Feathr Config Template](https://github.com/feathr-ai/feathr/blob/main/feathr_project/feathrcli/data/feathr_user_workspace/feathr_config.yaml) for more details. Defaults to "./feathr_config.yaml".
7274
local_workspace_dir (optional): Set where is the local work space dir. If not set, Feathr will create a temporary folder to store local workspace related files.
7375
credential (optional): Azure credential to access cloud resources, most likely to be the returned result of DefaultAzureCredential(). If not set, Feathr will initialize DefaultAzureCredential() inside the __init__ function to get credentials.
74-
project_registry_tag (optional): Adding tags for project in Feathr registry. This might be useful if you want to tag your project as deprecated, or allow certain customizations on project leve. Default is empty
76+
project_registry_tag (optional): Adding tags for project in Feathr registry. This might be useful if you want to tag your project as deprecated, or allow certain customizations on project level. Default is empty
7577
use_env_vars (optional): Whether to use environment variables to set up the client. If set to False, the client will not use environment variables to set up the client. Defaults to True.
7678
"""
7779
self.logger = logging.getLogger(__name__)
@@ -94,13 +96,19 @@ def __init__(
9496
self.project_name = self.env_config.get(
9597
'project_config__project_name')
9698

97-
# Redis configs
98-
self.redis_host = self.env_config.get(
99-
'online_store__redis__host')
100-
self.redis_port = self.env_config.get(
101-
'online_store__redis__port')
102-
self.redis_ssl_enabled = self.env_config.get(
103-
'online_store__redis__ssl_enabled')
99+
# Redis configs. This is optional unless users have configured Redis host.
100+
if self.env_config.get('online_store__redis__host'):
101+
# For illustrative purposes.
102+
spec = importlib.util.find_spec("redis")
103+
if spec is None:
104+
self.logger.warning('You have configured Redis host, but there is no local Redis client package. Install the package using "pip install redis". ')
105+
self.redis_host = self.env_config.get(
106+
'online_store__redis__host')
107+
self.redis_port = self.env_config.get(
108+
'online_store__redis__port')
109+
self.redis_ssl_enabled = self.env_config.get(
110+
'online_store__redis__ssl_enabled')
111+
self._construct_redis_client()
104112

105113
# Offline store enabled configs; false by default
106114
self.s3_enabled = self.env_config.get(
@@ -182,7 +190,6 @@ def __init__(
182190
master = self.env_config.get('spark_config__local__master')
183191
)
184192

185-
self._construct_redis_client()
186193

187194
self.secret_names = []
188195

@@ -459,15 +466,16 @@ def _construct_redis_key(self, feature_table, key):
459466
key = self._COMPOSITE_KEY_SEPARATOR.join(key)
460467
return feature_table + self._KEY_SEPARATOR + key
461468

462-
def _str_to_bool(self, s):
469+
def _str_to_bool(self, s: str, variable_name = None):
463470
"""Define a function to detect convert string to bool, since Redis client sometimes require a bool and sometimes require a str
464471
"""
465-
if s == 'True' or s == True:
472+
if s.casefold() == 'True'.casefold() or s == True:
466473
return True
467-
elif s == 'False' or s == False:
474+
elif s.casefold() == 'False'.casefold() or s == False:
468475
return False
469476
else:
470-
raise ValueError # evil ValueError that doesn't tell you what the wrong value was
477+
self.logger.warning(f'{s} is not a valid Bool value. Maybe you want to double check if it is set correctly for {variable_name}.')
478+
return s
471479

472480
def _construct_redis_client(self):
473481
"""Constructs the Redis client. The host, port, credential and other parameters can be set via environment
@@ -477,13 +485,12 @@ def _construct_redis_client(self):
477485
host = self.redis_host
478486
port = self.redis_port
479487
ssl_enabled = self.redis_ssl_enabled
480-
redis_client = redis.Redis(
488+
self.redis_client = redis.Redis(
481489
host=host,
482490
port=port,
483491
password=password,
484-
ssl=self._str_to_bool(ssl_enabled))
492+
ssl=self._str_to_bool(ssl_enabled, "ssl_enabled"))
485493
self.logger.info('Redis connection is successful and completed.')
486-
self.redis_client = redis_client
487494

488495

489496
def get_offline_features(self,

feathr_project/feathr/utils/_env_config_reader.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def get(self, key: str, default: str = None) -> str:
6262
if res is not None:
6363
return res
6464

65-
logger.info(f"Config {key} is not found in the environment variable, configuration file, or the remote key value store.")
65+
logger.info(f"Config {key} is not found in the environment variable, configuration file, or the remote key value store. Using default value which is {default}.")
6666

6767
return default
6868

@@ -80,12 +80,20 @@ def get_from_env_or_akv(self, key: str) -> str:
8080
Returns:
8181
Feathr client's config value.
8282
"""
83-
conf_var = (
84-
self._get_variable_from_env(key) or
85-
(self._get_variable_from_akv(key) if self.akv_name else None)
86-
)
83+
res_env = (self._get_variable_from_env(key) if self.use_env_vars else None)
84+
res_keyvault = (self._get_variable_from_akv(key) if self.akv_name else None)
85+
86+
# rewrite the logic below to make sure:
87+
# First we have the order (i.e. res1 > res2 > res3 > default)
88+
# Also previously we use OR for the result, which will yield a bug where say res1=None, res2=False, res3=None. Using OR will result to None result, although res2 actually have value
89+
for res in [res_env, res_keyvault]:
90+
if res is not None:
91+
return res
92+
93+
logger.warning(f"Config {key} is not found in the environment variable or the remote key value store.")
94+
return None
95+
8796

88-
return conf_var
8997

9098
def _get_variable_from_env(self, key: str) -> str:
9199
# make it work for lower case and upper case.

feathr_project/setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,4 +113,4 @@
113113
"Operating System :: OS Independent",
114114
],
115115
python_requires=">=3.7"
116-
)
116+
)

0 commit comments

Comments
 (0)