Skip to content

Commit 4b0783e

Browse files
v1.1.0 (#3)
* feature/v1.1.0: v1.1.0 --------- Co-authored-by: deleteLater <[email protected]>
1 parent b00b128 commit 4b0783e

File tree

7 files changed

+231
-183
lines changed

7 files changed

+231
-183
lines changed

README.md

Lines changed: 109 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,75 @@
1-
# FeatBit python sdk
1+
# FeatBit Server-Side SDK for Python
22

33
## Introduction
44

5-
This is the Python Server SDK for the feature management platform FeatBit. It is
6-
intended for use in a multiple-users python server applications.
5+
This is the Python Server-Side SDK for the 100% open-source feature flags management platform [FeatBit](https://github.com/featbit/featbit).
76

8-
This SDK has two main purposes:
9-
10-
- Store the available feature flags and evaluate the feature flags by given user in the server side SDK
11-
- Sends feature flags usage, and custom events for the insights and A/B/n testing.
7+
The FeatBit Server-Side SDK for Python is designed primarily for use in multi-user systems such as web servers and applications.
128

139
## Data synchonization
1410

15-
We use websocket to make the local data synchronized with the server, and then store them in the memory by default.
16-
Whenever there is any changes to a feature flag or his related data, the changes would be pushed to the SDK, the average
17-
synchronization time is less than **100** ms. Be aware the websocket connection can be interrupted by any error or
18-
internet interruption, but it would be restored automatically right after the problem is gone.
11+
We use websocket to make the local data synchronized with the FeatBit server, and then store them in memory by
12+
default. Whenever there is any change to a feature flag or its related data, this change will be pushed to the SDK and
13+
the average synchronization time is less than 100 ms. Be aware the websocket connection may be interrupted due to
14+
internet outage, but it will be resumed automatically once the problem is gone.
1915

20-
## Offline mode support
16+
If you want to use your own data source, see [Offline Mode](#offline-mode).
2117

22-
In the offline mode, SDK DOES not exchange any data with feature flag center, this mode is only use for internal test for instance.
18+
## Get Started
2319

24-
To open the offline mode:
25-
```python
26-
config = Config(env_secret, event_url, streaming_url, offline=True)
20+
### Installation
21+
install the sdk in using pip, this version of the SDK is compatible with Python 3.6 through 3.11.
22+
23+
```shell
24+
pip install fb-python-sdk
2725
```
2826

29-
## Evaluation of a feature flag
27+
### Quick Start
3028

31-
SDK will initialize all the related data(feature flags, segments etc.) in the bootstrapping and receive the data updates
32-
in real time, as mentioned in the above
29+
> Note that the _**env_secret**_, _**streaming_url**_ and _**event_url**_ are required to initialize the SDK.
3330
34-
After initialization, the SDK has all the feature flags in the memory and all evaluation is done locally and synchronously, the average evaluation time is < **10** ms.
31+
The following code demonstrates basic usage of the SDK.
3532

36-
## Installation
37-
install the sdk in using pip, this version of the SDK is compatible with Python 3.6 through 3.10.
33+
```python
34+
from fbclient import get, set_config
35+
from fbclient.config import Config
3836

39-
```
40-
pip install fb-python-sdk
37+
env_secret = '<replace-with-your-env-secret>'
38+
event_url = 'http://localhost:5100'
39+
streaming_url = '"ws://localhost:5100"'
40+
41+
set_config(Config(env_secret, event_url, streaming_url))
42+
client = get()
43+
44+
if client.initialize:
45+
flag_key = '<replace-with-your-flag-key>'
46+
user_key = 'bot-id'
47+
user_name = 'bot'
48+
user = {'key': user_key, 'name': user_name}
49+
detail = client.variation_detail(flag_key, user, default=None)
50+
print(f'flag {flag_key} returns {detail.value} for user {user_key}, reason: {detail.reason}')
51+
52+
# ensure that the SDK shuts down cleanly and has a chance to deliver events to FeatBit before the program exits
53+
client.stop()
4154
```
4255

43-
## SDK
56+
### Examples
4457

45-
Applications SHOULD instantiate a single instance for the lifetime of the application. In the case where an application
58+
- [Python Demo](https://github.com/featbit/featbit-samples/blob/main/samples/dino-game/demo-python/demo_python.py)
59+
60+
### FBClient
61+
62+
Applications **SHOULD instantiate a single FBClient instance** for the lifetime of the application. In the case where an application
4663
needs to evaluate feature flags from different environments, you may create multiple clients, but they should still be
4764
retained for the lifetime of the application rather than created per request or per thread.
4865

49-
### Bootstrapping
66+
#### Bootstrapping
5067

51-
The bootstrapping is in fact the call of constructor of `FFCClient`, in which the SDK will be initialized and connect to feature flag center
68+
The bootstrapping is in fact the call of constructor of `FBClient`, in which the SDK will be initialized and connect to FeatBit.
5269

5370
The constructor will return when it successfully connects, or when the timeout(default: 15 seconds) expires, whichever comes first. If it has not succeeded in connecting when the timeout elapses, you will receive the client in an uninitialized state where feature flags will return default values; it will still continue trying to connect in the background unless there has been a network error or you close the client(using `stop()`). You can detect whether initialization has succeeded by calling `initialize()`.
5471

55-
The best way to use the SDK as a singleton, first make sure you have called `fbclient.set_config()` at startup time. Then `fbclient.get()` will return the same shared `fbclient.client.FFCClient` instance each time. The client will be initialized if it runs first time.
72+
The best way to use the SDK as a singleton, first make sure you have called `fbclient.set_config()` at startup time. Then `fbclient.get()` will return the same shared `fbclient.client.FBClient` instance each time. The client will be initialized if it runs first time.
5673
```python
5774
from fbclient.config import Config
5875
from fbclient import get, set_config
@@ -61,61 +78,91 @@ set_config(Config(env_secret, event_url, streaming_url))
6178
client = get()
6279

6380
if client.initialize:
64-
# your code
65-
81+
# the client is ready
6682
```
67-
You can also manage your `fbclient.client.FBClient`, the SDK will be initialized if you call `fbclient.client.FBClient` constructor.
83+
You can also manage your `fbclient.client.FBClient`, the SDK will be initialized if you call `fbclient.client.FBClient` constructor. With constructor, you can set the timeout for initialization, the default value is 15 seconds.
6884
```python
6985
from fbclient.config import Config
7086
from fbclient.client import FBClient
7187

7288
client = FBClient(Config(env_secret, event_url, streaming_url), start_wait=15)
7389

7490
if client.initialize:
75-
# your code
76-
91+
# the client is ready
7792
```
7893
If you prefer to have the constructor return immediately, and then wait for initialization to finish at some other point, you can use `fbclient.client.fbclient.update_status_provider` object, which provides an asynchronous way, as follows:
7994

8095
``` python
8196
from fbclient.config import Config
8297
from fbclient.client import FBClient
8398

84-
client = FFCClient(Config(env_secret), start_wait=0)
85-
if client._update_status_provider.wait_for_OKState():
86-
# your code
87-
99+
client = FBClient(Config(env_secret), start_wait=0)
100+
if client.update_status_provider.wait_for_OKState():
101+
# the client is ready
88102
```
89103

104+
It's possible to set a timeout in seconds for the `wait_for_OKState` method. If the timeout is reached, the method will return `False` and the client will still be in an uninitialized state. If you do not specify a timeout, the method will wait indefinitely.
90105

91-
### Evaluation
92106

93-
SDK calculates the value of a feature flag for a given user, and returns a flag vlaue/an object that describes the way that the value was determined.
107+
> To check if the client is ready is optional. Even if the client is not ready, you can still evaluate feature flags, but the default value will be returned if SDK is not yet initialized.
108+
109+
### FBUser
94110

95-
`User`: A dictionary of attributes that can affect flag evaluation, usually corresponding to a user of your application.
111+
A dictionary of attributes that can affect flag evaluation, usually corresponding to a user of your application.
96112
This object contains built-in properties(`key`, `name`). The `key` and `name` are required. The `key` must uniquely identify each user; this could be a username or email address for authenticated users, or a ID for anonymous users. The `name` is used to search your user quickly. You may also define custom properties with arbitrary names and values.
97-
For instance, the custom key should be a string; the custom value should be a string or a number
113+
For instance, the custom key should be a string; the custom value should be a string, number or boolean value
114+
115+
```python
116+
user = {'key': user_key, 'name': user_name, 'age': age}
117+
```
118+
119+
### Evaluation
120+
121+
SDK calculates the value of a feature flag for a given user, and returns a flag vlaue/an object that describes the way that the value was determined.
98122

99123
```python
100124
if client.initialize:
101125
user = {'key': user_key, 'name': user_name, 'age': age}
126+
# evaluate the flag value
102127
flag_value = client.variation(flag_key, user, default_value)
103-
# your if/else code according to flag value
104-
128+
# evaluate the flag value and get the detail
129+
detail = client.variation_detail(flag_key, user, default=None)
105130
```
106-
If evaluation called before SDK client initialized or you set the wrong flag key or user for the evaluation, SDK will return
107-
the default value you set. The `fbclient.common_types.FlagState` will explain the details of the last evaluation including error raison.
108131

109-
If you would like to get variations of all feature flags in a special environment, you can use `fbclient.client.FBClient.get_all_latest_flag_variations`, SDK will return `fbclient.common_types.AllFlagStates`, that explain the details of all feature flags
132+
If you would like to get variations of all feature flags in a special environment, you can use `fbclient.client.FBClient.get_all_latest_flag_variations`, SDK will return `fbclient.common_types.AllFlagStates`, that explain the details of all feature flags. `fbclient.common_types.AllFlagStates.get()` returns the detail of a given feature flag key.
133+
110134
```python
111135
if client.initialize:
112136
user = {'key': user_key, 'name': user_name}
113137
all_flag_values = client.get_all_latest_flag_variations(user)
114-
ed = all_flag_values.get(flag_key)
115-
flag_value = ed.variation
116-
# your if/else code according to flag value
138+
detail = all_flag_values.get(flag_key, default=None)
139+
```
140+
141+
> Note that if evaluation called before Go SDK client initialized, you set the wrong flag key/user for the evaluation or the related feature flag is not found, SDK will return the default value you set. The `fbclient.common_types.EvalDetail` will explain the details of the latest evaluation including error raison.
142+
143+
### Offline Mode
144+
145+
In some situations, you might want to stop making remote calls to FeatBit. Here is how:
117146

118-
147+
```python
148+
config = Config(env_secret, event_url, streaming_url, offline=True)
149+
```
150+
When you put the SDK in offline mode, no insight message is sent to the server and all feature flag evaluations return
151+
fallback values because there are no feature flags or segments available. If you want to use your own data source,
152+
SDK allows users to populate feature flags and segments data from a JSON string. Here is an example: [fbclient_test_data.json](tests/fbclient_test_data.json).
153+
154+
The format of the data in flags and segments is defined by FeatBit and is subject to change. Rather than trying to construct these objects yourself, it's simpler to request existing flags directly from the FeatBit server in JSON format and use this output as the starting point for your file. Here's how:
155+
156+
```shell
157+
# replace http://localhost:5100 with your evaluation server url
158+
curl -H "Authorization: <your-env-secret>" http://localhost:5100/api/public/sdk/server/latest-all > featbit-bootstrap.json
159+
```
160+
161+
Then you can use this file to initialize the SDK in offline mode:
162+
163+
```python
164+
// first load data from file and then
165+
client.initialize_from_external_json(json)
119166
```
120167

121168
### Experiments (A/B/n Testing)
@@ -128,4 +175,14 @@ client.track_metric(user, event_name, numeric_value);
128175
**numeric_value** is not mandatory, the default value is **1**.
129176

130177
Make sure `track_metric` is called after the related feature flag is evaluated by simply calling `variation` or `variation_detail`
131-
otherwise, the custom event may not be included into the experiment result.
178+
otherwise, the custom event may not be included into the experiment result.
179+
180+
## Getting support
181+
182+
- If you have a specific question about using this sdk, we encourage you
183+
to [ask it in our slack](https://join.slack.com/t/featbit/shared_invite/zt-1ew5e2vbb-x6Apan1xZOaYMnFzqZkGNQ).
184+
- If you encounter a bug or would like to request a
185+
feature, [submit an issue](https://github.com/featbit/featbit-python-sdk/issues/new).
186+
187+
## See Also
188+
- [Connect To Python Sdk](https://docs.featbit.co/docs/getting-started/4.-connect-an-sdk/server-side-sdks/python-sdk)

0 commit comments

Comments
 (0)