Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
10.5.1 (Oct 15, 2025)
- Added using String only parameter for treatments in FallbackTreatmentConfiguration class.

10.5.0 (Sep 15, 2025)
- Changed the log level from error to debug when renewing the token for Streaming service in asyncio mode.
- Added new configuration for Fallback Treatments, which allows setting a treatment value and optional config to be returned in place of "control", either globally or by flag. Read more in our docs.
Expand Down
25 changes: 22 additions & 3 deletions splitio/models/fallback_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ def __init__(self, global_fallback_treatment=None, by_flag_fallback_treatment=No
:param by_flag_fallback_treatment: Dict of flags and their fallback treatment
:type by_flag_fallback_treatment: {str: FallbackTreatment}
"""
self._global_fallback_treatment = global_fallback_treatment
self._by_flag_fallback_treatment = by_flag_fallback_treatment
self._global_fallback_treatment = self._build_global_fallback(global_fallback_treatment)
self._by_flag_fallback_treatment = self._build_by_flag_fallback(by_flag_fallback_treatment)

@property
def global_fallback_treatment(self):
Expand All @@ -37,7 +37,26 @@ def by_flag_fallback_treatment(self):
def by_flag_fallback_treatment(self, new_value):
"""Set global fallback treatment."""
self.by_flag_fallback_treatment = new_value


def _build_global_fallback(self, global_fallback_treatment):
if isinstance(global_fallback_treatment, str):
return FallbackTreatment(global_fallback_treatment)

return global_fallback_treatment

def _build_by_flag_fallback(self, by_flag_fallback_treatment):
if not isinstance(by_flag_fallback_treatment, dict):
return by_flag_fallback_treatment

parsed_by_flag_fallback = {}
for key, value in by_flag_fallback_treatment.items():
if isinstance(value, str):
parsed_by_flag_fallback[key] = FallbackTreatment(value)
else:
parsed_by_flag_fallback[key] = value

return parsed_by_flag_fallback

class FallbackTreatmentCalculator(object):
"""FallbackTreatmentCalculator object class."""

Expand Down
2 changes: 1 addition & 1 deletion splitio/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '10.5.0'
__version__ = '10.5.1'
38 changes: 38 additions & 0 deletions tests/integration/test_client_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -1393,6 +1393,27 @@ def test_localhost_e2e(self):
factory.destroy(event)
event.wait()

def test_fallback_treatments(self):
"""Instantiate a client with a JSON file and issue get_treatment() calls."""
self._update_temp_file(splits_json['splitChange2_1'])
filename = os.path.join(os.path.dirname(__file__), 'files', 'split_changes_temp.json')
factory = get_factory('localhost',
config={
'splitFile': filename,
'fallbackTreatments': FallbackTreatmentsConfiguration("on-global", {'fallback_feature': "on-local"})
}
)
factory.block_until_ready(1)
client = factory.client()

assert client.get_treatment("key", "feature") == "on-global"
assert client.get_treatment("key", "fallback_feature") == "on-local"

event = threading.Event()
factory.destroy(event)
event.wait()


class PluggableIntegrationTests(object):
"""Pluggable storage-based integration tests."""

Expand Down Expand Up @@ -3335,6 +3356,23 @@ async def test_localhost_e2e(self):
assert split.configs == {}
await factory.destroy()

@pytest.mark.asyncio
async def test_fallback_treatments(self):
"""Instantiate a client with a JSON file and issue get_treatment() calls."""
self._update_temp_file(splits_json['splitChange2_1'])
filename = os.path.join(os.path.dirname(__file__), 'files', 'split_changes_temp.json')
factory = await get_factory_async('localhost',
config={
'splitFile': filename,
'fallbackTreatments': FallbackTreatmentsConfiguration("on-global", {'fallback_feature': "on-local"})
}
)
await factory.block_until_ready(1)
client = factory.client()

assert await client.get_treatment("key", "feature") == "on-global"
assert await client.get_treatment("key", "fallback_feature") == "on-local"
await factory.destroy()

class PluggableIntegrationAsyncTests(object):
"""Pluggable storage-based integration tests."""
Expand Down
7 changes: 7 additions & 0 deletions tests/models/test_fallback.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ def test_working(self):

fallback_config.by_flag_fallback_treatment["flag2"] = flag_fb
assert fallback_config.by_flag_fallback_treatment == {"flag1": flag_fb, "flag2": flag_fb}

fallback_config = FallbackTreatmentsConfiguration("on", {"flag1": "off"})
assert isinstance(fallback_config.global_fallback_treatment, FallbackTreatment)
assert fallback_config.global_fallback_treatment.treatment == "on"

assert isinstance(fallback_config.by_flag_fallback_treatment["flag1"], FallbackTreatment)
assert fallback_config.by_flag_fallback_treatment["flag1"].treatment == "off"


class FallbackTreatmentCalculatorTests(object):
Expand Down