Skip to content

Commit a339b2a

Browse files
committed
ensure flag_evaluation variable is present, add flag_evaluation to afterhooks and update readme with async doco
Signed-off-by: leohoare <[email protected]>
1 parent cd20c12 commit a339b2a

File tree

2 files changed

+36
-6
lines changed

2 files changed

+36
-6
lines changed

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ print("Value: " + str(flag_value))
109109
|| [Eventing](#eventing) | React to state changes in the provider or flag management system. |
110110
|| [Shutdown](#shutdown) | Gracefully clean up a provider during application shutdown. |
111111
|| [Transaction Context Propagation](#transaction-context-propagation) | Set a specific [evaluation context](/docs/reference/concepts/evaluation-context) for a transaction (e.g. an HTTP request or a thread) |
112+
|| [Asynchronous Feature Retrieval](#asynchronous-feature-retrieval) | Evaluate flags in an asychronous context. |
112113
|| [Extending](#extending) | Extend OpenFeature with custom providers and hooks. |
113114

114115
<sub>Implemented: ✅ | In-progress: ⚠️ | Not implemented yet: ❌</sub>
@@ -316,6 +317,26 @@ async def some_endpoint():
316317
return create_response()
317318
```
318319

320+
### Asynchronous Feature Retrieval
321+
322+
The OpenFeature API supports asynchronous calls, enabling non-blocking feature evaluations for improved performance, especially useful in concurrent or latency-sensitive scenarios. If a provider *hasn't* implemented asynchronous calls, the client can still be used asynchronously, but calls will be blocking (synchronous).
323+
324+
```python
325+
import asyncio
326+
from openfeature import api
327+
from openfeature.provider.in_memory_provider import InMemoryFlag, InMemoryProvider
328+
329+
my_flags = { "v2_enabled": InMemoryFlag("on", {"on": True, "off": False}) }
330+
api.set_provider(InMemoryProvider(my_flags))
331+
client = api.get_client()
332+
flag_value = await client.get_boolean_value_async("v2_enabled", False) # API calls are suffixed by _async
333+
334+
print("Value: " + str(flag_value))
335+
```
336+
337+
See the [develop a provider](#develop-a-provider) for how to support asynchronous functionality in providers.
338+
339+
319340
### Shutdown
320341

321342
The OpenFeature API provides a shutdown function to perform a cleanup of all registered providers. This should only be called when your application is in the process of shutting down.

openfeature/client.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -556,12 +556,13 @@ async def evaluate_flag_details_async(
556556
hook_hints,
557557
)
558558
if error_code:
559-
return FlagEvaluationDetails(
559+
flag_evaluation = FlagEvaluationDetails(
560560
flag_key=flag_key,
561561
value=default_value,
562562
reason=Reason.ERROR,
563563
error_code=error_code,
564564
)
565+
return flag_evaluation
565566

566567
merged_context = self._before_hooks_and_merge_context(
567568
flag_type,
@@ -591,14 +592,14 @@ async def evaluate_flag_details_async(
591592

592593
except OpenFeatureError as err:
593594
error_hooks(flag_type, hook_context, err, reversed_merged_hooks, hook_hints)
594-
595-
return FlagEvaluationDetails(
595+
flag_evaluation = FlagEvaluationDetails(
596596
flag_key=flag_key,
597597
value=default_value,
598598
reason=Reason.ERROR,
599599
error_code=err.error_code,
600600
error_message=err.error_message,
601601
)
602+
return flag_evaluation
602603
# Catch any type of exception here since the user can provide any exception
603604
# in the error hooks
604605
except Exception as err: # pragma: no cover
@@ -609,16 +610,23 @@ async def evaluate_flag_details_async(
609610
error_hooks(flag_type, hook_context, err, reversed_merged_hooks, hook_hints)
610611

611612
error_message = getattr(err, "error_message", str(err))
612-
return FlagEvaluationDetails(
613+
flag_evaluation = FlagEvaluationDetails(
613614
flag_key=flag_key,
614615
value=default_value,
615616
reason=Reason.ERROR,
616617
error_code=ErrorCode.GENERAL,
617618
error_message=error_message,
618619
)
620+
return flag_evaluation
619621

620622
finally:
621-
after_all_hooks(flag_type, hook_context, reversed_merged_hooks, hook_hints)
623+
after_all_hooks(
624+
flag_type,
625+
hook_context,
626+
flag_evaluation,
627+
reversed_merged_hooks,
628+
hook_hints,
629+
)
622630

623631
def evaluate_flag_details(
624632
self,
@@ -657,12 +665,13 @@ def evaluate_flag_details(
657665
hook_hints,
658666
)
659667
if error_code:
660-
return FlagEvaluationDetails(
668+
flag_evaluation = FlagEvaluationDetails(
661669
flag_key=flag_key,
662670
value=default_value,
663671
reason=Reason.ERROR,
664672
error_code=error_code,
665673
)
674+
return flag_evaluation
666675

667676
merged_context = self._before_hooks_and_merge_context(
668677
flag_type,

0 commit comments

Comments
 (0)