Skip to content

Commit 806b6d1

Browse files
committed
Updated readme, changes and tests
1 parent 0f55db9 commit 806b6d1

12 files changed

Lines changed: 198 additions & 38 deletions

File tree

.github/.DS_Store

6 KB
Binary file not shown.

.github/workflows/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* @splitio/sdk

.github/workflows/ci.yml

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
name: ci
2+
on:
3+
push:
4+
branches:
5+
- main
6+
- development
7+
pull_request:
8+
branches:
9+
- main
10+
- development
11+
12+
concurrency:
13+
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
14+
cancel-in-progress: true
15+
16+
jobs:
17+
test:
18+
name: Test
19+
runs-on: ubuntu-22.04
20+
steps:
21+
- name: Checkout code
22+
uses: actions/checkout@v3
23+
with:
24+
fetch-depth: 0
25+
26+
- name: Setup Python
27+
uses: actions/setup-python@v3
28+
with:
29+
python-version: '3.7.16'
30+
31+
- name: Install dependencies
32+
run: |
33+
sudo apt update
34+
sudo apt-get install -y libkrb5-dev
35+
pip install -U setuptools pip wheel
36+
pip install -e .[cpphash,redis,uwsgi]
37+
38+
- name: Run tests
39+
run: python setup.py install
40+
run: tests/pytest
41+
42+
- name: Set VERSION env
43+
run: echo "VERSION=$(cat setup.py | grep "version=" | cut -d'"' -f2)" >> $GITHUB_ENV
44+
45+
- name: SonarQube Scan (Push)
46+
if: github.event_name == 'push'
47+
uses: SonarSource/sonarcloud-github-action@v1.9
48+
env:
49+
SONAR_TOKEN: ${{ secrets.SONARQUBE_TOKEN }}
50+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
51+
with:
52+
projectBaseDir: .
53+
args: >
54+
-Dsonar.host.url=${{ secrets.SONARQUBE_HOST }}
55+
-Dsonar.projectVersion=${{ env.VERSION }}
56+
57+
- name: SonarQube Scan (Pull Request)
58+
if: github.event_name == 'pull_request'
59+
uses: SonarSource/sonarcloud-github-action@v1.9
60+
env:
61+
SONAR_TOKEN: ${{ secrets.SONARQUBE_TOKEN }}
62+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
63+
with:
64+
projectBaseDir: .
65+
args: >
66+
-Dsonar.host.url=${{ secrets.SONARQUBE_HOST }}
67+
-Dsonar.projectVersion=${{ env.VERSION }}
68+
-Dsonar.pullrequest.key=${{ github.event.pull_request.number }}
69+
-Dsonar.pullrequest.branch=${{ github.event.pull_request.head.ref }}
70+
-Dsonar.pullrequest.base=${{ github.event.pull_request.base.ref }}

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,6 @@ dmypy.json
130130

131131
# IDE
132132
.idea/
133+
134+
# Other
135+
.DS_Store

CHANGES.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
11
0.0.1
22
- First release. Up to date with spec 0.5.1 and python sdk 0.0.6
3+
34
0.1.0
45
- Up to date with spec 0.8.0 and python sdk 0.8.1. Using split client 10.2.0
6+
7+
1.0.0 (Nov 5 2026)
8+
- BREAKING CHANGE: Passing the SplitClient object to Provider constructor is now only through the initialization context dictionary
9+
- BREAKING CHANGE: Provider will throw exception when ObjectDetail and ObjectValue evaluation is used, since it will attempt to parse the treatment as a JSON structure.
10+
- Upgraded Split SDK to 10.5.1
11+
- Upgraded OpenFeature SDK to 0.8.3
12+
- Added support for asyncio mode
13+
- Added ability to pass Ready Timeout and ConfigurationOptions to Provider initialization

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright © 2022 Split Software, Inc.
1+
Copyright © 2025 Split Software, Inc.
22

33
Licensed under the Apache License, Version 2.0 (the "License");
44
you may not use this file except in compliance with the License.

README.md

Lines changed: 75 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
This Provider is designed to allow the use of OpenFeature with Split, the platform for controlled rollouts, serving features to your users via the Split feature flag to manage your complete customer experience.
66

77
## Compatibility
8-
This SDK is compatible with Python 3 and higher.
8+
This SDK is compatible with Python 3.7.16 and higher.
99

1010
## Getting started
1111
### Pip Installation
@@ -18,21 +18,28 @@ Below is a simple example that describes using the Split Provider. Please see th
1818
```python
1919
from openfeature import api
2020
from split_openfeature import SplitProvider
21-
22-
api.set_provider(SplitProvider(api_key="YOUR_API_KEY"))
21+
config = {
22+
'impressionsMode': 'OPTIMIZED',
23+
'impressionsRefreshRate': 30,
24+
}
25+
provider = SplitProvider({"SdkKey": "YOUR_API_KEY", "ConfigOptions": config, "ReadyBlockTime": 5})
26+
api.set_provider(provider)
2327
```
2428

