@@ -83,7 +83,7 @@ from openfeature.provider.in_memory_provider import InMemoryFlag, InMemoryProvid
8383
8484# flags defined in memory
8585my_flags = {
86- " v2_enabled" : InMemoryFlag(" on" , {" on" : True , " off" : False })
86+ " v2_enabled" : InMemoryFlag(" on" , {" on" : True , " off" : False }),
8787}
8888
8989# configure a provider
@@ -161,7 +161,7 @@ global_context = EvaluationContext(
161161 targeting_key = " targeting_key1" , attributes = {" application" : " value1" }
162162)
163163request_context = EvaluationContext(
164- targeting_key = " targeting_key2" , attributes = {" email" : request.form[' email' ]}
164+ targeting_key = " targeting_key2" , attributes = {" email" : request.form[" email" ]}
165165)
166166
167167# # set global context
@@ -210,10 +210,10 @@ client = api.get_client()
210210
211211# trigger tracking event action
212212client.track(
213- ' visited-promo-page' ,
213+ " visited-promo-page" ,
214214 evaluation_context = EvaluationContext(),
215215 tracking_event_details = TrackingEventDetails(99.77 ).add(" currencyCode" , " USD" ),
216- )
216+ )
217217```
218218
219219Note that some providers may not support tracking; check the documentation for your provider for more information.
@@ -243,14 +243,13 @@ If a domain has no associated provider, the global provider is used.
243243from openfeature import api
244244
245245# Registering the default provider
246- api.set_provider(MyProvider());
246+ api.set_provider(MyProvider())
247247# Registering a provider to a domain
248- api.set_provider(MyProvider(), " my-domain" );
249-
248+ api.set_provider(MyProvider(), " my-domain" )
250249# A client bound to the default provider
251- default_client = api.get_client();
250+ default_client = api.get_client()
252251# A client bound to the MyProvider provider
253- domain_scoped_client = api.get_client(" my-domain" );
252+ domain_scoped_client = api.get_client(" my-domain" )
254253```
255254
256255Domains can be defined on a provider during registration.
@@ -266,16 +265,20 @@ Please refer to the documentation of the provider you're using to see what event
266265from openfeature import api
267266from openfeature.event import EventDetails, ProviderEvent
268267
268+
269269def on_provider_ready (event_details : EventDetails):
270270 print (f " Provider { event_details.provider_name} is ready " )
271271
272+
272273api.add_handler(ProviderEvent.PROVIDER_READY , on_provider_ready)
273274
274275client = api.get_client()
275276
277+
276278def on_provider_ready (event_details : EventDetails):
277279 print (f " Provider { event_details.provider_name} is ready " )
278280
281+
279282client.add_handler(ProviderEvent.PROVIDER_READY , on_provider_ready)
280283```
281284
@@ -301,26 +304,33 @@ app = Flask(__name__)
301304# Set the transaction context propagator
302305api.set_transaction_context_propagator(ContextVarsTransactionContextPropagator())
303306
307+
304308# Middleware to set the transaction context
305309# You can call api.set_transaction_context anywhere you have information,
306310# you want to have available in the code-paths below the current one.
307311@app.before_request
308312def set_request_transaction_context ():
309- ip = request.headers.get(" X-Forwarded-For" , request.remote_addr)
310- user_id = request.headers.get(" User-Id" ) # Assuming we're getting the user ID from a header
311- evaluation_context = EvaluationContext(targeting_key = user_id, attributes = {" ipAddress" : ip})
312- api.set_transaction_context(evaluation_context)
313+ ip = request.headers.get(" X-Forwarded-For" , request.remote_addr)
314+ user_id = request.headers.get(
315+ " User-Id"
316+ ) # Assuming we're getting the user ID from a header
317+ evaluation_context = EvaluationContext(
318+ targeting_key = user_id, attributes = {" ipAddress" : ip}
319+ )
320+ api.set_transaction_context(evaluation_context)
321+
313322
314323def create_response () -> str :
315- # This method can be anywhere in our code.
316- # The feature flag evaluation will automatically contain the transaction context merged with other context
317- new_response = api.get_client().get_string_value(" response-message" , " Hello User!" )
318- return f " Message from server: { new_response} "
324+ # This method can be anywhere in our code.
325+ # The feature flag evaluation will automatically contain the transaction context merged with other context
326+ new_response = api.get_client().get_string_value(" response-message" , " Hello User!" )
327+ return f " Message from server: { new_response} "
328+
319329
320330# Example route where we use the transaction context
321- @app.route (' /greeting' )
331+ @app.route (" /greeting" )
322332def some_endpoint ():
323- return create_response()
333+ return create_response()
324334```
325335
326336This also works for asyncio based implementations e.g. FastApi as seen in the following example:
@@ -337,24 +347,31 @@ app = FastAPI()
337347# Set the transaction context propagator
338348api.set_transaction_context_propagator(ContextVarsTransactionContextPropagator())
339349
350+
340351# Middleware to set the transaction context
341352@app.middleware (" http" )
342353async def set_request_transaction_context (request : Request, call_next ):
343354 ip = request.headers.get(" X-Forwarded-For" , request.client.host)
344- user_id = request.headers.get(" User-Id" ) # Assuming we're getting the user ID from a header
345- evaluation_context = EvaluationContext(targeting_key = user_id, attributes = {" ipAddress" : ip})
355+ user_id = request.headers.get(
356+ " User-Id"
357+ ) # Assuming we're getting the user ID from a header
358+ evaluation_context = EvaluationContext(
359+ targeting_key = user_id, attributes = {" ipAddress" : ip}
360+ )
346361 api.set_transaction_context(evaluation_context)
347362 response = await call_next(request)
348363 return response
349364
365+
350366def create_response () -> str :
351367 # This method can be located anywhere in our code.
352368 # The feature flag evaluation will automatically include the transaction context merged with other context.
353369 new_response = api.get_client().get_string_value(" response-message" , " Hello User!" )
354370 return f " Message from server: { new_response} "
355371
372+
356373# Example route where we use the transaction context
357- @app.get (' /greeting' )
374+ @app.get (" /greeting" )
358375async def some_endpoint ():
359376 return create_response()
360377```
@@ -368,10 +385,12 @@ import asyncio
368385from openfeature import api
369386from openfeature.provider.in_memory_provider import InMemoryFlag, InMemoryProvider
370387
371- my_flags = { " v2_enabled" : InMemoryFlag(" on" , {" on" : True , " off" : False }) }
388+ my_flags = {" v2_enabled" : InMemoryFlag(" on" , {" on" : True , " off" : False })}
372389api.set_provider(InMemoryProvider(my_flags))
373390client = api.get_client()
374- flag_value = await client.get_boolean_value_async(" v2_enabled" , False ) # API calls are suffixed by _async
391+ flag_value = await client.get_boolean_value_async(
392+ " v2_enabled" , False
393+ ) # API calls are suffixed by _async
375394
376395print (" Value: " + str (flag_value))
377396```
@@ -404,9 +423,9 @@ from openfeature.flag_evaluation import FlagResolutionDetails
404423from openfeature.hook import Hook
405424from openfeature.provider import AbstractProvider, Metadata
406425
426+
407427class MyProvider (AbstractProvider ):
408- def get_metadata (self ) -> Metadata:
409- ...
428+ def get_metadata (self ) -> Metadata: ...
410429
411430 def get_provider_hooks (self ) -> List[Hook]:
412431 return []
@@ -416,40 +435,35 @@ class MyProvider(AbstractProvider):
416435 flag_key : str ,
417436 default_value : bool ,
418437 evaluation_context : Optional[EvaluationContext] = None ,
419- ) -> FlagResolutionDetails[bool ]:
420- ...
438+ ) -> FlagResolutionDetails[bool ]: ...
421439
422440 def resolve_string_details (
423441 self ,
424442 flag_key : str ,
425443 default_value : str ,
426444 evaluation_context : Optional[EvaluationContext] = None ,
427- ) -> FlagResolutionDetails[str ]:
428- ...
445+ ) -> FlagResolutionDetails[str ]: ...
429446
430447 def resolve_integer_details (
431448 self ,
432449 flag_key : str ,
433450 default_value : int ,
434451 evaluation_context : Optional[EvaluationContext] = None ,
435- ) -> FlagResolutionDetails[int ]:
436- ...
452+ ) -> FlagResolutionDetails[int ]: ...
437453
438454 def resolve_float_details (
439455 self ,
440456 flag_key : str ,
441457 default_value : float ,
442458 evaluation_context : Optional[EvaluationContext] = None ,
443- ) -> FlagResolutionDetails[float ]:
444- ...
459+ ) -> FlagResolutionDetails[float ]: ...
445460
446461 def resolve_object_details (
447462 self ,
448463 flag_key : str ,
449464 default_value : Union[dict , list ],
450465 evaluation_context : Optional[EvaluationContext] = None ,
451- ) -> FlagResolutionDetails[Union[dict , list ]]:
452- ...
466+ ) -> FlagResolutionDetails[Union[dict , list ]]: ...
453467```
454468
455469Providers can also be extended to support async functionality.
@@ -461,46 +475,41 @@ To support add asynchronous calls to a provider:
461475``` python
462476class MyProvider (AbstractProvider ):
463477 ...
478+
464479 async def resolve_boolean_details_async (
465480 self ,
466481 flag_key : str ,
467482 default_value : bool ,
468483 evaluation_context : Optional[EvaluationContext] = None ,
469- ) -> FlagResolutionDetails[bool ]:
470- ...
484+ ) -> FlagResolutionDetails[bool ]: ...
471485
472486 async def resolve_string_details_async (
473487 self ,
474488 flag_key : str ,
475489 default_value : str ,
476490 evaluation_context : Optional[EvaluationContext] = None ,
477- ) -> FlagResolutionDetails[str ]:
478- ...
491+ ) -> FlagResolutionDetails[str ]: ...
479492
480493 async def resolve_integer_details_async (
481494 self ,
482495 flag_key : str ,
483496 default_value : int ,
484497 evaluation_context : Optional[EvaluationContext] = None ,
485- ) -> FlagResolutionDetails[int ]:
486- ...
498+ ) -> FlagResolutionDetails[int ]: ...
487499
488500 async def resolve_float_details_async (
489501 self ,
490502 flag_key : str ,
491503 default_value : float ,
492504 evaluation_context : Optional[EvaluationContext] = None ,
493- ) -> FlagResolutionDetails[float ]:
494- ...
505+ ) -> FlagResolutionDetails[float ]: ...
495506
496507 async def resolve_object_details_async (
497508 self ,
498509 flag_key : str ,
499510 default_value : Union[dict , list ],
500511 evaluation_context : Optional[EvaluationContext] = None ,
501- ) -> FlagResolutionDetails[Union[dict , list ]]:
502- ...
503-
512+ ) -> FlagResolutionDetails[Union[dict , list ]]: ...
504513```
505514
506515> Built a new provider? [ Let us know] ( https://github.com/open-feature/openfeature.dev/issues/new?assignees=&labels=provider&projects=&template=document-provider.yaml&title=%5BProvider%5D%3A+ ) so we can add it to the docs!
@@ -516,10 +525,15 @@ Any of the evaluation life-cycle stages (`before`/`after`/`error`/`finally_after
516525from openfeature.hook import Hook, HookContext, HookHints
517526from openfeature.flag_evaluation import FlagEvaluationDetails, FlagValueType
518527
528+
519529class MyHook (Hook ):
520- def after (self , hook_context : HookContext, details : FlagEvaluationDetails[FlagValueType], hints : HookHints):
530+ def after (
531+ self ,
532+ hook_context : HookContext,
533+ details : FlagEvaluationDetails[FlagValueType],
534+ hints : HookHints,
535+ ):
521536 print (" This runs after the flag has been evaluated" )
522-
523537```
524538
525539> Built a new hook? [ Let us know] ( https://github.com/open-feature/openfeature.dev/issues/new?assignees=&labels=hook&projects=&template=document-hook.yaml&title=%5BHook%5D%3A+ ) so we can add it to the docs!
0 commit comments