From 806b6d1f9ff153c68792719dadba623bea34ef23 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 4 Nov 2025 12:09:56 -0800 Subject: [PATCH 01/15] Updated readme, changes and tests --- .github/.DS_Store | Bin 0 -> 6148 bytes .github/workflows/CODEOWNERS | 1 + .github/workflows/ci.yml | 70 ++++++++++++++++++ .gitignore | 3 + CHANGES.txt | 9 +++ LICENSE | 2 +- README.md | 83 +++++++++++++++++++--- setup.py | 2 - split_openfeature/split_client_wrapper.py | 6 ++ split_openfeature/split_provider.py | 5 +- tests/test_client.py | 39 ++++------ tests/test_split_client_wrapper.py | 16 +++++ 12 files changed, 198 insertions(+), 38 deletions(-) create mode 100644 .github/.DS_Store create mode 100644 .github/workflows/CODEOWNERS create mode 100644 .github/workflows/ci.yml diff --git a/.github/.DS_Store b/.github/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..3f88f3595a8acc15053144471a157d13e402cb5f GIT binary patch literal 6148 zcmeHK%}N6?5T4NrTfFq>F|W`!Sj!5%_aF#@Y!?;Gl42>n?%Vk$K8N3AM%kqSJ&DK+ zB;O?SljMUOhxE0N@PMS)fZTAu++Q zHXIaTfv|=GHI%KyU=7E7Fu&SxP}Fc@D?Zp({w!WNua5j7yA#)n-a7-%z?6Y=9WLbk zzriO{Eb^xzK5_<}fq%vT&zeoM!lCSKJvpAdwE^t`O+@?#Q6SJKKLHrXJ#v+eY7e5r YuQnVMC5z1GaG?JPWJ0`i27ZBo4+u>+;Q#;t literal 0 HcmV?d00001 diff --git a/.github/workflows/CODEOWNERS b/.github/workflows/CODEOWNERS new file mode 100644 index 0000000..ab53a7c --- /dev/null +++ b/.github/workflows/CODEOWNERS @@ -0,0 +1 @@ +* @splitio/sdk \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3936567 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,70 @@ +name: ci +on: + push: + branches: + - main + - development + pull_request: + branches: + - main + - development + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + test: + name: Test + runs-on: ubuntu-22.04 + steps: + - name: Checkout code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Setup Python + uses: actions/setup-python@v3 + with: + python-version: '3.7.16' + + - name: Install dependencies + run: | + sudo apt update + sudo apt-get install -y libkrb5-dev + pip install -U setuptools pip wheel + pip install -e .[cpphash,redis,uwsgi] + + - name: Run tests + run: python setup.py install + run: tests/pytest + + - name: Set VERSION env + run: echo "VERSION=$(cat setup.py | grep "version=" | cut -d'"' -f2)" >> $GITHUB_ENV + + - name: SonarQube Scan (Push) + if: github.event_name == 'push' + uses: SonarSource/sonarcloud-github-action@v1.9 + env: + SONAR_TOKEN: ${{ secrets.SONARQUBE_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + projectBaseDir: . + args: > + -Dsonar.host.url=${{ secrets.SONARQUBE_HOST }} + -Dsonar.projectVersion=${{ env.VERSION }} + + - name: SonarQube Scan (Pull Request) + if: github.event_name == 'pull_request' + uses: SonarSource/sonarcloud-github-action@v1.9 + env: + SONAR_TOKEN: ${{ secrets.SONARQUBE_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + projectBaseDir: . + args: > + -Dsonar.host.url=${{ secrets.SONARQUBE_HOST }} + -Dsonar.projectVersion=${{ env.VERSION }} + -Dsonar.pullrequest.key=${{ github.event.pull_request.number }} + -Dsonar.pullrequest.branch=${{ github.event.pull_request.head.ref }} + -Dsonar.pullrequest.base=${{ github.event.pull_request.base.ref }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 3efd499..9ce3aa4 100644 --- a/.gitignore +++ b/.gitignore @@ -130,3 +130,6 @@ dmypy.json # IDE .idea/ + +# Other +.DS_Store \ No newline at end of file diff --git a/CHANGES.txt b/CHANGES.txt index 389cae1..151b363 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,13 @@ 0.0.1 - First release. Up to date with spec 0.5.1 and python sdk 0.0.6 + 0.1.0 - Up to date with spec 0.8.0 and python sdk 0.8.1. Using split client 10.2.0 + +1.0.0 (Nov 5 2026) +- BREAKING CHANGE: Passing the SplitClient object to Provider constructor is now only through the initialization context dictionary +- 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. +- Upgraded Split SDK to 10.5.1 +- Upgraded OpenFeature SDK to 0.8.3 +- Added support for asyncio mode +- Added ability to pass Ready Timeout and ConfigurationOptions to Provider initialization diff --git a/LICENSE b/LICENSE index 051b5fd..df08de3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright © 2022 Split Software, Inc. +Copyright © 2025 Split Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 4744fc8..01db216 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ 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. ## Compatibility -This SDK is compatible with Python 3 and higher. +This SDK is compatible with Python 3.7.16 and higher. ## Getting started ### Pip Installation @@ -18,21 +18,28 @@ Below is a simple example that describes using the Split Provider. Please see th ```python from openfeature import api from split_openfeature import SplitProvider - -api.set_provider(SplitProvider(api_key="YOUR_API_KEY")) +config = { + 'impressionsMode': 'OPTIMIZED', + 'impressionsRefreshRate': 30, + } +provider = SplitProvider({"SdkKey": "YOUR_API_KEY", "ConfigOptions": config, "ReadyBlockTime": 5}) +api.set_provider(provider) ``` -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. +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. ```python from openfeature import api from split_openfeature import SplitProvider from splitio import get_factory -factory = get_factory("YOUR_API_KEY", config=config_file) +config = { + 'impressionsMode': 'OPTIMIZED', + 'impressionsRefreshRate': 30, + } +factory = get_factory("YOUR_API_KEY", config=config) factory.block_until_ready(5) -api.set_provider(SplitProvider(client=factory.client())) +api.set_provider(SplitProvider({"SplitClient": factory.client()})) ``` -where config_file is the Split config file you want to use ## Use of OpenFeature with Split 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 ```python context = EvaluationContext(targeting_key="TARGETING_KEY") api.set_evaluation_context(context) -```` +``` If the context was set at the client or api level, it is not required to provide it during flag evaluation. +### Asyncio mode +The provider supports asyncio mode as well, using the asyncio mode in Split SDK. +Example below shows using the provider in asyncio + +```python +from openfeature import api +from split_openfeature import SplitProviderAsync +config = { + 'impressionsMode': 'OPTIMIZED', + 'impressionsRefreshRate': 30, + } +provider = SplitProvider({"SdkKey": "YOUR_API_KEY", "ConfigOptions": config, "ReadyBlockTime": 5}) +await provider.create() +api.set_provider(provider) +``` + +Example below show how to create the Split Client externally and pass it to Provider +```python +from openfeature import api +from split_openfeature import SplitProviderAsync +from splitio import get_factory_async + +config = { + 'impressionsMode': 'OPTIMIZED', + 'impressionsRefreshRate': 30, + } +factory = get_factory_async("YOUR_API_KEY", config=config) +await factory.block_until_ready(5) +provider = SplitProviderAsync({"SplitClient": factory.client()}) +await provider.create() +api.set_provider(provider) +``` + +Example below fetching the treatment in asyncio mode +```python +from openfeature import api +from openfeature.evaluation_context import EvaluationContext + +client = api.get_client("CLIENT_NAME") + +context = EvaluationContext(targeting_key="TARGETING_KEY") +value = await client.get_boolean_value("FLAG_NAME", False, context) +``` + +### Shutting down Split SDK factory +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 + +```python +from threading import Event + +destroy_event = Event() +provider._split_client_wrapper._factory.destroy(destroy_event) +destroy_event.wait() +``` + +Below the example for asyncio mode +```python +await provider._split_client_wrapper._factory.destroy() +``` + ## Submitting issues 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. diff --git a/setup.py b/setup.py index dc64293..7e70095 100644 --- a/setup.py +++ b/setup.py @@ -6,8 +6,6 @@ setuptools.setup( name="split_openfeature", version="1.0.0", - author="Robert Grassian", - author_email="robert.grassian@split.io", description="The official Python Split Provider for OpenFeature", long_description=long_description, long_description_content_type="text/markdown", diff --git a/split_openfeature/split_client_wrapper.py b/split_openfeature/split_client_wrapper.py index 7496d75..df740a5 100644 --- a/split_openfeature/split_client_wrapper.py +++ b/split_openfeature/split_client_wrapper.py @@ -69,6 +69,12 @@ def is_sdk_ready(self): return self.sdk_ready + def destroy(self, destroy_event=None): + self._factory.destroy(destroy_event) + + async def destroy_async(self): + await self._factory.destroy() + async def is_sdk_ready_async(self): if self.sdk_ready: return True diff --git a/split_openfeature/split_provider.py b/split_openfeature/split_provider.py index e2adc1e..392b977 100644 --- a/split_openfeature/split_provider.py +++ b/split_openfeature/split_provider.py @@ -1,5 +1,7 @@ import typing import logging +import json +from threading import Event from openfeature.hook import Hook from openfeature.evaluation_context import EvaluationContext @@ -7,7 +9,6 @@ from openfeature.flag_evaluation import Reason, FlagResolutionDetails from openfeature.provider import AbstractProvider, Metadata from split_openfeature.split_client_wrapper import SplitClientWrapper -import json _LOGGER = logging.getLogger(__name__) @@ -159,7 +160,7 @@ def resolve_float_details(self, flag_key: str, default_value: float, def resolve_object_details(self, flag_key: str, default_value: dict, evaluation_context: EvaluationContext = EvaluationContext()): return self._evaluate_treatment(flag_key, evaluation_context, default_value) - + class SplitProviderAsync(SplitProviderBase): def __init__(self, initial_context): if isinstance(initial_context, dict): diff --git a/tests/test_client.py b/tests/test_client.py index b8966a6..e64a508 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1,4 +1,6 @@ import pytest +from threading import Event + from openfeature import api from openfeature.evaluation_context import EvaluationContext from openfeature.exception import ErrorCode @@ -23,6 +25,7 @@ def provider(self): @pytest.fixture def set_provider(self, provider): + self.provider = provider api.set_provider(provider) @pytest.fixture @@ -33,6 +36,10 @@ def client(self, set_provider): def targeting_key(self, client): client.context = EvaluationContext(targeting_key="key") + def _destroy_factory(self): + self.provider._split_client_wrapper._factory.destroy() + assert self.provider._split_client_wrapper._factory.destroyed + def test_use_default(self, client): # flags that do not exist should return the default value flag_name = "random-non-existent-feature" @@ -57,7 +64,7 @@ def test_use_default(self, client): default_obj = {"foo": "bar"} result = client.get_object_value(flag_name, default_obj) assert result == default_obj - + def test_missing_targeting_key(self, client): # Split requires a targeting key and should return the default treatment # and throw an error if not provided @@ -102,10 +109,6 @@ def test_float_split(self, client): result = client.get_float_value(self.float_feature, 2.3) assert result == 50.5 -# def test_obj_split(self, client): -# result = client.get_object_value(self.obj_feature, {}) -# assert result == {"key": "value"} - def test_get_metadata(self): assert api.get_provider_metadata().name == "Split" @@ -141,15 +144,13 @@ def test_string_details(self, client): assert details.value == "off" assert details.variant == "off" assert details.error_code is None - ''' + def test_obj_details(self, client): - details = client.get_object_details(self.obj_feature, {}) + details = client.get_object_details(self.obj_feature, {"val": "control"}) assert details.flag_key == self.obj_feature - assert details.reason == Reason.TARGETING_MATCH - assert details.value == {"key": "value"} - assert details.variant == "{\"key\": \"value\"}" - assert details.error_code is None - ''' + assert details.reason == Reason.ERROR + assert details.error_code == ErrorCode.PARSE_ERROR + assert details.value == {"val": "control"} def test_boolean_fail(self, client): # attempt to fetch an object treatment as a Boolean. Should result in the default @@ -183,19 +184,7 @@ def test_float_fail(self, client): assert details.error_code == ErrorCode.PARSE_ERROR assert details.reason == Reason.ERROR assert details.variant is None - ''' - def test_obj_fail(self, client): - # attempt to fetch a string treatment as an object. Should result in the default - default_treatment = {"foo": "bar"} - value = client.get_object_value(self.some_other_feature, default_treatment) - assert value == default_treatment - - details = client.get_object_details(self.some_other_feature, default_treatment) - assert details.value == default_treatment - assert details.error_code == ErrorCode.PARSE_ERROR - assert details.reason == Reason.ERROR - assert details.variant is None - ''' + self._destroy_factory() class TestClientInternal(TestClient): @pytest.fixture diff --git a/tests/test_split_client_wrapper.py b/tests/test_split_client_wrapper.py index 561e685..391ff21 100644 --- a/tests/test_split_client_wrapper.py +++ b/tests/test_split_client_wrapper.py @@ -1,5 +1,6 @@ import pytest import unittest +from threading import Event from splitio import get_factory, get_factory_async from split_openfeature import SplitClientWrapper @@ -12,16 +13,26 @@ def test_using_external_splitclient(self): wrapper = SplitClientWrapper({"SplitClient": split_client}) assert wrapper.split_client != None assert wrapper.is_sdk_ready() + + destroy_event = Event() + wrapper.destroy(destroy_event) + destroy_event.wait() + assert split_factory.destroyed def test_using_internal_splitclient(self): wrapper = SplitClientWrapper({"ReadyBlockTime": 1, "SdkKey": "localhost", "ConfigOptions": {"splitFile": "split.yaml"}}) assert wrapper.split_client != None assert wrapper.is_sdk_ready() assert wrapper.sdk_ready == 1 + destroy_event = Event() + wrapper.destroy(destroy_event) + destroy_event.wait() + assert wrapper._factory.destroyed def test_sdk_not_ready(self): wrapper = SplitClientWrapper({"ReadyBlockTime": 0.1, "SdkKey": "api", "ConfigOptions": {}}) assert not wrapper.is_sdk_ready() + wrapper.destroy() def test_invalid_apikey(self): with self.assertRaises(AttributeError) as context: @@ -49,6 +60,8 @@ async def test_using_external_splitclient_async(self): await wrapper.create() assert wrapper.split_client != None assert await wrapper.is_sdk_ready_async() + await wrapper.destroy_async() + assert split_factory.destroyed @pytest.mark.asyncio async def test_using_internal_splitclient_async(self): @@ -57,9 +70,12 @@ async def test_using_internal_splitclient_async(self): assert wrapper.split_client != None assert await wrapper.is_sdk_ready_async() assert wrapper.sdk_ready == True + await wrapper.destroy_async() + assert wrapper._factory.destroyed @pytest.mark.asyncio async def test_sdk_not_ready_async(self): wrapper = SplitClientWrapper({"ReadyBlockTime": 0.1, "SdkKey": "api", "ConfigOptions": {}, "ThreadingMode": "asyncio"}) await wrapper.create() assert not await wrapper.is_sdk_ready_async() + await wrapper.destroy_async() From 8b3ec26ac2f9c577ec96b7761312ca34bd349423 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 4 Nov 2025 12:20:27 -0800 Subject: [PATCH 02/15] polish --- README.md | 9 ++++++++- split_openfeature/split_provider.py | 1 - 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 01db216..26d3694 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,14 @@ from openfeature.evaluation_context import EvaluationContext client = api.get_client("CLIENT_NAME") context = EvaluationContext(targeting_key="TARGETING_KEY") -value = await client.get_boolean_value("FLAG_NAME", False, context) +value = await client.get_boolean_value_async("FLAG_NAME", False, context) +``` +### Logging +Split Provider use `logging` library, Each module has it's own logger, the root being split_provider. Below is an example of simple usage which will set all libraries using `logging` including the provider, to use `DEBUG` mode. +```python +import logging + +logging.basicConfig(level=logging.DEBUG) ``` ### Shutting down Split SDK factory diff --git a/split_openfeature/split_provider.py b/split_openfeature/split_provider.py index 392b977..5c240b1 100644 --- a/split_openfeature/split_provider.py +++ b/split_openfeature/split_provider.py @@ -1,7 +1,6 @@ import typing import logging import json -from threading import Event from openfeature.hook import Hook from openfeature.evaluation_context import EvaluationContext From 59a26dd9aab27dc4251e778537140b1cd1d87915 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 4 Nov 2025 12:24:12 -0800 Subject: [PATCH 03/15] fixed ci --- .github/workflows/ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3936567..a159eb6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,8 +36,7 @@ jobs: pip install -e .[cpphash,redis,uwsgi] - name: Run tests - run: python setup.py install - run: tests/pytest + run: python setup.py install; cd tests; pytest - name: Set VERSION env run: echo "VERSION=$(cat setup.py | grep "version=" | cut -d'"' -f2)" >> $GITHUB_ENV From ecc0560dbfdc5206398a98d95c8843eec4510d6f Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 4 Nov 2025 12:28:52 -0800 Subject: [PATCH 04/15] added installing pytest --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a159eb6..bf1b132 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,6 +34,7 @@ jobs: sudo apt-get install -y libkrb5-dev pip install -U setuptools pip wheel pip install -e .[cpphash,redis,uwsgi] + pip install pytest --quiet - name: Run tests run: python setup.py install; cd tests; pytest From 6832f23b70ed45e74360f1f24804e3f814991dab Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 4 Nov 2025 12:31:15 -0800 Subject: [PATCH 05/15] added install requirements --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bf1b132..787d8bb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,6 +35,7 @@ jobs: pip install -U setuptools pip wheel pip install -e .[cpphash,redis,uwsgi] pip install pytest --quiet + pip install requirements.txt - name: Run tests run: python setup.py install; cd tests; pytest From ff965c6ee06479ef3e96a92cd1d61fa6af4af8fd Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 4 Nov 2025 12:33:27 -0800 Subject: [PATCH 06/15] fixed install requirements --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 787d8bb..5b71ddb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: pip install -U setuptools pip wheel pip install -e .[cpphash,redis,uwsgi] pip install pytest --quiet - pip install requirements.txt + pip install -r requirements.txt - name: Run tests run: python setup.py install; cd tests; pytest From 5dc1922ec3c85b1ed9ecb60bfd7787f5510394a2 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 4 Nov 2025 12:37:16 -0800 Subject: [PATCH 07/15] updated python to 3.9 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5b71ddb..815a96e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v3 with: - python-version: '3.7.16' + python-version: '3.9' - name: Install dependencies run: | From ab43c0d0f36aa42ae0acb9d9c47dc8245de63fa5 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 4 Nov 2025 12:39:52 -0800 Subject: [PATCH 08/15] added install mock --- .github/workflows/ci.yml | 1 + README.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 815a96e..90ea4de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,6 +35,7 @@ jobs: pip install -U setuptools pip wheel pip install -e .[cpphash,redis,uwsgi] pip install pytest --quiet + pip install mock pip install -r requirements.txt - name: Run tests diff --git a/README.md b/README.md index 26d3694..3a5b1f9 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ 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. ## Compatibility -This SDK is compatible with Python 3.7.16 and higher. +This SDK is compatible with Python 3.9 and higher. ## Getting started ### Pip Installation From e57ac02f14818ffbec5661dc74bc869572accf8d Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 4 Nov 2025 12:43:14 -0800 Subject: [PATCH 09/15] added pytest-asyncio --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 90ea4de..3828f81 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,6 +36,7 @@ jobs: pip install -e .[cpphash,redis,uwsgi] pip install pytest --quiet pip install mock + pip install pytest-asyncio pip install -r requirements.txt - name: Run tests From 6ca043b15f4d4bebe67c3f8fe0b8e3d02ae65439 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 4 Nov 2025 12:49:26 -0800 Subject: [PATCH 10/15] updated python to 3.9.13 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3828f81..ee13f3b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v3 with: - python-version: '3.9' + python-version: '3.9.13' - name: Install dependencies run: | From bdf050695e3329afeb64b59cce8ae2d4e207be27 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 4 Nov 2025 14:39:45 -0800 Subject: [PATCH 11/15] added asyncio option in split sdk requirement --- requirements.txt | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index cce7b2a..42865f3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ openfeature_sdk==0.8.3 -splitio_client==10.5.1 +splitio_client[cpphash,asyncio]==10.5.1 diff --git a/setup.py b/setup.py index 7e70095..bd05f97 100644 --- a/setup.py +++ b/setup.py @@ -15,5 +15,5 @@ "Programming Language :: Python :: 3", 'Topic :: Software Development :: Libraries' ], - python_requires='>=3.5' + python_requires='>=3.9' ) From 9e51e6d4457688ea9e0da7d48cb933e8a22f80af Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 4 Nov 2025 14:51:14 -0800 Subject: [PATCH 12/15] added sonar properties --- .coveragerc | 31 +++++++++++++++++++++++++++++++ sonar-project.properties | 10 ++++++++++ 2 files changed, 41 insertions(+) create mode 100644 .coveragerc create mode 100644 sonar-project.properties diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..c6294d3 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,31 @@ +[run] +source = + splitio/ + +omit = + tests/* + */__init__.py + +branch = True + +relative_files = True + +[report] +# Regexes for lines to exclude from consideration +exclude_lines = + # Have to re-enable the standard pragma + pragma: no cover + + # Don't complain about missing debug-only code: + def __repr__ + if self\.debug + + # Don't complain if tests don't hit defensive assertion code: + raise AssertionError + raise NotImplementedError + + # Don't complain if non-runnable code isn't run: + if 0: + if __name__ == .__main__.: + +precision = 2 diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..45afccb --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,10 @@ +sonar.projectName=split_openfeature +sonar.projectKey=split_openfeature +sonar.python.version=3.9 +sonar.sources=splitio +sonar.tests=tests +sonar.text.excluded.file.suffixes=.csv +sonar.python.coverage.reportPaths=coverage.xml +sonar.coverage.exclusions=**/__init__.py +sonar.links.ci=https://github.com/splitio/split-openfeature-provider-python +sonar.links.scm=https://github.com/splitio/split-openfeature-provider-python/actions From f266f16175bb38db3eeac1b2d9d0ddcfa4b43d6e Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 4 Nov 2025 15:03:19 -0800 Subject: [PATCH 13/15] polish --- .github/workflows/ci.yml | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ee13f3b..2598dd3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,7 @@ jobs: pip install -r requirements.txt - name: Run tests - run: python setup.py install; cd tests; pytest + run: cd tests; pytest -v - name: Set VERSION env run: echo "VERSION=$(cat setup.py | grep "version=" | cut -d'"' -f2)" >> $GITHUB_ENV diff --git a/requirements.txt b/requirements.txt index 42865f3..b4ddeda 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ openfeature_sdk==0.8.3 -splitio_client[cpphash,asyncio]==10.5.1 +splitio_client[cpphash,asyncio]==10.5.1 \ No newline at end of file From 4e5e09bdf28e340a2b0d61cb9599e00f2c85dc78 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 4 Nov 2025 15:10:36 -0800 Subject: [PATCH 14/15] fixed test --- tests/test_split_provider.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_split_provider.py b/tests/test_split_provider.py index 08a7115..265b288 100644 --- a/tests/test_split_provider.py +++ b/tests/test_split_provider.py @@ -575,4 +575,5 @@ async def test_sdk_not_ready(self): await provider.create() details = await provider.resolve_boolean_details_async(self.flag_name, False, self.eval_context) assert details.error_code == ErrorCode.PROVIDER_NOT_READY - assert details.value == False \ No newline at end of file + assert details.value == False + await provider._split_client_wrapper._factory.destroy() \ No newline at end of file From 1d0de7c900152cb55f33794e62bc33a2800f2884 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Tue, 4 Nov 2025 15:14:50 -0800 Subject: [PATCH 15/15] fixed source in sonar --- sonar-project.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonar-project.properties b/sonar-project.properties index 45afccb..6bfbc8c 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,7 +1,7 @@ sonar.projectName=split_openfeature sonar.projectKey=split_openfeature sonar.python.version=3.9 -sonar.sources=splitio +sonar.sources=split_openfeature sonar.tests=tests sonar.text.excluded.file.suffixes=.csv sonar.python.coverage.reportPaths=coverage.xml