25-
If you are more familiar with Split or want access to other initialization options, you can provide a Split `client` to the constructor. See the [Split Java SDK Documentation](https://help.split.io/hc/en-us/articles/360020405151-Java-SDK) for more information.
29+
If you are more familiar with Split or want access to other initialization options, you can provide a Split `client` to the constructor. See the [Harness Split Python SDK Documentation](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/server-side-sdks/python-sdk/) for more information.
2630
```python
2731
from openfeature import api
2832
from split_openfeature import SplitProvider
2933
from splitio import get_factory
3034

31-
factory = get_factory("YOUR_API_KEY", config=config_file)
35+
config = {
36+
'impressionsMode': 'OPTIMIZED',
37+
'impressionsRefreshRate': 30,
38+
}
39+
factory = get_factory("YOUR_API_KEY", config=config)
3240
factory.block_until_ready(5)
33-
api.set_provider(SplitProvider(client=factory.client()))
41+
api.set_provider(SplitProvider({"SplitClient": factory.client()}))
3442
```
35-
where config_file is the Split config file you want to use
3643

3744
## Use of OpenFeature with Split
3845
After the initial setup you can use OpenFeature according to their [documentation](https://docs.openfeature.dev/docs/reference/concepts/evaluation-api/).
@@ -56,9 +63,69 @@ or at the OpenFeatureAPI level
5663
```python
5764
context = EvaluationContext(targeting_key="TARGETING_KEY")
5865
api.set_evaluation_context(context)
59-
````
66+
```
6067
If the context was set at the client or api level, it is not required to provide it during flag evaluation.
6168

69+
### Asyncio mode
70+
The provider supports asyncio mode as well, using the asyncio mode in Split SDK.
71+
Example below shows using the provider in asyncio
72+
73+
```python
74+
from openfeature import api
75+
from split_openfeature import SplitProviderAsync
76+
config = {
77+
'impressionsMode': 'OPTIMIZED',
78+
'impressionsRefreshRate': 30,
79+
}
80+
provider = SplitProvider({"SdkKey": "YOUR_API_KEY", "ConfigOptions": config, "ReadyBlockTime": 5})
81+
await provider.create()
82+
api.set_provider(provider)
83+
```
84+
85+
Example below show how to create the Split Client externally and pass it to Provider
86+
```python
87+
from openfeature import api
88+
from split_openfeature import SplitProviderAsync
89+
from splitio import get_factory_async
90+
91+
config = {
92+
'impressionsMode': 'OPTIMIZED',
93+
'impressionsRefreshRate': 30,
94+
}
95+
factory = get_factory_async("YOUR_API_KEY", config=config)
96+
await factory.block_until_ready(5)
97+
provider = SplitProviderAsync({"SplitClient": factory.client()})
98+
await provider.create()
99+
api.set_provider(provider)
100+
```
101+
102+
Example below fetching the treatment in asyncio mode
103+
```python
104+
from openfeature import api
105+
from openfeature.evaluation_context import EvaluationContext
106+
107+
client = api.get_client("CLIENT_NAME")
108+
109+
context = EvaluationContext(targeting_key="TARGETING_KEY")
110+
value = await client.get_boolean_value("FLAG_NAME", False, context)
111+
```
112+
113+
### Shutting down Split SDK factory
114+
Currently OpenFeature SDK does not provide override for provider shutdown, when using internal split client object, the Split SDK will not shutdown properly. We recommend using the example below before terminating the OpenFeature object
115+
116+
```python
117+
from threading import Event
118+
119+
destroy_event = Event()
120+
provider._split_client_wrapper._factory.destroy(destroy_event)
121+
destroy_event.wait()
122+
```
123+
124+
Below the example for asyncio mode
125+
```python
126+
await provider._split_client_wrapper._factory.destroy()
127+
```
128+
62129
## Submitting issues
63130

64131
The Split team monitors all issues submitted to this [issue tracker](https://github.com/splitio/split-openfeature-provider-python/issues). We encourage you to use this issue tracker to submit any bug reports, feedback, and feature enhancements. We'll do our best to respond in a timely manner.

setup.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
setuptools.setup(
77
name="split_openfeature",
88
version="1.0.0",
9-
author="Robert Grassian",
10-
author_email="robert.grassian@split.io",
119
description="The official Python Split Provider for OpenFeature",
1210
long_description=long_description,
1311
long_description_content_type="text/markdown",

split_openfeature/split_client_wrapper.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ def is_sdk_ready(self):
6969

7070
return self.sdk_ready
7171

72+
def destroy(self, destroy_event=None):
73+
self._factory.destroy(destroy_event)
74+
75+
async def destroy_async(self):
76+
await self._factory.destroy()
77+
7278
async def is_sdk_ready_async(self):
7379
if self.sdk_ready:
7480
return True

split_openfeature/split_provider.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import typing
22
import logging
3+
import json
4+
from threading import Event
35

46
from openfeature.hook import Hook
57
from openfeature.evaluation_context import EvaluationContext
68
from openfeature.exception import ErrorCode, GeneralError, ParseError, OpenFeatureError, TargetingKeyMissingError
79
from openfeature.flag_evaluation import Reason, FlagResolutionDetails
810
from openfeature.provider import AbstractProvider, Metadata
911
from split_openfeature.split_client_wrapper import SplitClientWrapper
10-
import json
1112

1213
_LOGGER = logging.getLogger(__name__)
1314

@@ -159,7 +160,7 @@ def resolve_float_details(self, flag_key: str, default_value: float,
159160
def resolve_object_details(self, flag_key: str, default_value: dict,
160161
evaluation_context: EvaluationContext = EvaluationContext()):
161162
return self._evaluate_treatment(flag_key, evaluation_context, default_value)
162-
163+
163164
class SplitProviderAsync(SplitProviderBase):
164165
def __init__(self, initial_context):
165166
if isinstance(initial_context, dict):

0 commit comments

Comments
 (0)