chore: update readme based on latest template (#227)
Signed-off-by: Todd Baert <todd.baert@dynatrace.com> Co-authored-by: Michael Beemer <beeme1mr@users.noreply.github.com> Co-authored-by: Federico Bond <federicobond@gmail.com>
This commit is contained in:
parent
0c314ab77c
commit
1864a3fa57
|
|
@ -0,0 +1,310 @@
|
|||
<!-- markdownlint-disable MD033 -->
|
||||
<!-- x-hide-in-docs-start -->
|
||||
<p align="center">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/open-feature/community/0e23508c163a6a1ac8c0ced3e4bd78faafe627c7/assets/logo/horizontal/white/openfeature-horizontal-white.svg" />
|
||||
<img align="center" alt="OpenFeature Logo" src="https://raw.githubusercontent.com/open-feature/community/0e23508c163a6a1ac8c0ced3e4bd78faafe627c7/assets/logo/horizontal/black/openfeature-horizontal-black.svg" />
|
||||
</picture>
|
||||
</p>
|
||||
|
||||
<h2 align="center">OpenFeature Python SDK</h2>
|
||||
|
||||
<!-- x-hide-in-docs-end -->
|
||||
<!-- The 'github-badges' class is used in the docs -->
|
||||
<p align="center" class="github-badges">
|
||||
|
||||
<a href="https://github.com/open-feature/spec/releases/tag/v0.3.0">
|
||||
<img alt="Specification" src="https://img.shields.io/static/v1?label=Specification&message=v0.3.0&color=red&style=for-the-badge" />
|
||||
</a>
|
||||
|
||||
<!-- x-release-please-start-version -->
|
||||
|
||||
<a href="https://github.com/open-feature/python-sdk/releases/tag/v0.3.1">
|
||||
<img alt="Latest version" src="https://img.shields.io/static/v1?label=release&message=v0.3.1&color=blue&style=for-the-badge" />
|
||||
</a>
|
||||
|
||||
<!-- x-release-please-end -->
|
||||
<br/>
|
||||
<a href="https://github.com/open-feature/python-sdk/actions/workflows/merge.yml">
|
||||
<img alt="Build status" src="https://github.com/open-feature/python-sdk/actions/workflows/build.yml/badge.svg" />
|
||||
</a>
|
||||
|
||||
<a href="https://codecov.io/gh/open-feature/python-sdk">
|
||||
<img alt="Codecov" src="https://codecov.io/gh/open-feature/python-sdk/branch/main/graph/badge.svg?token=FQ1I444HB3" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.python.org/downloads/">
|
||||
<img alt="Min python version" src="https://img.shields.io/badge/python->=3.8-blue.svg" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.repostatus.org/#wip">
|
||||
<img alt="Repo status" src="https://www.repostatus.org/badges/latest/wip.svg" />
|
||||
</a>
|
||||
</p>
|
||||
<!-- x-hide-in-docs-start -->
|
||||
|
||||
[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.
|
||||
|
||||
<!-- x-hide-in-docs-end -->
|
||||
|
||||
## 🚀 Quick start
|
||||
|
||||
### Requirements
|
||||
|
||||
- Python 3.8+
|
||||
|
||||
### Install
|
||||
|
||||
<!---x-release-please-start-version-->
|
||||
|
||||
#### Pip install
|
||||
|
||||
```bash
|
||||
pip install openfeature-sdk==0.3.1
|
||||
```
|
||||
|
||||
#### requirements.txt
|
||||
|
||||
```bash
|
||||
openfeature-sdk==0.3.1
|
||||
```
|
||||
|
||||
```python
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
<!---x-release-please-end-->
|
||||
|
||||
### Usage
|
||||
|
||||
```python
|
||||
from openfeature import api
|
||||
from openfeature.provider.in_memory_provider import InMemoryFlag, InMemoryProvider
|
||||
|
||||
# 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
|
||||
|
||||
| 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. |
|
||||
|
||||
<sub>Implemented: ✅ | In-progress: ⚠️ | Not implemented yet: ❌</sub>
|
||||
|
||||
### Providers
|
||||
|
||||
[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.
|
||||
|
||||
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()
|
||||
```
|
||||
|
||||
<!-- 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
|
||||
|
||||
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 (
|
||||
get_client,
|
||||
get_provider,
|
||||
set_provider,
|
||||
get_evaluation_context,
|
||||
set_evaluation_context,
|
||||
)
|
||||
|
||||
global_context = EvaluationContext(
|
||||
targeting_key="targeting_key1", attributes={"application": "value1"}
|
||||
)
|
||||
request_context = EvaluationContext(
|
||||
targeting_key="targeting_key2", attributes={"email": request.form['email']}
|
||||
)
|
||||
|
||||
## set global context
|
||||
set_evaluation_context(global_context)
|
||||
|
||||
# merge second context
|
||||
client = get_client(name="No-op Provider")
|
||||
client.get_string_value("email", "fallback", request_context)
|
||||
```
|
||||
|
||||
### 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.
|
||||
|
||||
Once you've added a hook as a dependency, it can be registered at the global, client, or flag invocation level.
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
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 `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!
|
||||
|
||||
### 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 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
|
||||
|
||||
class MyHook(Hook):
|
||||
def after(self, hook_context: HookContext, details: FlagEvaluationDetails, hints: dict):
|
||||
print("This runs after the flag has been evaluated")
|
||||
|
||||
```
|
||||
|
||||
> 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!
|
||||
|
||||
<!-- x-hide-in-docs-start -->
|
||||
|
||||
## ⭐️ Support the project
|
||||
|
||||
- Give this repo a ⭐️!
|
||||
- Follow us on social media:
|
||||
- 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/)
|
||||
|
||||
## 🤝 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 who has already contributed
|
||||
|
||||
<a href="https://github.com/open-feature/python-sdk/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=open-feature/python-sdk" alt="Pictures of the folks who have contributed to the project" />
|
||||
</a>
|
||||
|
||||
Made with [contrib.rocks](https://contrib.rocks).
|
||||
|
||||
<!-- x-hide-in-docs-end -->
|
||||
201
readme.md
201
readme.md
|
|
@ -1,201 +0,0 @@
|
|||
<!-- markdownlint-disable MD033 -->
|
||||
<p align="center">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/open-feature/community/0e23508c163a6a1ac8c0ced3e4bd78faafe627c7/assets/logo/horizontal/white/openfeature-horizontal-white.svg">
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/open-feature/community/0e23508c163a6a1ac8c0ced3e4bd78faafe627c7/assets/logo/horizontal/black/openfeature-horizontal-black.svg">
|
||||
<img align="center" alt="OpenFeature Logo">
|
||||
</picture>
|
||||
</p>
|
||||
|
||||
<h2 align="center">OpenFeature Python SDK</h2>
|
||||
|
||||
[](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+
|
||||
|
||||
## 📦 Installation:
|
||||
|
||||
### Add it to your build
|
||||
|
||||
<!---x-release-please-start-version-->
|
||||
|
||||
Pip install
|
||||
|
||||
```bash
|
||||
pip install openfeature-sdk==0.3.1
|
||||
```
|
||||
|
||||
requirements.txt
|
||||
|
||||
```bash
|
||||
openfeature-sdk==0.3.1
|
||||
```
|
||||
|
||||
```python
|
||||
pip install requirements.txt
|
||||
```
|
||||
|
||||
<!---x-release-please-end-->
|
||||
|
||||
## 🌟 Features:
|
||||
|
||||
- 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:
|
||||
|
||||
### Configure it
|
||||
|
||||
In order to use the sdk there is some minor configuration. Follow the script below:
|
||||
|
||||
```python
|
||||
from openfeature import api
|
||||
from openfeature.provider.no_op_provider import NoOpProvider
|
||||
|
||||
api.set_provider(NoOpProvider())
|
||||
open_feature_client = api.get_client()
|
||||
```
|
||||
|
||||
### Basics:
|
||||
|
||||
While Boolean provides the simplest introduction, we offer a variety of flag types.
|
||||
|
||||
```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:
|
||||
|
||||
```python
|
||||
|
||||
api.set_provider(NoOpProvider())
|
||||
```
|
||||
|
||||
Each provider class may have further setup required i.e. secret keys, environment variables etc
|
||||
|
||||
### Context-aware evaluation:
|
||||
|
||||
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`.
|
||||
|
||||
```python
|
||||
from openfeature.api import (
|
||||
get_client,
|
||||
get_provider,
|
||||
set_provider
|
||||
get_evaluation_context,
|
||||
set_evaluation_context,
|
||||
)
|
||||
|
||||
global_context = EvaluationContext(
|
||||
targeting_key="targeting_key1", attributes={"application": "value1"}
|
||||
)
|
||||
request_context = EvaluationContext(
|
||||
targeting_key="targeting_key2", attributes={"email": request.form['email']}
|
||||
)
|
||||
|
||||
## set global context
|
||||
set_evaluation_context(first_context)
|
||||
|
||||
# merge second context
|
||||
client = get_client(name="No-op Provider", version="0.5.2")
|
||||
client.get_string_value("email", None, request_context)
|
||||
|
||||
```
|
||||
|
||||
### Events
|
||||
|
||||
TBD (See Issue [#131](https://github.com/open-feature/python-sdk/issues/131))
|
||||
|
||||
### Providers:
|
||||
|
||||
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.
|
||||
|
||||
See [here](https://openfeature.dev/ecosystem) for a catalog of available providers.
|
||||
|
||||
### Hooks:
|
||||
|
||||
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.
|
||||
|
||||
```python
|
||||
from openfeature.hook import Hook
|
||||
|
||||
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.
|
||||
|
||||
### Logging:
|
||||
|
||||
TBD
|
||||
|
||||
## ⭐️ Support the project
|
||||
|
||||
- Give this repo a ⭐️!
|
||||
- Follow us on social media:
|
||||
- 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/)
|
||||
|
||||
## 🤝 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
|
||||
|
||||
<!-- TODO: update with correct repo -->
|
||||
<a href="https://github.com/open-feature/python-sdk/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=open-feature/python-sdk" alt="Pictures of the folks who have contributed to the project" />
|
||||
</a>
|
||||
|
||||
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)
|
||||
|
||||
<!-- TODO: add FOSSA widget -->
|
||||
|
||||
[openfeature-website]: https://openfeature.dev
|
||||
Loading…
Reference in New Issue