11from __future__ import annotations
22
33import logging
4- from typing import Any , Callable , Dict , List , Optional , TypeVar , Union , cast
4+ from typing import TYPE_CHECKING , Any , Callable , List , cast
55
6- from typing_extensions import ParamSpec
7-
8- from aws_lambda_powertools .logging import Logger
96from aws_lambda_powertools .utilities .feature_flags import schema
10- from aws_lambda_powertools .utilities .feature_flags .base import StoreProvider
117from aws_lambda_powertools .utilities .feature_flags .comparators import (
128 compare_all_in_list ,
139 compare_any_in_list ,
1814 compare_time_range ,
1915)
2016from aws_lambda_powertools .utilities .feature_flags .exceptions import ConfigurationStoreError
21- from aws_lambda_powertools .utilities .feature_flags .types import JSONType
17+ from aws_lambda_powertools .utilities .feature_flags .types import P , T
18+
19+ if TYPE_CHECKING :
20+ from aws_lambda_powertools .logging import Logger
21+ from aws_lambda_powertools .utilities .feature_flags .base import StoreProvider
22+ from aws_lambda_powertools .utilities .feature_flags .types import JSONType
2223
23- T = TypeVar ("T" )
24- P = ParamSpec ("P" )
2524
2625RULE_ACTION_MAPPING = {
2726 schema .RuleAction .EQUALS .value : lambda a , b : a == b ,
4948
5049
5150class FeatureFlags :
52- def __init__ (self , store : StoreProvider , logger : Optional [ Union [ logging .Logger , Logger ]] = None ):
51+ def __init__ (self , store : StoreProvider , logger : logging .Logger | Logger | None = None ):
5352 """Evaluates whether feature flags should be enabled based on a given context.
5453
5554 It uses the provided store to fetch feature flag rules before evaluating them.
@@ -100,12 +99,12 @@ def _evaluate_conditions(
10099 self ,
101100 rule_name : str ,
102101 feature_name : str ,
103- rule : Dict [str , Any ],
104- context : Dict [str , Any ],
102+ rule : dict [str , Any ],
103+ context : dict [str , Any ],
105104 ) -> bool :
106105 """Evaluates whether context matches conditions, return False otherwise"""
107106 rule_match_value = rule .get (schema .RULE_MATCH_VALUE )
108- conditions = cast (List [Dict ], rule .get (schema .CONDITIONS_KEY ))
107+ conditions = cast (List [dict ], rule .get (schema .CONDITIONS_KEY ))
109108
110109 if not conditions :
111110 self .logger .debug (
@@ -141,9 +140,9 @@ def _evaluate_rules(
141140 self ,
142141 * ,
143142 feature_name : str ,
144- context : Dict [str , Any ],
143+ context : dict [str , Any ],
145144 feat_default : Any ,
146- rules : Dict [str , Any ],
145+ rules : dict [str , Any ],
147146 boolean_feature : bool ,
148147 ) -> bool :
149148 """Evaluates whether context matches rules and conditions, otherwise return feature default"""
@@ -164,7 +163,7 @@ def _evaluate_rules(
164163 )
165164 return feat_default
166165
167- def get_configuration (self ) -> Dict :
166+ def get_configuration (self ) -> dict :
168167 """Get validated feature flag schema from configured store.
169168
170169 Largely used to aid testing, since it's called by `evaluate` and `get_enabled_features` methods.
@@ -178,7 +177,7 @@ def get_configuration(self) -> Dict:
178177
179178 Returns
180179 ------
181- Dict [str, Dict ]
180+ dict [str, dict ]
182181 parsed JSON dictionary
183182
184183 **Example**
@@ -208,13 +207,13 @@ def get_configuration(self) -> Dict:
208207 """
209208 # parse result conf as JSON, keep in cache for max age defined in store
210209 self .logger .debug (f"Fetching schema from registered store, store={ self .store } " )
211- config : Dict = self .store .get_configuration ()
210+ config : dict = self .store .get_configuration ()
212211 validator = schema .SchemaValidator (schema = config , logger = self .logger )
213212 validator .validate ()
214213
215214 return config
216215
217- def evaluate (self , * , name : str , context : Optional [ Dict [ str , Any ]] = None , default : JSONType ) -> JSONType :
216+ def evaluate (self , * , name : str , context : dict [ str , Any ] | None = None , default : JSONType ) -> JSONType :
218217 """Evaluate whether a feature flag should be enabled according to stored schema and input context
219218
220219 **Logic when evaluating a feature flag**
@@ -243,7 +242,7 @@ def evaluate(self, *, name: str, context: Optional[Dict[str, Any]] = None, defau
243242 ----------
244243 name: str
245244 feature name to evaluate
246- context: Optional[Dict[ str, Any]]
245+ context: dict[ str, Any] | None
247246 Attributes that should be evaluated against the stored schema.
248247
249248 for example: `{"tenant_id": "X", "username": "Y", "region": "Z"}`
@@ -306,7 +305,7 @@ def lambda_handler(event: dict, context: LambdaContext):
306305 # Maintenance: Revisit before going GA. We might to simplify customers on-boarding by not requiring it
307306 # for non-boolean flags. It'll need minor implementation changes, docs changes, and maybe refactor
308307 # get_enabled_features. We can minimize breaking change, despite Beta label, by having a new
309- # method `get_matching_features` returning Dict [feature_name, feature_value]
308+ # method `get_matching_features` returning dict [feature_name, feature_value]
310309 boolean_feature = feature .get (
311310 schema .FEATURE_DEFAULT_VAL_TYPE_KEY ,
312311 True ,
@@ -330,19 +329,19 @@ def lambda_handler(event: dict, context: LambdaContext):
330329 boolean_feature = boolean_feature ,
331330 )
332331
333- def get_enabled_features (self , * , context : Optional [ Dict [ str , Any ]] = None ) -> List [str ]:
332+ def get_enabled_features (self , * , context : dict [ str , Any ] | None = None ) -> list [str ]:
334333 """Get all enabled feature flags while also taking into account context
335334 (when a feature has defined rules)
336335
337336 Parameters
338337 ----------
339- context: Optional[Dict[ str, Any]]
338+ context: dict[ str, Any] | None
340339 dict of attributes that you would like to match the rules
341340 against, can be `{'tenant_id: 'X', 'username':' 'Y', 'region': 'Z'}` etc.
342341
343342 Returns
344343 ----------
345- List [str]
344+ list [str]
346345 list of all feature names that either matches context or have True as default
347346
348347 **Example**
@@ -359,10 +358,10 @@ def get_enabled_features(self, *, context: Optional[Dict[str, Any]] = None) -> L
359358 if context is None :
360359 context = {}
361360
362- features_enabled : List [str ] = []
361+ features_enabled : list [str ] = []
363362
364363 try :
365- features : Dict [str , Any ] = self .get_configuration ()
364+ features : dict [str , Any ] = self .get_configuration ()
366365 except ConfigurationStoreError as err :
367366 self .logger .debug (f"Failed to fetch feature flags from store, returning empty list, reason={ err } " )
368367 return features_enabled
0 commit comments