Skip to content

Commit 62055b3

Browse files
nicklaslclaude
andauthored
docs(python): expand Python provider documentation (#268)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 0c2d807 commit 62055b3

2 files changed

Lines changed: 194 additions & 9 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ The tools and SDKs published for direct usage:
1212
- [Go](./openfeature-provider/go/README.md)
1313
- [Java](./openfeature-provider/java/README.md)
1414
- [JavaScript/TypeScript](./openfeature-provider/js/README.md)
15+
- [Python](./openfeature-provider/python/README.md) (alpha)
1516
- [Rust](./openfeature-provider/rust/README.md)
1617
- [Ruby](./openfeature-provider/ruby/README.md)
1718

openfeature-provider/python/README.md

Lines changed: 193 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,121 @@
11
# Confidence OpenFeature Provider for Python
22

3-
This is the Python OpenFeature provider for [Confidence](https://confidence.spotify.com/) feature flags. It uses a WebAssembly-based local resolver for low-latency flag evaluation.
3+
![Status: Alpha](https://img.shields.io/badge/status-alpha-orange)
4+
5+
A high-performance OpenFeature provider for [Confidence](https://confidence.spotify.com/) feature flags that evaluates flags locally for minimal latency.
6+
7+
## Features
8+
9+
- **Local Resolution**: Evaluates feature flags locally using WebAssembly (WASM)
10+
- **Low Latency**: No network calls during flag evaluation
11+
- **Automatic Sync**: Periodically syncs flag configurations from Confidence
12+
- **Exposure Logging**: Fully supported exposure logging (and other resolve analytics)
13+
- **OpenFeature Compatible**: Works with the standard OpenFeature SDK
14+
15+
## Requirements
16+
17+
- Python 3.9+
18+
- OpenFeature SDK 0.8.0+
419

520
## Installation
621

722
```bash
823
pip install confidence-openfeature-provider
924
```
1025

11-
## Usage
26+
## Getting Your Credentials
27+
28+
You'll need a **client secret** from Confidence to use this provider.
29+
30+
**📖 See the [Integration Guide: Getting Your Credentials](../INTEGRATION_GUIDE.md#getting-your-credentials)** for step-by-step instructions on:
31+
- How to navigate the Confidence dashboard
32+
- Creating a Backend integration
33+
- Creating a test flag for verification
34+
- Best practices for credential storage
35+
36+
## Quick Start
1237

1338
```python
1439
from openfeature import api
40+
from openfeature.evaluation_context import EvaluationContext
1541
from confidence import ConfidenceProvider
1642

1743
# Create and register the provider
1844
provider = ConfidenceProvider(client_secret="your-client-secret")
1945
api.set_provider(provider)
2046

21-
# Get the client and evaluate flags
47+
# Get a client
2248
client = api.get_client()
23-
result = client.get_boolean_value("my-flag.enabled", default_value=False)
49+
50+
# Create evaluation context with user attributes for targeting
51+
context = EvaluationContext(
52+
targeting_key="user-123",
53+
attributes={
54+
"country": "US",
55+
"plan": "premium",
56+
}
57+
)
58+
59+
# Evaluate a flag
60+
enabled = client.get_boolean_value("test-flag.enabled", default_value=False, evaluation_context=context)
61+
print(f"Flag value: {enabled}")
62+
63+
# Don't forget to shutdown when your application exits (see Shutdown section)
2464
```
2565

26-
## Features
66+
## Evaluation Context
67+
68+
The evaluation context contains information about the user/session being evaluated for targeting and A/B testing.
2769

28-
- Local WASM-based flag resolution for low latency
29-
- Automatic state polling for flag updates
30-
- Async flag logging to Confidence backend
31-
- Support for materialized (sticky) assignments
70+
### Python Examples
71+
72+
```python
73+
from openfeature.evaluation_context import EvaluationContext
74+
75+
# Simple attributes
76+
context = EvaluationContext(
77+
targeting_key="user-123",
78+
attributes={
79+
"country": "US",
80+
"plan": "premium",
81+
"age": 25,
82+
}
83+
)
84+
```
85+
86+
## Error Handling
87+
88+
The provider uses a **default value fallback** pattern - when evaluation fails, it returns your specified default value instead of throwing an error.
89+
90+
**📖 See the [Integration Guide: Error Handling](../INTEGRATION_GUIDE.md#error-handling)** for:
91+
- Common failure scenarios
92+
- Error codes and meanings
93+
- Production best practices
94+
- Monitoring recommendations
95+
96+
### Python Examples
97+
98+
```python
99+
# The provider returns the default value on errors
100+
enabled = client.get_boolean_value("my-flag.enabled", default_value=False, evaluation_context=context)
101+
# enabled will be False if evaluation failed
102+
103+
# For detailed error information, use get_boolean_details()
104+
details = client.get_boolean_details("my-flag.enabled", default_value=False, evaluation_context=context)
105+
if details.error_code:
106+
print(f"Flag evaluation error: {details.error_message}")
107+
print(f"Reason: {details.reason}")
108+
```
109+
110+
## Shutdown
111+
112+
**Important**: To ensure proper cleanup and flushing of exposure logs, you should call `shutdown()` on the provider when your application exits.
113+
114+
```python
115+
from openfeature import api
116+
# Shutdown the provider to flush logs and clean up resources
117+
api.shutdown()
118+
```
32119

33120
## Configuration
34121

@@ -40,6 +127,103 @@ provider = ConfidenceProvider(
40127
)
41128
```
42129

130+
### Configuration Options
131+
132+
- `client_secret` (str, required): The Confidence client secret for authentication.
133+
- `state_poll_interval` (float, optional): Interval in seconds between state polling updates. Defaults to 30.0.
134+
- `log_poll_interval` (float, optional): Interval in seconds for sending evaluation logs. Defaults to 10.0.
135+
- `use_remote_materialization_store` (bool, optional): Enable remote materialization storage. Defaults to False.
136+
137+
## Materializations
138+
139+
The provider supports **materializations** for two key use cases:
140+
141+
1. **Sticky Assignments**: Maintain consistent variant assignments across evaluations even when targeting attributes change.
142+
2. **Custom Targeting via Materialized Segments**: Efficiently target precomputed sets of identifiers from datasets.
143+
144+
### Default Behavior
145+
146+
By default, materializations are not supported. If a flag requires materialization data, the evaluation will return the default value.
147+
148+
### Remote Materialization Store
149+
150+
Enable remote materialization storage to have Confidence manage materialization data server-side:
151+
152+
```python
153+
provider = ConfidenceProvider(
154+
client_secret="your-client-secret",
155+
use_remote_materialization_store=True,
156+
)
157+
```
158+
159+
**⚠️ Important Performance Impact**: This option adds network calls during flag evaluation for materialization reads/writes.
160+
161+
### Custom Materialization Store
162+
163+
For advanced use cases, you can implement the `MaterializationStore` protocol to manage materialization data in your own infrastructure. The protocol defines two methods:
164+
165+
- `read(ops: List[ReadOp]) -> List[ReadResult]`: Batch read of materialization data
166+
- `write(ops: List[VariantWriteOp]) -> None`: Batch write of variant assignments
167+
168+
The read operations support two types:
169+
170+
- **VariantReadOp**: Query for a sticky variant assignment (returns `VariantReadResult`)
171+
- **InclusionReadOp**: Query for segment inclusion (returns `InclusionReadResult`)
172+
173+
```python
174+
from confidence.materialization import (
175+
MaterializationStore,
176+
ReadOp,
177+
ReadResult,
178+
VariantReadOp,
179+
VariantReadResult,
180+
InclusionReadOp,
181+
InclusionReadResult,
182+
VariantWriteOp,
183+
)
184+
185+
class MyMaterializationStore:
186+
"""Custom materialization store implementation."""
187+
188+
def read(self, ops: list[ReadOp]) -> list[ReadResult]:
189+
results = []
190+
for op in ops:
191+
if isinstance(op, VariantReadOp):
192+
# Look up sticky variant assignment
193+
variant = self._lookup_variant(op.unit, op.materialization, op.rule)
194+
results.append(VariantReadResult(
195+
unit=op.unit,
196+
materialization=op.materialization,
197+
rule=op.rule,
198+
variant=variant, # None if no assignment exists
199+
))
200+
elif isinstance(op, InclusionReadOp):
201+
# Check segment inclusion
202+
included = self._check_inclusion(op.unit, op.materialization)
203+
results.append(InclusionReadResult(
204+
unit=op.unit,
205+
materialization=op.materialization,
206+
included=included,
207+
))
208+
return results
209+
210+
def write(self, ops: list[VariantWriteOp]) -> None:
211+
for op in ops:
212+
# Store sticky variant assignment
213+
self._store_variant(op.unit, op.materialization, op.rule, op.variant)
214+
```
215+
216+
Pass your custom store to the provider:
217+
218+
```python
219+
provider = ConfidenceProvider(
220+
client_secret="your-client-secret",
221+
materialization_store=MyMaterializationStore(),
222+
)
223+
```
224+
225+
**Thread Safety**: Your implementation must be thread-safe as it may be called concurrently from multiple threads.
226+
43227
## Logging
44228

45229
Configure logging to see provider activity:

0 commit comments

Comments
 (0)