-[](https://badge.fury.io/py/openfeature-sdk)
-
-[](https://www.repostatus.org/#wip)
-[](https://github.com/open-feature/spec/tree/v0.3.0)
-[](https://github.com/open-feature/python-sdk/actions/workflows/merge.yml)
-[](https://codecov.io/gh/open-feature/python-sdk)
+
+
+
-> ⚠️ Development is in progress, but there's not a stable release available. ⚠️
+
+
+
-This is the Python implementation of [OpenFeature](https://openfeature.dev), a vendor-agnostic abstraction library for evaluating feature flags.
+
-We support multiple data types for flags (numbers, strings, booleans, objects) as well as hooks, which can alter the lifecycle of a flag evaluation.
+
+
+
-This library is intended to be used in server-side contexts and has not been evaluated for use in mobile devices.
+
+
+
+
+
-## 🔍 Requirements:
+
+
+
-- Python 3.8+
+
+
+
+
+
+
+
+
+
+
+[OpenFeature](https://openfeature.dev) is an open standard that provides a vendor-agnostic, community-driven API for feature flagging that works with your favorite feature flag management tool.
-## 📦 Installation:
+
+
+## 🚀 Quick start
+
+### Requirements
+
+- Python 3.8+
-### Add it to your build
+### Install
@@ -52,55 +75,53 @@ pip install requirements.txt
-## 🌟 Features:
+### Usage
-- support for various backend [providers](https://openfeature.dev/docs/reference/concepts/provider)
-- easy integration and extension via [hooks](https://openfeature.dev/docs/reference/concepts/hooks)
-- bool, string, numeric, and object flag types
-- [context-aware](https://openfeature.dev/docs/reference/concepts/evaluation-context) evaluation
+
-## 🚀 Usage:
+### API Reference
-### Configure it
+
-In order to use the sdk there is some minor configuration. Follow the script below:
+## 🌟 Features
-```python
-from openfeature import api
-from openfeature.provider.no_op_provider import NoOpProvider
+| Status | Features | Description |
+| ------ | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
+| ✅ | [Providers](#providers) | Integrate with a commercial, open source, or in-house feature management tool. |
+| ✅ | [Targeting](#targeting) | Contextually-aware flag evaluation using [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context). |
+| ✅ | [Hooks](#hooks) | Add functionality to various stages of the flag evaluation life-cycle. |
+| ❌ | [Logging](#logging) | Integrate with popular logging packages. |
+| ❌ | [Named clients](#named-clients) | Utilize multiple providers in a single application. |
+| ❌ | [Eventing](#eventing) | React to state changes in the provider or flag management system. |
+| ✅ | [Shutdown](#shutdown) | Gracefully clean up a provider during application shutdown. |
+| ✅ | [Extending](#extending) | Extend OpenFeature with custom providers and hooks. |
-api.set_provider(NoOpProvider())
-open_feature_client = api.get_client()
-```
+Implemented: ✅ | In-progress: ⚠️ | Not implemented yet: ❌
-### Basics:
+### Providers
-While Boolean provides the simplest introduction, we offer a variety of flag types.
+[Providers](https://openfeature.dev/docs/reference/concepts/provider) are an abstraction between a flag management system and the OpenFeature SDK.
+Look [here](https://openfeature.dev/ecosystem?instant_search%5BrefinementList%5D%5Btype%5D%5B0%5D=Provider&instant_search%5BrefinementList%5D%5Btechnology%5D%5B0%5D=python) for a complete list of available providers.
+If the provider you're looking for hasn't been created yet, see the [develop a provider](#develop-a-provider) section to learn how to build it yourself.
-```python
-# Depending on the flag type, use one of the methods below
-flag_key = "PROVIDER_FLAG"
-boolean_result = open_feature_client.get_boolean_value(key=flag_key,default_value=False)
-integer_result = open_feature_client.get_integer_value(key=flag_key,default_value=-1)
-float_result = open_feature_client.get_float_value(key=flag_key,default_value=-1)
-string_result = open_feature_client.get_string_value(key=flag_key,default_value="")
-object_result = open_feature_client.get_object_value(key=flag_key,default_value={})
-```
-
-You can also bind a provider to a specific client by name instead of setting that provider globally:
+Once you've added a provider as a dependency, it can be registered with OpenFeature like this:
```python
+from openfeature import api
+from openfeature.provider.no_op_provider import NoOpProvider
api.set_provider(NoOpProvider())
+open_feature_client = api.get_client()
```
-Each provider class may have further setup required i.e. secret keys, environment variables etc
+In some situations, it may be beneficial to register multiple providers in the same application.
+This is possible using [named clients](#named-clients), which is covered in more detail below.
-### Context-aware evaluation:
+### Targeting
-Sometimes the value of a flag must take into account some dynamic criteria about the application or user, such as the user location, IP, email address, or the location of the server.
-In OpenFeature, we refer to this as [`targeting`](https://openfeature.dev/specification/glossary#targeting).
-If the flag system you're using supports targeting, you can provide the input data using the `EvaluationContext`.
+Sometimes, the value of a flag must consider some dynamic criteria about the application or user, such as the user's location, IP, email address, or the server's location.
+In OpenFeature, we refer to this as [targeting](https://openfeature.dev/specification/glossary#targeting).
+If the flag management system you're using supports targeting, you can provide the input data using the [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context).
```python
from openfeature.api import (
@@ -127,19 +148,61 @@ client.get_string_value("email", None, request_context)
```
-### Events
+### Hooks
+
+[Hooks](https://openfeature.dev/docs/reference/concepts/hooks) allow for custom logic to be added at well-defined points of the flag evaluation life-cycle.
+Look [here](https://openfeature.dev/ecosystem/?instant_search%5BrefinementList%5D%5Btype%5D%5B0%5D=Hook&instant_search%5BrefinementList%5D%5Btechnology%5D%5B0%5D=python) for a complete list of available hooks.
+If the hook you're looking for hasn't been created yet, see the [develop a hook](#develop-a-hook) section to learn how to build it yourself.
-TBD (See Issue [#131](https://github.com/open-feature/python-sdk/issues/131))
+Once you've added a hook as a dependency, it can be registered at the global, client, or flag invocation level.
-### Providers:
+```python
+# set global hooks at the API-level
+from openfeature.api import add_hooks
+add_hooks([MyHook()])
-To develop a provider, you need to create a new project and include the OpenFeature SDK as a dependency. This can be a new repository or included in [the existing contrib repository](https://github.com/open-feature/python-sdk-contrib) available under the OpenFeature organization. Finally, you’ll then need to write the provider itself. This can be accomplished by implementing the `Provider` interface exported by the OpenFeature SDK.
+# or configure them in the client
+client = OpenFeatureClient()
+client.add_hooks([MyHook()])
+```
-See [here](https://openfeature.dev/ecosystem) for a catalog of available providers.
+
-### Hooks:
+### Logging
-A hook is a mechanism that allows for adding arbitrary behavior at well-defined points of the flag evaluation life-cycle. Use cases include validating the resolved flag value, modifying or adding data to the evaluation context, logging, telemetry, and tracking.
+Logging customization is not yet available in the Python SDK.
+
+### Named clients
+
+Named clients are not yet available in the Python SDK. Progress on this feature can be tracked [here](https://github.com/open-feature/python-sdk/issues/125).
+
+### Eventing
+
+Events are not yet available in the Python SDK. Progress on this feature can be tracked [here](https://github.com/open-feature/python-sdk/issues/125).
+
+### Shutdown
+
+A shutdown method is not yet available in the Python SDK. Progress on this feature can be tracked [here](https://github.com/open-feature/python-sdk/issues/125).
+
+## Extending
+
+### Develop a provider
+
+To develop a provider, you need to create a new project and include the OpenFeature SDK as a dependency.
+This can be a new repository or included in [the existing contrib repository](https://github.com/open-feature/python-sdk-contrib) available under the OpenFeature organization.
+You’ll then need to write the provider by implementing the `FeatureProvider` interface exported by the OpenFeature SDK.
+
+
+
+> 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!
+
+### Develop a hook
+
+To develop a hook, you need to create a new project and include the OpenFeature SDK as a dependency.
+This can be a new repository or included in [the existing contrib repository](https://github.com/open-feature/python-sdk-contrib) available under the OpenFeature organization.
+Implement your own hook by conforming to the `Hook interface`.
+To satisfy the interface, all methods (`Before`/`After`/`Finally`/`Error`) need to be defined.
+To avoid defining empty functions, make use of the `UnimplementedHook` struct (which already implements all the empty functions).
```python
from openfeature.hook import Hook
@@ -148,21 +211,11 @@ class MyHook(Hook):
def after(self, hook_context: HookContext, details: FlagEvaluationDetails, hints: dict):
print("This runs after the flag has been evaluated")
-
-# set global hooks at the API-level
-from openfeature.api import add_hooks
-add_hooks([MyHook()])
-
-# or configure them in the client
-client = OpenFeatureClient()
-client.add_hooks([MyHook()])
```
-See [here](https://openfeature.dev/ecosystem) for a catalog of available hooks.
+> 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!
-### Logging:
-
-TBD
+
## ⭐️ Support the project
@@ -171,31 +224,18 @@ TBD
- Twitter: [@openfeature](https://twitter.com/openfeature)
- LinkedIn: [OpenFeature](https://www.linkedin.com/company/openfeature/)
- Join us on [Slack](https://cloud-native.slack.com/archives/C0344AANLA1)
-- For more check out our [community page](https://openfeature.dev/community/)
+- For more, check out our [community page](https://openfeature.dev/community/)
## 🤝 Contributing
Interested in contributing? Great, we'd love your help! To get started, take a look at the [CONTRIBUTING](CONTRIBUTING.md) guide.
-### Thanks to everyone that has already contributed
+### Thanks to everyone who has already contributed
-
Made with [contrib.rocks](https://contrib.rocks).
-## Contacting us
-
-We hold regular meetings which you can see [here](https://github.com/open-feature/community/#meetings-and-events).
-
-We are also present on the `#openfeature` channel in the [CNCF slack](https://slack.cncf.io/).
-
-## 📜 License
-
-[Apache License 2.0](LICENSE)
-
-
-
-[openfeature-website]: https://openfeature.dev
+
From 87e91b1919fddfd42784817db1b305e2f7c5f45b Mon Sep 17 00:00:00 2001
From: Todd Baert
Date: Thu, 26 Oct 2023 13:14:39 -0400
Subject: [PATCH 02/10] fixup: mv readme
Signed-off-by: Todd Baert
---
readme.md => README.md | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename readme.md => README.md (100%)
diff --git a/readme.md b/README.md
similarity index 100%
rename from readme.md
rename to README.md
From ca14a1c389a79296e73b1b0fa0481dda668d2ece Mon Sep 17 00:00:00 2001
From: Todd Baert
Date: Thu, 26 Oct 2023 14:34:04 -0400
Subject: [PATCH 03/10] fixup: badge alts
Signed-off-by: Todd Baert
---
README.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index f7c6ebbb..62994e8a 100644
--- a/README.md
+++ b/README.md
@@ -20,13 +20,13 @@
-
+
-
+
@@ -34,11 +34,11 @@
-
+
-
+
-[OpenFeature](https://openfeature.dev) is an open standard that provides a vendor-agnostic, community-driven API for feature flagging that works with your favorite feature flag management tool.
+[OpenFeature](https://openfeature.dev) is an open specification that provides a vendor-agnostic, community-driven API for feature flagging that works with your favorite feature flag management tool.
@@ -77,11 +77,25 @@ pip install -r requirements.txt
### Usage
-
+```python
+from openfeature import api
+from openfeature.provider.in_memory_provider import InMemoryFlag, InMemoryProvider
-### API Reference
+# flags defined in memory
+my_flags = {
+ "v2_enabled": InMemoryFlag("on", {"on": True, "off": False})
+}
-
+# configure a provider
+api.set_provider(InMemoryProvider(my_flags))
+
+# create a client
+client = api.get_client()
+
+# get a bool flag value
+flag_value = client.get_boolean_value("v2_enabled", False)
+print("Value: " + str(flag_value))
+```
## 🌟 Features
@@ -114,8 +128,8 @@ api.set_provider(NoOpProvider())
open_feature_client = api.get_client()
```
-In some situations, it may be beneficial to register multiple providers in the same application.
-This is possible using [named clients](#named-clients), which is covered in more detail below.
+
### Targeting
@@ -127,7 +141,7 @@ If the flag management system you're using supports targeting, you can provide t
from openfeature.api import (
get_client,
get_provider,
- set_provider
+ set_provider,
get_evaluation_context,
set_evaluation_context,
)
@@ -143,9 +157,8 @@ request_context = EvaluationContext(
set_evaluation_context(global_context)
# merge second context
-client = get_client(name="No-op Provider", version="0.5.2")
-client.get_string_value("email", None, request_context)
-
+client = get_client(name="No-op Provider")
+client.get_string_value("email", "fallback", request_context)
```
### Hooks
@@ -157,16 +170,20 @@ If the hook you're looking for hasn't been created yet, see the [develop a hook]
Once you've added a hook as a dependency, it can be registered at the global, client, or flag invocation level.
```python
-# set global hooks at the API-level
from openfeature.api import add_hooks
+from openfeature.flag_evaluation import FlagEvaluationOptions
+
+# set global hooks at the API-level
add_hooks([MyHook()])
# or configure them in the client
client = OpenFeatureClient()
client.add_hooks([MyHook()])
-```
-
+# or at the invocation-level
+options = FlagEvaluationOptions(hooks=[MyHook()])
+client.get_boolean_flag("my-flag", False, flag_evaluation_options=options)
+```
### Logging
@@ -190,9 +207,62 @@ A shutdown method is not yet available in the Python SDK. Progress on this featu
To develop a provider, you need to create a new project and include the OpenFeature SDK as a dependency.
This can be a new repository or included in [the existing contrib repository](https://github.com/open-feature/python-sdk-contrib) available under the OpenFeature organization.
-You’ll then need to write the provider by implementing the `FeatureProvider` interface exported by the OpenFeature SDK.
+You’ll then need to write the provider by implementing the `AbstractProvider` class exported by the OpenFeature SDK.
-
+```python
+from typing import List, Optional
+
+from openfeature.evaluation_context import EvaluationContext
+from openfeature.flag_evaluation import FlagResolutionDetails
+from openfeature.provider.provider import AbstractProvider
+
+class MyProvider(AbstractProvider):
+ def get_metadata(self) -> Metadata:
+ ...
+
+ def get_provider_hooks(self) -> List[Hook]:
+ return []
+
+ def resolve_boolean_details(
+ self,
+ flag_key: str,
+ default_value: bool,
+ evaluation_context: Optional[EvaluationContext] = None,
+ ) -> FlagResolutionDetails[bool]:
+ ...
+
+ def resolve_string_details(
+ self,
+ flag_key: str,
+ default_value: str,
+ evaluation_context: Optional[EvaluationContext] = None,
+ ) -> FlagResolutionDetails[str]:
+ ...
+
+ def resolve_integer_details(
+ self,
+ flag_key: str,
+ default_value: int,
+ evaluation_context: Optional[EvaluationContext] = None,
+ ) -> FlagResolutionDetails[int]:
+ ...
+
+ def resolve_float_details(
+ self,
+ flag_key: str,
+ default_value: float,
+ evaluation_context: Optional[EvaluationContext] = None,
+ ) -> FlagResolutionDetails[float]:
+ ...
+
+ def resolve_object_details(
+ self,
+ flag_key: str,
+ default_value: Union[dict, list],
+ evaluation_context: Optional[EvaluationContext] = None,
+ ) -> FlagResolutionDetails[Union[dict, list]]:
+ ...
+```
> 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!
@@ -200,9 +270,8 @@ You’ll then need to write the provider by implementing the `FeatureProvider` i
To develop a hook, you need to create a new project and include the OpenFeature SDK as a dependency.
This can be a new repository or included in [the existing contrib repository](https://github.com/open-feature/python-sdk-contrib) available under the OpenFeature organization.
-Implement your own hook by conforming to the `Hook interface`.
-To satisfy the interface, all methods (`Before`/`After`/`Finally`/`Error`) need to be defined.
-To avoid defining empty functions, make use of the `UnimplementedHook` struct (which already implements all the empty functions).
+Implement your own hook by creating a hook that inherits from the `Hook` class.
+Any of the evaluation life-cycle stages (`before`/`after`/`error`/`finally_after`) can be override to add the desired business logic.
```python
from openfeature.hook import Hook