From e06bc72eb6eb013eb14bd713347f8b858817308b Mon Sep 17 00:00:00 2001 From: Matthew Keeler Date: Wed, 3 Dec 2025 12:55:10 -0500 Subject: [PATCH] chore: Expose flag change listeners from data system --- ldclient/client.py | 5 ++--- ldclient/impl/datasystem/__init__.py | 9 +++------ ldclient/impl/datasystem/fdv1.py | 16 ++------------- ldclient/impl/datasystem/fdv2.py | 20 +++---------------- .../impl/datasystem/test_fdv2_datasystem.py | 16 +++++++-------- .../impl/datasystem/test_fdv2_persistence.py | 8 ++++---- 6 files changed, 22 insertions(+), 52 deletions(-) diff --git a/ldclient/client.py b/ldclient/client.py index 7022f13..1becbba 100644 --- a/ldclient/client.py +++ b/ldclient/client.py @@ -263,8 +263,8 @@ def __start_up(self, start_wait: float): else: self._data_system = FDv2(self._config, datasystem_config) - # Provide flag evaluation function for value-change tracking - self._data_system.set_flag_value_eval_fn( # type: ignore + self.__flag_tracker = FlagTrackerImpl( + self._data_system.flag_change_listeners, lambda key, context: self.variation(key, context, None) ) # Expose providers and store from data system @@ -272,7 +272,6 @@ def __start_up(self, start_wait: float): self.__data_source_status_provider = ( self._data_system.data_source_status_provider ) - self.__flag_tracker = self._data_system.flag_tracker big_segment_store_manager = BigSegmentStoreManager(self._config.big_segments) self.__big_segment_store_manager = big_segment_store_manager diff --git a/ldclient/impl/datasystem/__init__.py b/ldclient/impl/datasystem/__init__.py index c7a3682..c1d65a9 100644 --- a/ldclient/impl/datasystem/__init__.py +++ b/ldclient/impl/datasystem/__init__.py @@ -8,6 +8,7 @@ from threading import Event from typing import Protocol, runtime_checkable +from ldclient.impl.listeners import Listeners from ldclient.interfaces import ( DataSourceStatusProvider, DataStoreStatusProvider, @@ -111,13 +112,9 @@ def data_store_status_provider(self) -> DataStoreStatusProvider: @property @abstractmethod - def flag_tracker(self) -> FlagTracker: + def flag_change_listeners(self) -> Listeners: """ - Returns an interface for tracking changes in feature flag configurations. - - The :class:`ldclient.interfaces.FlagTracker` contains methods for - requesting notifications about feature flag changes using an event - listener model. + Returns the collection of listeners for flag change events. """ raise NotImplementedError diff --git a/ldclient/impl/datasystem/fdv1.py b/ldclient/impl/datasystem/fdv1.py index 07828a5..ee1656e 100644 --- a/ldclient/impl/datasystem/fdv1.py +++ b/ldclient/impl/datasystem/fdv1.py @@ -60,10 +60,6 @@ def __init__(self, config: Config): # Set up data source plumbing self._data_source_listeners = Listeners() self._flag_change_listeners = Listeners() - self._flag_tracker_impl = FlagTrackerImpl( - self._flag_change_listeners, - lambda key, context: None, # Replaced by client to use its evaluation method - ) self._data_source_update_sink = DataSourceUpdateSinkImpl( self._store_wrapper, self._data_source_listeners, @@ -102,14 +98,6 @@ def stop(self): def store(self) -> ReadOnlyStore: return self._store_wrapper - def set_flag_value_eval_fn(self, eval_fn): - """ - Injects the flag value evaluation function used by the flag tracker to - compute FlagValueChange events. The function signature should be - (key: str, context: Context) -> Any. - """ - self._flag_tracker_impl = FlagTrackerImpl(self._flag_change_listeners, eval_fn) - def set_diagnostic_accumulator(self, diagnostic_accumulator: DiagnosticAccumulator): """ Sets the diagnostic accumulator for streaming initialization metrics. @@ -126,8 +114,8 @@ def data_store_status_provider(self) -> DataStoreStatusProvider: return self._data_store_status_provider_impl @property - def flag_tracker(self) -> FlagTracker: - return self._flag_tracker_impl + def flag_change_listeners(self) -> Listeners: + return self._flag_change_listeners @property def data_availability(self) -> DataAvailability: diff --git a/ldclient/impl/datasystem/fdv2.py b/ldclient/impl/datasystem/fdv2.py index d411fd5..c37b9d7 100644 --- a/ldclient/impl/datasystem/fdv2.py +++ b/ldclient/impl/datasystem/fdv2.py @@ -295,12 +295,6 @@ def __init__( wrapper, writable, self._data_store_status_provider ) - # Flag tracker (evaluation function set later by client) - self._flag_tracker = FlagTrackerImpl( - self._flag_change_listeners, - lambda key, context: None # Placeholder, replaced by client - ) - # Threading self._stop_event = Event() self._lock = ReadWriteLock() @@ -659,14 +653,6 @@ def store(self) -> ReadOnlyStore: """Get the underlying store for flag evaluation.""" return self._store.get_active_store() - def set_flag_value_eval_fn(self, eval_fn): - """ - Set the flag value evaluation function for the flag tracker. - - :param eval_fn: Function with signature (key: str, context: Context) -> Any - """ - self._flag_tracker = FlagTrackerImpl(self._flag_change_listeners, eval_fn) - @property def data_source_status_provider(self) -> DataSourceStatusProvider: """Get the data source status provider.""" @@ -678,9 +664,9 @@ def data_store_status_provider(self) -> DataStoreStatusProvider: return self._data_store_status_provider @property - def flag_tracker(self) -> FlagTracker: - """Get the flag tracker for monitoring flag changes.""" - return self._flag_tracker + def flag_change_listeners(self) -> Listeners: + """Get the collection of listeners for flag change events.""" + return self._flag_change_listeners @property def data_availability(self) -> DataAvailability: diff --git a/ldclient/testing/impl/datasystem/test_fdv2_datasystem.py b/ldclient/testing/impl/datasystem/test_fdv2_datasystem.py index c77f799..07cfecc 100644 --- a/ldclient/testing/impl/datasystem/test_fdv2_datasystem.py +++ b/ldclient/testing/impl/datasystem/test_fdv2_datasystem.py @@ -55,7 +55,7 @@ def listener(flag_change: FlagChange): if count == 3: modified.set() - fdv2.flag_tracker.add_listener(listener) + fdv2.flag_change_listeners.add(listener) fdv2.start(set_on_ready) assert set_on_ready.wait(1), "Data system did not become ready in time" @@ -86,7 +86,7 @@ def listener(flag_change: FlagChange): changes.append(flag_change) changed.set() - fdv2.flag_tracker.add_listener(listener) + fdv2.flag_change_listeners.add(listener) fdv2.start(set_on_ready) assert set_on_ready.wait(1), "Data system did not become ready in time" @@ -140,7 +140,7 @@ def listener(flag_change: FlagChange): set_on_ready = Event() fdv2 = FDv2(Config(sdk_key="dummy"), data_system_config) - fdv2.flag_tracker.add_listener(listener) + fdv2.flag_change_listeners.add(listener) fdv2.start(set_on_ready) assert set_on_ready.wait(1), "Data system did not become ready in time" @@ -215,7 +215,7 @@ def listener(flag_change: FlagChange): set_on_ready = Event() fdv2 = FDv2(Config(sdk_key="dummy"), data_system_config) - fdv2.flag_tracker.add_listener(listener) + fdv2.flag_change_listeners.add(listener) fdv2.start(set_on_ready) assert set_on_ready.wait(1), "Data system did not become ready in time" @@ -269,7 +269,7 @@ def listener(flag_change: FlagChange): set_on_ready = Event() fdv2 = FDv2(Config(sdk_key="dummy"), data_system_config) - fdv2.flag_tracker.add_listener(listener) + fdv2.flag_change_listeners.add(listener) fdv2.start(set_on_ready) assert set_on_ready.wait(1), "Data system did not become ready in time" @@ -324,7 +324,7 @@ def listener(flag_change: FlagChange): set_on_ready = Event() fdv2 = FDv2(Config(sdk_key="dummy"), data_system_config) - fdv2.flag_tracker.add_listener(listener) + fdv2.flag_change_listeners.add(listener) fdv2.start(set_on_ready) assert set_on_ready.wait(1), "Data system did not become ready in time" @@ -484,7 +484,7 @@ def listener(_: FlagChange): if count == 3: synchronizer_ran.set() - fdv2.flag_tracker.add_listener(listener) + fdv2.flag_change_listeners.add(listener) fdv2.start(set_on_ready) assert set_on_ready.wait(1), "Data system did not become ready in time" @@ -537,7 +537,7 @@ def listener(_: FlagChange): nonlocal count count += 1 - fdv2.flag_tracker.add_listener(listener) + fdv2.flag_change_listeners.add(listener) fdv2.start(set_on_ready) assert set_on_ready.wait(1), "Data system did not become ready in time" diff --git a/ldclient/testing/impl/datasystem/test_fdv2_persistence.py b/ldclient/testing/impl/datasystem/test_fdv2_persistence.py index a59fc77..81d42b9 100644 --- a/ldclient/testing/impl/datasystem/test_fdv2_persistence.py +++ b/ldclient/testing/impl/datasystem/test_fdv2_persistence.py @@ -239,7 +239,7 @@ def listener(flag_change: FlagChange): ): # First change is from initial sync, second is our update flag_changed.set() - fdv2.flag_tracker.add_listener(listener) + fdv2.flag_change_listeners.add(listener) fdv2.start(set_on_ready) assert set_on_ready.wait(1), "Data system did not become ready in time" @@ -293,7 +293,7 @@ def listener(flag_change: FlagChange): ): # First change is from initial sync, second is our update flag_changed.set() - fdv2.flag_tracker.add_listener(listener) + fdv2.flag_change_listeners.add(listener) fdv2.start(set_on_ready) assert set_on_ready.wait(1), "Data system did not become ready in time" @@ -341,7 +341,7 @@ def listener(flag_change: FlagChange): if flag_change.key == "sync-flag": sync_flag_arrived.set() - fdv2.flag_tracker.add_listener(listener) + fdv2.flag_change_listeners.add(listener) fdv2.start(set_on_ready) assert set_on_ready.wait(1), "Data system did not become ready in time" @@ -571,7 +571,7 @@ def test_persistent_store_outage_recovery_flushes_on_recovery(): persistent_store.reset_operation_tracking() event = Event() - fdv2.flag_tracker.add_listener(lambda _flag_change: event.set()) + fdv2.flag_change_listeners.add(lambda _flag_change: event.set()) # Simulate a new flag being added while store is "offline" # (In reality, the store is still online, but we're testing the recovery mechanism) td_synchronizer.update(td_synchronizer.flag("new-flag").on(False))