Skip to content

Commit 5610069

Browse files
authored
Merge pull request #591 from splitio/development
Release 10.4.0
2 parents 0e87c99 + 9180c03 commit 5610069

23 files changed

+957
-480
lines changed

CHANGES.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
10.4.0 (Aug 4, 2025)
2+
- Added a new optional argument to the client `getTreatment` methods to allow passing additional evaluation options, such as a map of properties to append to the generated impressions sent to Split backend. Read more in our docs.
3+
14
10.3.0 (Jun 17, 2025)
25
- Added support for rule-based segments. These segments determine membership at runtime by evaluating their configured rules against the user attributes provided to the SDK.
36
- Added support for feature flag prerequisites. This allows customers to define dependency conditions between flags, which are evaluated before any allowlists or targeting rules.

splitio/api/impressions.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,7 @@ def _build_bulk(impressions):
3030
{
3131
'f': test_name,
3232
'i': [
33-
{
34-
'k': impression.matching_key,
35-
't': impression.treatment,
36-
'm': impression.time,
37-
'c': impression.change_number,
38-
'r': impression.label,
39-
'b': impression.bucketing_key,
40-
'pt': impression.previous_time
41-
}
33+
ImpressionsAPIBase._filter_out_null_prop(impression)
4234
for impression in imps
4335
]
4436
}
@@ -48,6 +40,30 @@ def _build_bulk(impressions):
4840
)
4941
]
5042

43+
@staticmethod
44+
def _filter_out_null_prop(impression):
45+
if impression.properties == None:
46+
return {
47+
'k': impression.matching_key,
48+
't': impression.treatment,
49+
'm': impression.time,
50+
'c': impression.change_number,
51+
'r': impression.label,
52+
'b': impression.bucketing_key,
53+
'pt': impression.previous_time
54+
}
55+
56+
return {
57+
'k': impression.matching_key,
58+
't': impression.treatment,
59+
'm': impression.time,
60+
'c': impression.change_number,
61+
'r': impression.label,
62+
'b': impression.bucketing_key,
63+
'pt': impression.previous_time,
64+
'properties': impression.properties
65+
}
66+
5167
@staticmethod
5268
def _build_counters(counters):
5369
"""

splitio/client/client.py

Lines changed: 175 additions & 98 deletions
Large diffs are not rendered by default.

splitio/client/input_validator.py

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import inspect
77

88
from splitio.client.key import Key
9+
from splitio.client import client
910
from splitio.engine.evaluator import CONTROL
1011

1112

@@ -538,6 +539,15 @@ def validate_attributes(attributes, method_name):
538539

539540
return True
540541

542+
def validate_evaluation_options(evaluation_options, method_name):
543+
if evaluation_options == None:
544+
return None
545+
546+
if not isinstance(evaluation_options, client.EvaluationOptions):
547+
_LOGGER.error("%s: evaluation options should be an instance of EvaluationOptions. Setting its value to None.", method_name)
548+
return None
549+
550+
return evaluation_options
541551

542552
class _ApiLogFilter(logging.Filter): # pylint: disable=too-few-public-methods
543553
def filter(self, record):
@@ -564,7 +574,7 @@ def validate_factory_instantiation(sdk_key):
564574
return True
565575

566576

567-
def valid_properties(properties):
577+
def valid_properties(properties, source):
568578
"""
569579
Check if properties is a valid dict and returns the properties
570580
that will be sent to the track method, avoiding unexpected types.
@@ -580,7 +590,7 @@ def valid_properties(properties):
580590
return True, None, size
581591

582592
if not isinstance(properties, dict):
583-
_LOGGER.error('track: properties must be of type dictionary.')
593+
_LOGGER.error('%s: properties must be of type dictionary.', source)
584594
return False, None, 0
585595

586596
valid_properties = dict()
@@ -595,9 +605,8 @@ def valid_properties(properties):
595605
if element is None:
596606
continue
597607

598-
if not isinstance(element, str) and not isinstance(element, Number) \
599-
and not isinstance(element, bool):
600-
_LOGGER.warning('Property %s is of invalid type. Setting value to None', element)
608+
if not _check_element_type(element):
609+
_LOGGER.warning('%s: Property %s is of invalid type. Setting value to None', source, element)
601610
element = None
602611

603612
valid_properties[property] = element
@@ -607,16 +616,22 @@ def valid_properties(properties):
607616

608617
if size > MAX_PROPERTIES_LENGTH_BYTES:
609618
_LOGGER.error(
610-
'The maximum size allowed for the properties is 32768 bytes. ' +
611-
'Current one is ' + str(size) + ' bytes. Event not queued'
612-
)
619+
'%s: The maximum size allowed for the properties is 32768 bytes. ' +
620+
'Current one is ' + str(size) + ' bytes. Event not queued', source)
613621
return False, None, size
614622

615623
if len(valid_properties.keys()) > 300:
616-
_LOGGER.warning('Event has more than 300 properties. Some of them will be trimmed' +
617-
' when processed')
624+
_LOGGER.warning('%s: Event has more than 300 properties. Some of them will be trimmed' +
625+
' when processed', source)
618626
return True, valid_properties if len(valid_properties) else None, size
619627

