88import hashlib
99import logging
1010from abc import ABC
11- from typing import List , Optional , Dict , Tuple , Any , Mapping , Callable
11+ from typing import List , Optional , Dict , Tuple , Any , Mapping , Callable , cast
1212from ._models import FeatureFlag , Variant , VariantAssignmentReason , TargetingContext , EvaluationEvent , VariantReference
1313
1414FEATURE_MANAGEMENT_KEY = "feature_management"
2525logger = logging .getLogger (__name__ )
2626
2727
28- def _get_feature_flag (configuration : Mapping [str , Any ], feature_flag_name : str ) -> Optional [FeatureFlag ]:
29- """
30- Gets the FeatureFlag json from the configuration, if it exists it gets converted to a FeatureFlag object.
31-
32- :param Mapping configuration: Configuration object.
33- :param str feature_flag_name: Name of the feature flag.
34- :return: FeatureFlag
35- :rtype: FeatureFlag
36- """
37- feature_management = configuration .get (FEATURE_MANAGEMENT_KEY )
38- if not feature_management or not isinstance (feature_management , Mapping ):
39- return None
40- feature_flags = feature_management .get (FEATURE_FLAG_KEY )
41- if not feature_flags or not isinstance (feature_flags , list ):
42- return None
43-
44- for feature_flag in feature_flags :
45- if feature_flag .get ("id" ) == feature_flag_name :
46- return FeatureFlag .convert_from_json (feature_flag )
47-
48- return None
49-
50-
51- def _list_feature_flag_names (configuration : Mapping [str , Any ]) -> List [str ]:
52- """
53- List of all feature flag names.
54-
55- :param Mapping configuration: Configuration object.
56- :return: List of feature flag names.
57- """
58- feature_flag_names = []
59- feature_management = configuration .get (FEATURE_MANAGEMENT_KEY )
60- if not feature_management or not isinstance (feature_management , Mapping ):
61- return []
62- feature_flags = feature_management .get (FEATURE_FLAG_KEY )
63- if not feature_flags or not isinstance (feature_flags , list ):
64- return []
65-
66- for feature_flag in feature_flags :
67- feature_flag_names .append (feature_flag .get ("id" ))
68-
69- return feature_flag_names
70-
71-
7228class FeatureManagerBase (ABC ):
7329 """
7430 Base class for Feature Manager. This class is responsible for all shared logic between the sync and async.
@@ -273,7 +229,7 @@ def _check_feature_base(self, feature_flag_id: str) -> Tuple[EvaluationEvent, bo
273229 self ._copy = self ._configuration .get (FEATURE_MANAGEMENT_KEY )
274230
275231 if not self ._cache .get (feature_flag_id ):
276- feature_flag = _get_feature_flag ( self ._configuration , feature_flag_id )
232+ feature_flag = self ._get_feature_flag ( feature_flag_id )
277233 self ._cache [feature_flag_id ] = feature_flag
278234 else :
279235 feature_flag = self ._cache .get (feature_flag_id )
@@ -300,4 +256,41 @@ def list_feature_flag_names(self) -> List[str]:
300256 """
301257 List of all feature flag names.
302258 """
303- return _list_feature_flag_names (self ._configuration )
259+ feature_flag_names : Dict [str , None ] = {}
260+ for feature_flag in self ._get_feature_flags ():
261+ feature_flag_name = feature_flag .get ("id" )
262+ # Only include entries with a valid string id; duplicates are listed once.
263+ if isinstance (feature_flag_name , str ):
264+ feature_flag_names [feature_flag_name ] = None
265+
266+ return list (feature_flag_names )
267+
268+ def _get_feature_flag (self , feature_flag_name : str ) -> Optional [FeatureFlag ]:
269+ """
270+ Gets the FeatureFlag json from the configuration, if it exists it gets converted to a FeatureFlag object.
271+
272+ :param str feature_flag_name: Name of the feature flag.
273+ :return: FeatureFlag
274+ :rtype: FeatureFlag
275+ """
276+ for feature_flag in reversed (self ._get_feature_flags ()):
277+ # If multiple feature flags share the same id, the last one defined wins.
278+ if feature_flag .get ("id" ) == feature_flag_name :
279+ return FeatureFlag .convert_from_json (feature_flag )
280+
281+ return None
282+
283+ def _get_feature_flags (self ) -> List [Any ]:
284+ """
285+ Gets the list of raw feature flag definitions from the configuration.
286+
287+ :return: List of feature flag definitions, or an empty list if none are configured.
288+ :rtype: list
289+ """
290+ feature_management = self ._configuration .get (FEATURE_MANAGEMENT_KEY )
291+ if not feature_management or not isinstance (feature_management , Mapping ):
292+ return []
293+ feature_flags = feature_management .get (FEATURE_FLAG_KEY )
294+ if not feature_flags or not isinstance (feature_flags , list ):
295+ return []
296+ return cast (List [Any ], feature_flags )
0 commit comments