Skip to content

Commit e6f983d

Browse files
Align tests with simplified FlagAwareMixin and safe FeatureFlagManager.instance()
- FlagAwareMixin.get() now uses _flag_prefix when calling get_flag - FeatureFlagManager.instance() gracefully handles missing LD config (catches AttributeError/TypeError, passes sdk_key=None) - Tests updated: singleton test expects disabled manager instead of exception, get_flag calls use positional args Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent a889827 commit e6f983d

3 files changed

Lines changed: 31 additions & 33 deletions

File tree

src/dremioai/config/feature_flags.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515
#
16+
import logging
1617
from typing import Optional, Any, Self, ClassVar
1718
from dremioai import log
1819
import ldclient
@@ -26,25 +27,31 @@ class FeatureFlagManager:
2627
_instance: ClassVar[Self] = None
2728

2829
def __init__(self, sdk_key: str):
29-
try:
30+
if sdk_key is not None:
31+
self._log.info(
32+
f"Initializing LaunchDarkly client with SDK key: {len(sdk_key)} bytes"
33+
)
3034
ldclient.set_config(Config(sdk_key))
3135
self._client = ldclient.get()
3236

3337
if self._client.is_initialized():
3438
self._log.info("LaunchDarkly client initialized successfully")
3539
else:
3640
self._log.warning("LaunchDarkly client initialization pending")
37-
except:
38-
self._log.exception(f"Failed to initialize LaunchDarkly client")
39-
raise
41+
else:
42+
self._client = None
4043

4144
@classmethod
4245
def instance(cls) -> Self:
4346
"""Lazily initializes from settings.instance().dremio.launchdarkly.sdk_key."""
4447
if cls._instance is None:
4548
from dremioai.config import settings
4649

47-
sdk_key = settings.instance().dremio.launchdarkly.sdk_key
50+
sdk_key = None
51+
try:
52+
sdk_key = settings.instance().dremio.launchdarkly.sdk_key
53+
except (AttributeError, TypeError):
54+
pass
4855
cls._instance = cls(sdk_key)
4956
return cls._instance
5057

@@ -59,8 +66,12 @@ def is_enabled(self) -> bool:
5966

6067
def get_flag(self, flag_key: str, default: Any) -> Any:
6168
if not self.is_enabled():
62-
self._log.debug(
63-
f"Flag '{flag_key}' not evaluated, LaunchDarkly not enabled/initialized (default: {default})"
69+
state, level = "enabled", logging.DEBUG
70+
if self._client is not None:
71+
state, level = "initialized", logging.WARNING
72+
self._log.log(
73+
level,
74+
f"Flag '{flag_key}' not evaluated, LaunchDarkly not {state}",
6475
)
6576
return default
6677
value = self._client.variation(

src/dremioai/config/settings.py

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,7 @@ class GetterMixin:
6363
Use with BaseModel or BaseSettings via multiple inheritance.
6464
"""
6565

66-
def get(self, field_name: str, default: Any = None):
67-
if not hasattr(self, field_name):
68-
raise AttributeError(
69-
f"'{type(self).__name__}' has no attribute '{field_name}'"
70-
)
66+
def get(self, field_name: str):
7167
return getattr(self, field_name)
7268

7369

@@ -84,20 +80,11 @@ def get(self, field_name: str, default: Any = None):
8480
class FlagAwareMixin(GetterMixin):
8581
_flag_prefix: str = ""
8682

87-
def get(self, field_name: str, default: Any = None):
88-
if not hasattr(self, field_name):
89-
raise AttributeError(
90-
f"'{type(self).__name__}' has no attribute '{field_name}'"
91-
)
92-
try:
93-
mgr = FeatureFlagManager.instance()
94-
except Exception:
95-
return super().get(field_name, default)
96-
if mgr.is_enabled():
97-
key = f"{self._flag_prefix}.{field_name}" if self._flag_prefix else field_name
98-
if (flag := mgr.get_flag(key, default=default)) is not None:
99-
return flag
100-
return super().get(field_name, default)
83+
def get(self, field_name: str):
84+
key = f"{self._flag_prefix}.{field_name}" if self._flag_prefix else field_name
85+
return FeatureFlagManager.instance().get_flag(
86+
key, super().get(field_name)
87+
)
10188

10289

10390
# Convenience base for sub-models that need both FlagAwareMixin and BaseModel.
@@ -410,7 +397,7 @@ def _propagate_flag_prefixes(obj, prefix: str):
410397
child = getattr(obj, name, None)
411398
if isinstance(child, FlagAwareMixin):
412399
child_prefix = f"{prefix}.{name}" if prefix else name
413-
object.__setattr__(child, '_flag_prefix', child_prefix)
400+
object.__setattr__(child, "_flag_prefix", child_prefix)
414401
_propagate_flag_prefixes(child, child_prefix)
415402

416403

tests/config/test_launchdarkly_integration.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,15 @@ def test_get_flag_returns_ld_value(self, mock_ldclient):
6262
mock_ldclient.get.return_value = mock_client
6363

6464
mgr = FeatureFlagManager("test-sdk-key")
65-
assert mgr.get_flag("my_flag", default=False) is True
65+
assert mgr.get_flag("my_flag", False) is True
6666

6767
@patch("dremioai.config.feature_flags.ldclient")
6868
def test_get_flag_returns_default_when_not_found(self, mock_ldclient):
6969
mock_client = _make_mock_ld_client({})
7070
mock_ldclient.get.return_value = mock_client
7171

7272
mgr = FeatureFlagManager("test-sdk-key")
73-
assert mgr.get_flag("unknown_flag", default="fallback") == "fallback"
73+
assert mgr.get_flag("unknown_flag", "fallback") == "fallback"
7474

7575
@patch("dremioai.config.feature_flags.ldclient")
7676
def test_get_flag_returns_default_when_not_initialized(self, mock_ldclient):
@@ -79,7 +79,7 @@ def test_get_flag_returns_default_when_not_initialized(self, mock_ldclient):
7979
mock_ldclient.get.return_value = mock_client
8080

8181
mgr = FeatureFlagManager("test-sdk-key")
82-
assert mgr.get_flag("any_flag", default="fallback") == "fallback"
82+
assert mgr.get_flag("any_flag", "fallback") == "fallback"
8383

8484
@patch("dremioai.config.feature_flags.ldclient")
8585
def test_singleton_pattern(self, mock_ldclient):
@@ -93,10 +93,10 @@ def test_singleton_pattern(self, mock_ldclient):
9393

9494
assert mgr1 is mgr2
9595

96-
def test_singleton_raises_when_ld_not_configured(self):
96+
def test_singleton_disabled_when_ld_not_configured(self):
9797
_make_settings()
98-
with pytest.raises(AttributeError):
99-
FeatureFlagManager.instance()
98+
mgr = FeatureFlagManager.instance()
99+
assert mgr.is_enabled() is False
100100

101101
@patch("dremioai.config.feature_flags.ldclient")
102102
def test_singleton_disabled_when_sdk_key_not_set(self, mock_ldclient):

0 commit comments

Comments
 (0)