628+
def _check_element_type(element):
629+
if not isinstance(element, str) and not isinstance(element, Number) \
630+
and not isinstance(element, bool):
631+
return False
632+
633+
return True
634+
620635
def validate_pluggable_adapter(config):
621636
"""
622637
Check if pluggable adapter contains the expected method signature

splitio/engine/impressions/adapters.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ async def record_unique_keys(self, uniques):
8989
:param uniques: unique keys disctionary
9090
:type uniques: Dictionary {'feature_flag1': set(), 'feature_flag2': set(), .. }
9191
"""
92+
if len(uniques) == 0:
93+
return
94+
9295
await self._telemtry_http_client.record_unique_keys({'keys': self._uniques_formatter(uniques)})
9396

9497

@@ -184,6 +187,9 @@ async def record_unique_keys(self, uniques):
184187
:param uniques: unique keys disctionary
185188
:type uniques: Dictionary {'feature_flag1': set(), 'feature_flag2': set(), .. }
186189
"""
190+
if len(uniques) == 0:
191+
return True
192+
187193
bulk_mtks = _uniques_formatter(uniques)
188194
try:
189195
inserted = await self._redis_client.rpush(_MTK_QUEUE_KEY, *bulk_mtks)
@@ -202,6 +208,9 @@ async def flush_counters(self, to_send):
202208
:param to_send: unique keys disctionary
203209
:type to_send: Dictionary {'feature_flag1': set(), 'feature_flag2': set(), .. }
204210
"""
211+
if len(to_send) == 0:
212+
return True
213+
205214
try:
206215
resulted = 0
207216
counted = 0
@@ -277,6 +286,7 @@ def flush_counters(self, to_send):
277286
"""
278287
if len(to_send) == 0:
279288
return
289+
280290
try:
281291
resulted = 0
282292
for pf_count in to_send:
@@ -325,6 +335,9 @@ async def record_unique_keys(self, uniques):
325335
:param uniques: unique keys disctionary
326336
:type uniques: Dictionary {'feature_flag1': set(), 'feature_flag2': set(), .. }
327337
"""
338+
if len(uniques) == 0:
339+
return True
340+
328341
bulk_mtks = _uniques_formatter(uniques)
329342
try:
330343
inserted = await self._adapter_client.push_items(self._prefix + _MTK_QUEUE_KEY, *bulk_mtks)
@@ -343,6 +356,9 @@ async def flush_counters(self, to_send):
343356
:param to_send: unique keys disctionary
344357
:type to_send: Dictionary {'feature_flag1': set(), 'feature_flag2': set(), .. }
345358
"""
359+
if len(to_send) == 0:
360+
return True
361+
346362
try:
347363
resulted = 0
348364
for pf_count in to_send:

splitio/engine/impressions/strategies.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,14 @@ def process_impressions(self, impressions):
3838
:returns: Tuple of to be stored, observed and counted impressions, and unique keys tuple
3939
:rtype: list[tuple[splitio.models.impression.Impression, dict]], list[], list[], list[]
4040
"""
41-
imps = [(self._observer.test_and_set(imp), attrs) for imp, attrs in impressions]
41+
imps = []
42+
for imp, attrs in impressions:
43+
if imp.properties is not None:
44+
imps.append((imp, attrs))
45+
continue
46+
47+
imps.append((self._observer.test_and_set(imp), attrs))
48+
4249
return [i for i, _ in imps], imps, [], []
4350

4451
class StrategyNoneMode(BaseStrategy):
@@ -85,7 +92,14 @@ def process_impressions(self, impressions):
8592
:returns: Tuple of to be stored, observed and counted impressions, and unique keys tuple
8693
:rtype: list[tuple[splitio.models.impression.Impression, dict]], list[splitio.models.impression.Impression], list[splitio.models.impression.Impression], list[]
8794
"""
88-
imps = [(self._observer.test_and_set(imp), attrs) for imp, attrs in impressions]
95+
imps = []
96+
for imp, attrs in impressions:
97+
if imp.properties is not None:
98+
imps.append((imp, attrs))
99+
continue
100+
101+
imps.append((self._observer.test_and_set(imp), attrs))
102+
89103
counter_imps = [imp for imp, _ in imps if imp.previous_time != None]
90104
this_hour = truncate_time(utctime_ms())
91105
return [i for i, _ in imps if i.previous_time is None or i.previous_time < this_hour], imps, counter_imps, []

splitio/models/impressions.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
'change_number',
1313
'bucketing_key',
1414
'time',
15-
'previous_time'
15+
'previous_time',
16+
'properties'
1617
]
1718
)
1819

splitio/storage/pluggable.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,6 +1231,7 @@ def _wrap_impressions(self, impressions):
12311231
'r': impression.label,
12321232
'c': impression.change_number,
12331233
'm': impression.time,
1234+
'properties': impression.properties
12341235
}
12351236
}
12361237
bulk_impressions.append(json.dumps(to_store))

splitio/storage/redis.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,6 +1100,7 @@ def _wrap_impressions(self, impressions):
11001100
'r': impression.label,
11011101
'c': impression.change_number,
11021102
'm': impression.time,
1103+
'properties': impression.properties
11031104
}
11041105
}
11051106
bulk_impressions.append(json.dumps(to_store))

splitio/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '10.3.0'
1+
__version__ = '10.4.0'

0 commit comments

Comments
 (0)