Source code for ldclient.interfaces

"""
This submodule contains interfaces for various components of the SDK.

They may be useful in writing new implementations of these components, or for testing.
"""

from abc import ABCMeta, abstractmethod, abstractproperty


[docs]class FeatureStore(object): """ Interface for a versioned store for feature flags and related objects received from LaunchDarkly. Implementations should permit concurrent access and updates. An "object", for ``FeatureStore``, is simply a dict of arbitrary data which must have at least three properties: ``key`` (its unique key), ``version`` (the version number provided by LaunchDarkly), and ``deleted`` (True if this is a placeholder for a deleted object). Delete and upsert requests are versioned: if the version number in the request is less than the currently stored version of the object, the request should be ignored. These semantics support the primary use case for the store, which synchronizes a collection of objects based on update messages that may be received out-of-order. """ __metaclass__ = ABCMeta
[docs] @abstractmethod def get(self, kind, key, callback=lambda x: x): """ Retrieves the object to which the specified key is mapped, or None if the key is not found or the associated object has a ``deleted`` property of True. The retrieved object, if any (a dict) can be transformed by the specified callback. :param kind: The kind of object to get :type kind: VersionedDataKind :param key: The key whose associated object is to be returned :type key: str :param callback: A function that accepts the retrieved data and returns a transformed value :type callback: function :return: The result of executing callback """
[docs] @abstractmethod def all(self, kind, callback=lambda x: x): """ Retrieves a dictionary of all associated objects of a given kind. The retrieved dict of keys to objects can be transformed by the specified callback. :param kind: The kind of objects to get :type kind: VersionedDataKind :param callback: A function that accepts the retrieved data and returns a transformed value :type callback: function :rtype: The result of executing callback """
[docs] @abstractmethod def init(self, all_data): """ Initializes (or re-initializes) the store with the specified set of objects. Any existing entries will be removed. Implementations can assume that this set of objects is up to date-- there is no need to perform individual version comparisons between the existing objects and the supplied data. :param all_data: All objects to be stored :type all_data: dict[VersionedDataKind, dict[str, dict]] """
[docs] @abstractmethod def delete(self, kind, key, version): """ Deletes the object associated with the specified key, if it exists and its version is less than the specified version. The object should be replaced in the data store by a placeholder with the specified version and a "deleted" property of TErue. :param kind: The kind of object to delete :type kind: VersionedDataKind :param key: The key of the object to be deleted :type key: str :param version: The version for the delete operation :type version: int """
[docs] @abstractmethod def upsert(self, kind, item): """ Updates or inserts the object associated with the specified key. If an item with the same key already exists, it should update it only if the new item's version property is greater than the old one. :param kind: The kind of object to update :type kind: VersionedDataKind :param item: The object to update or insert :type feature: dict """
@abstractproperty def initialized(self): """ Returns whether the store has been initialized yet or not :rtype: bool """
[docs]class FeatureStoreCore(object): """ Interface for a simplified subset of the functionality of :class:`FeatureStore`, to be used in conjunction with :class:`ldclient.feature_store_helpers.CachingStoreWrapper`. This allows developers of custom ``FeatureStore`` implementations to avoid repeating logic that would commonly be needed in any such implementation, such as caching. Instead, they can implement only ``FeatureStoreCore`` and then create a ``CachingStoreWrapper``. """ __metaclass__ = ABCMeta
[docs] @abstractmethod def get_internal(self, kind, key): """ Returns the object to which the specified key is mapped, or None if no such item exists. The method should not attempt to filter out any items based on their deleted property, nor to cache any items. :param kind: The kind of object to get :type kind: VersionedDataKind :param key: The key of the object :type key: str :return: The object to which the specified key is mapped, or None :rtype: dict """
[docs] @abstractmethod def get_all_internal(self, callback): """ Returns a dictionary of all associated objects of a given kind. The method should not attempt to filter out any items based on their deleted property, nor to cache any items. :param kind: The kind of objects to get :type kind: VersionedDataKind :return: A dictionary of keys to items :rtype: dict[str, dict] """
[docs] @abstractmethod def init_internal(self, all_data): """ Initializes (or re-initializes) the store with the specified set of objects. Any existing entries will be removed. Implementations can assume that this set of objects is up to date-- there is no need to perform individual version comparisons between the existing objects and the supplied data. :param all_data: A dictionary of data kinds to item collections :type all_data: dict[VersionedDataKind, dict[str, dict]] """
[docs] @abstractmethod def upsert_internal(self, kind, item): """ Updates or inserts the object associated with the specified key. If an item with the same key already exists, it should update it only if the new item's version property is greater than the old one. It should return the final state of the item, i.e. if the update succeeded then it returns the item that was passed in, and if the update failed due to the version check then it returns the item that is currently in the data store (this ensures that `CachingStoreWrapper` will update the cache correctly). :param kind: The kind of object to update :type kind: VersionedDataKind :param item: The object to update or insert :type item: dict :return: The state of the object after the update :rtype: dict """
[docs] @abstractmethod def initialized_internal(self): """ Returns true if this store has been initialized. In a shared data store, it should be able to detect this even if initInternal was called in a different process, i.e. the test should be based on looking at what is in the data store. The method does not need to worry about caching this value; `CachingStoreWrapper` will only call it when necessary. :rtype: bool """
# Internal use only. Common methods for components that perform a task in the background.
[docs]class BackgroundOperation(object): # noinspection PyMethodMayBeStatic
[docs] def start(self): """ Starts an operation in the background. Should return immediately and not block. """ pass
[docs] def stop(self): """ Stops an operation running in the background. May return before the operation is actually stopped. """ pass
# noinspection PyMethodMayBeStatic
[docs] def is_alive(self): """ Returns whether the operation is alive or not :rtype: bool """ return True
[docs]class UpdateProcessor(BackgroundOperation): """ Interface for the component that obtains feature flag data in some way and passes it to a :class:`FeatureStore`. The built-in implementations of this are the client's standard streaming or polling behavior. For testing purposes, there is also :func:`ldclient.integrations.Files.new_data_source()`. """ __metaclass__ = ABCMeta
[docs] def initialized(self): """ Returns whether the update processor has received feature flags and has initialized its feature store. :rtype: bool """
[docs]class EventProcessor(object): """ Interface for the component that buffers analytics events and sends them to LaunchDarkly. The default implementation can be replaced for testing purposes. """ __metaclass__ = ABCMeta
[docs] @abstractmethod def send_event(self, event): """ Processes an event to be sent at some point. """
[docs] @abstractmethod def flush(self): """ Specifies that any buffered events should be sent as soon as possible, rather than waiting for the next flush interval. This method is asynchronous, so events still may not be sent until a later time. However, calling ``stop()`` will synchronously deliver any events that were not yet delivered prior to shutting down. """
[docs] @abstractmethod def stop(self): """ Shuts down the event processor after first delivering all pending events. """
[docs]class FeatureRequester(object): """ Interface for the component that acquires feature flag data in polling mode. The default implementation can be replaced for testing purposes. """ __metaclass__ = ABCMeta
[docs] def get_all(self): """ Gets all feature flags. """ pass
[docs]class DiagnosticDescription(object): """ Optional interface for components to describe their own configuration. """
[docs] @abstractmethod def describe_configuration(self, config): """ Used internally by the SDK to inspect the configuration. :param ldclient.config.Config config: the full configuration, in case this component depends on properties outside itself :return: a string describing the type of the component, or None :rtype: string """ pass