splitio-client 9.6.1__tar.gz → 9.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. splitio_client-9.7.0/LICENSE.txt +13 -0
  2. {splitio_client-9.6.1 → splitio_client-9.7.0}/PKG-INFO +2 -2
  3. {splitio_client-9.6.1 → splitio_client-9.7.0}/README.md +1 -1
  4. {splitio_client-9.6.1 → splitio_client-9.7.0}/setup.py +9 -10
  5. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/api/auth.py +2 -1
  6. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/api/commons.py +14 -5
  7. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/impressions/adapters.py +14 -0
  8. splitio_client-9.7.0/splitio/models/__init__.py +6 -0
  9. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/grammar/condition.py +2 -0
  10. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/grammar/matchers/__init__.py +15 -3
  11. splitio_client-9.7.0/splitio/models/grammar/matchers/semver.py +260 -0
  12. splitio_client-9.7.0/splitio/models/grammar/matchers/utils/utils.py +168 -0
  13. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/splits.py +38 -1
  14. splitio_client-9.7.0/splitio/spec.py +1 -0
  15. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/storage/pluggable.py +1 -1
  16. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/storage/redis.py +2 -2
  17. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/sync/segment.py +2 -2
  18. splitio_client-9.7.0/splitio/version.py +1 -0
  19. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio_client.egg-info/PKG-INFO +2 -2
  20. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio_client.egg-info/SOURCES.txt +34 -1
  21. splitio_client-9.7.0/splitio_client.egg-info/requires.txt +24 -0
  22. splitio_client-9.7.0/tests/__init__.py +0 -0
  23. splitio_client-9.7.0/tests/helpers/__init__.py +9 -0
  24. splitio_client-9.7.0/tests/helpers/mockserver.py +249 -0
  25. splitio_client-9.7.0/tests/integration/__init__.py +42 -0
  26. splitio_client-9.7.0/tests/integration/test_client_e2e.py +1652 -0
  27. splitio_client-9.7.0/tests/integration/test_redis_integration.py +250 -0
  28. splitio_client-9.7.0/tests/integration/test_streaming_e2e.py +1478 -0
  29. splitio_client-9.7.0/tests/push/__init__.py +0 -0
  30. splitio_client-9.7.0/tests/push/test_manager.py +252 -0
  31. splitio_client-9.7.0/tests/push/test_parser.py +116 -0
  32. splitio_client-9.7.0/tests/push/test_processor.py +58 -0
  33. splitio_client-9.7.0/tests/push/test_segment_worker.py +60 -0
  34. splitio_client-9.7.0/tests/push/test_split_worker.py +201 -0
  35. splitio_client-9.7.0/tests/push/test_splitsse.py +126 -0
  36. splitio_client-9.7.0/tests/push/test_sse.py +125 -0
  37. splitio_client-9.7.0/tests/push/test_status_tracker.py +195 -0
  38. splitio_client-9.7.0/tests/recorder/__init__.py +0 -0
  39. splitio_client-9.7.0/tests/recorder/test_recorder.py +79 -0
  40. splitio_client-9.7.0/tests/sync/__init__.py +0 -0
  41. splitio_client-9.7.0/tests/sync/test_events_synchronizer.py +68 -0
  42. splitio_client-9.7.0/tests/sync/test_impressions_count_synchronizer.py +38 -0
  43. splitio_client-9.7.0/tests/sync/test_impressions_synchronizer.py +68 -0
  44. splitio_client-9.7.0/tests/sync/test_manager.py +123 -0
  45. splitio_client-9.7.0/tests/sync/test_segments_synchronizer.py +358 -0
  46. splitio_client-9.7.0/tests/sync/test_splits_synchronizer.py +670 -0
  47. splitio_client-9.7.0/tests/sync/test_synchronizer.py +445 -0
  48. splitio_client-9.7.0/tests/sync/test_telemetry.py +150 -0
  49. splitio_client-9.7.0/tests/sync/test_unique_keys_sync.py +54 -0
  50. splitio_client-9.6.1/splitio/version.py +0 -1
  51. splitio_client-9.6.1/splitio_client.egg-info/requires.txt +0 -27
  52. {splitio_client-9.6.1 → splitio_client-9.7.0}/setup.cfg +0 -0
  53. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/__init__.py +0 -0
  54. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/api/__init__.py +0 -0
  55. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/api/client.py +0 -0
  56. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/api/events.py +0 -0
  57. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/api/impressions.py +0 -0
  58. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/api/segments.py +0 -0
  59. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/api/splits.py +0 -0
  60. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/api/telemetry.py +0 -0
  61. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/client/__init__.py +0 -0
  62. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/client/client.py +0 -0
  63. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/client/config.py +0 -0
  64. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/client/factory.py +0 -0
  65. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/client/input_validator.py +0 -0
  66. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/client/key.py +0 -0
  67. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/client/listener.py +0 -0
  68. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/client/localhost.py +0 -0
  69. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/client/manager.py +0 -0
  70. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/client/util.py +0 -0
  71. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/__init__.py +0 -0
  72. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/cache/__init__.py +0 -0
  73. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/cache/lru.py +0 -0
  74. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/evaluator.py +0 -0
  75. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/filters.py +0 -0
  76. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/hashfns/__init__.py +0 -0
  77. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/hashfns/legacy.py +0 -0
  78. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/hashfns/murmur3py.py +0 -0
  79. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/impressions/__init__.py +0 -0
  80. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/impressions/impressions.py +0 -0
  81. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/impressions/manager.py +0 -0
  82. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/impressions/strategies.py +0 -0
  83. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/impressions/unique_keys_tracker.py +0 -0
  84. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/splitters.py +0 -0
  85. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/telemetry.py +0 -0
  86. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/exceptions.py +0 -0
  87. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/factories.py +0 -0
  88. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/impressions.py +0 -0
  89. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/key.py +0 -0
  90. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/datatypes.py +0 -0
  91. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/events.py +0 -0
  92. {splitio_client-9.6.1/splitio/models → splitio_client-9.7.0/splitio/models/grammar}/__init__.py +0 -0
  93. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/grammar/matchers/base.py +0 -0
  94. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/grammar/matchers/keys.py +0 -0
  95. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/grammar/matchers/misc.py +0 -0
  96. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/grammar/matchers/numeric.py +0 -0
  97. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/grammar/matchers/sets.py +0 -0
  98. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/grammar/matchers/string.py +0 -0
  99. {splitio_client-9.6.1/splitio/models/grammar → splitio_client-9.7.0/splitio/models/grammar/matchers/utils}/__init__.py +0 -0
  100. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/grammar/partitions.py +0 -0
  101. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/impressions.py +0 -0
  102. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/notification.py +0 -0
  103. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/segments.py +0 -0
  104. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/telemetry.py +0 -0
  105. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/token.py +0 -0
  106. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/push/__init__.py +0 -0
  107. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/push/manager.py +0 -0
  108. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/push/parser.py +0 -0
  109. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/push/processor.py +0 -0
  110. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/push/segmentworker.py +0 -0
  111. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/push/splitsse.py +0 -0
  112. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/push/splitworker.py +0 -0
  113. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/push/sse.py +0 -0
  114. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/push/status_tracker.py +0 -0
  115. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/recorder/__init__.py +0 -0
  116. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/recorder/recorder.py +0 -0
  117. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/storage/__init__.py +0 -0
  118. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/storage/adapters/__init__.py +0 -0
  119. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/storage/adapters/cache_trait.py +0 -0
  120. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/storage/adapters/redis.py +0 -0
  121. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/storage/adapters/util.py +0 -0
  122. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/storage/inmemmory.py +0 -0
  123. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/sync/__init__.py +0 -0
  124. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/sync/event.py +0 -0
  125. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/sync/impression.py +0 -0
  126. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/sync/manager.py +0 -0
  127. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/sync/split.py +0 -0
  128. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/sync/synchronizer.py +0 -0
  129. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/sync/telemetry.py +0 -0
  130. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/sync/unique_keys.py +0 -0
  131. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/sync/util.py +0 -0
  132. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/tasks/__init__.py +0 -0
  133. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/tasks/events_sync.py +0 -0
  134. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/tasks/impressions_sync.py +0 -0
  135. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/tasks/segment_sync.py +0 -0
  136. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/tasks/split_sync.py +0 -0
  137. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/tasks/telemetry_sync.py +0 -0
  138. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/tasks/unique_keys_sync.py +0 -0
  139. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/tasks/util/__init__.py +0 -0
  140. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/tasks/util/asynctask.py +0 -0
  141. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/tasks/util/workerpool.py +0 -0
  142. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/util/__init__.py +0 -0
  143. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/util/backoff.py +0 -0
  144. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/util/decorators.py +0 -0
  145. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/util/storage_helper.py +0 -0
  146. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/util/threadutil.py +0 -0
  147. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/util/time.py +0 -0
  148. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio_client.egg-info/dependency_links.txt +0 -0
  149. {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio_client.egg-info/top_level.txt +0 -0
@@ -0,0 +1,13 @@
1
+ Copyright © 2024 Split Software, Inc.
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: splitio_client
3
- Version: 9.6.1
3
+ Version: 9.7.0
4
4
  Summary: Split.io Python Client
5
5
  Home-page: https://github.com/splitio/python-client
6
6
  Author: Patricio Echague, Sebastian Arrubia
7
7
  Author-email: pato@split.io, sebastian@split.io
8
8
  License: Apache License 2.0
9
- Download-URL: https://github.com/splitio/python-client/tarball/9.6.1
9
+ Download-URL: https://github.com/splitio/python-client/tarball/9.7.0
10
10
  Description: UNKNOWN
11
11
  Platform: UNKNOWN
12
12
  Classifier: Environment :: Console
@@ -7,7 +7,7 @@ This SDK is designed to work with Split, the platform for controlled rollouts, w
7
7
  [![Twitter Follow](https://img.shields.io/twitter/follow/splitsoftware.svg?style=social&label=Follow&maxAge=1529000)](https://twitter.com/intent/follow?screen_name=splitsoftware)
8
8
 
9
9
  ## Compatibility
10
- This SDK is compatible with **Python 3 and higher**.
10
+ This SDK is compatible with **Python 3.7 and higher**.
11
11
 
12
12
  ## Getting started
13
13
  Below is a simple example that describes the instantiation and most basic usage of our SDK:
@@ -6,21 +6,20 @@ from setuptools import setup, find_packages
6
6
 
7
7
  TESTS_REQUIRES = [
8
8
  'flake8',
9
- 'pytest==7.0.1',
10
- 'pytest-mock>=3.5.1',
11
- 'coverage==6.2',
9
+ 'pytest==7.1.0',
10
+ 'pytest-mock==3.11.1',
11
+ 'coverage==7.2.7',
12
12
  'pytest-cov',
13
- 'importlib-metadata==4.2',
14
- 'tomli==1.2.3',
15
- 'iniconfig==1.1.1',
16
- 'attrs==22.1.0'
13
+ 'importlib-metadata==6.7',
14
+ 'tomli',
15
+ 'iniconfig',
16
+ 'attrs'
17
17
  ]
18
18
 
19
19
  INSTALL_REQUIRES = [
20
- 'requests>=2.9.1',
21
- 'pyyaml>=5.4',
20
+ 'requests',
21
+ 'pyyaml',
22
22
  'docopt>=0.6.2',
23
- 'enum34;python_version<"3.4"',
24
23
  'bloom-filter2>=2.0.0'
25
24
  ]
26
25
 
@@ -5,6 +5,7 @@ import json
5
5
 
6
6
  from splitio.api import APIException
7
7
  from splitio.api.commons import headers_from_metadata, record_telemetry
8
+ from splitio.spec import SPEC_VERSION
8
9
  from splitio.util.time import get_current_epoch_time_ms
9
10
  from splitio.api.client import HttpClientException
10
11
  from splitio.models.token import from_raw
@@ -43,7 +44,7 @@ class AuthAPI(object): # pylint: disable=too-few-public-methods
43
44
  try:
44
45
  response = self._client.get(
45
46
  'auth',
46
- '/v2/auth',
47
+ '/v2/auth?s=' + SPEC_VERSION,
47
48
  self._sdk_key,
48
49
  extra_headers=self._metadata,
49
50
  )
@@ -1,10 +1,10 @@
1
1
  """Commons module."""
2
2
  from splitio.util.time import get_current_epoch_time_ms
3
+ from splitio.spec import SPEC_VERSION
3
4
 
4
5
  _CACHE_CONTROL = 'Cache-Control'
5
6
  _CACHE_CONTROL_NO_CACHE = 'no-cache'
6
7
 
7
-
8
8
  def headers_from_metadata(sdk_metadata, client_key=None):
9
9
  """
10
10
  Generate a dict with headers required by data-recording API endpoints.
@@ -57,7 +57,7 @@ def record_telemetry(status_code, elapsed, metric_name, telemetry_runtime_produc
57
57
  class FetchOptions(object):
58
58
  """Fetch Options object."""
59
59
 
60
- def __init__(self, cache_control_headers=False, change_number=None, sets=None):
60
+ def __init__(self, cache_control_headers=False, change_number=None, sets=None, spec=SPEC_VERSION):
61
61
  """
62
62
  Class constructor.
63
63
 
@@ -73,6 +73,7 @@ class FetchOptions(object):
73
73
  self._cache_control_headers = cache_control_headers
74
74
  self._change_number = change_number
75
75
  self._sets = sets
76
+ self._spec = spec
76
77
 
77
78
  @property
78
79
  def cache_control_headers(self):
@@ -89,6 +90,11 @@ class FetchOptions(object):
89
90
  """Return sets."""
90
91
  return self._sets
91
92
 
93
+ @property
94
+ def spec(self):
95
+ """Return sets."""
96
+ return self._spec
97
+
92
98
  def __eq__(self, other):
93
99
  """Match between other options."""
94
100
  if self._cache_control_headers != other._cache_control_headers:
@@ -97,6 +103,8 @@ class FetchOptions(object):
97
103
  return False
98
104
  if self._sets != other._sets:
99
105
  return False
106
+ if self._spec != other._spec:
107
+ return False
100
108
  return True
101
109
 
102
110
 
@@ -116,14 +124,15 @@ def build_fetch(change_number, fetch_options, metadata):
116
124
  :return: Objects for fetch
117
125
  :rtype: dict, dict
118
126
  """
119
- query = {'since': change_number}
127
+ query = {'s': fetch_options.spec} if fetch_options.spec is not None else {}
128
+ query['since'] = change_number
120
129
  extra_headers = metadata
121
130
  if fetch_options is None:
122
131
  return query, extra_headers
123
132
  if fetch_options.cache_control_headers:
124
133
  extra_headers[_CACHE_CONTROL] = _CACHE_CONTROL_NO_CACHE
125
- if fetch_options.change_number is not None:
126
- query['till'] = fetch_options.change_number
127
134
  if fetch_options.sets is not None:
128
135
  query['sets'] = fetch_options.sets
136
+ if fetch_options.change_number is not None:
137
+ query['till'] = fetch_options.change_number
129
138
  return query, extra_headers
@@ -40,6 +40,9 @@ class InMemorySenderAdapter(ImpressionsSenderAdapter):
40
40
  :param uniques: unique keys disctionary
41
41
  :type uniques: Dictionary {'feature_flag1': set(), 'feature_flag2': set(), .. }
42
42
  """
43
+ if len(uniques) == 0:
44
+ return
45
+
43
46
  self._telemtry_http_client.record_unique_keys({'keys': self._uniques_formatter(uniques)})
44
47
 
45
48
  def _uniques_formatter(self, uniques):
@@ -73,6 +76,9 @@ class RedisSenderAdapter(ImpressionsSenderAdapter):
73
76
  :param uniques: unique keys disctionary
74
77
  :type uniques: Dictionary {'feature_flag1': set(), 'feature_flag2': set(), .. }
75
78
  """
79
+ if len(uniques) == 0:
80
+ return
81
+
76
82
  bulk_mtks = _uniques_formatter(uniques)
77
83
  try:
78
84
  inserted = self._redis_client.rpush(_MTK_QUEUE_KEY, *bulk_mtks)
@@ -90,6 +96,9 @@ class RedisSenderAdapter(ImpressionsSenderAdapter):
90
96
  :param to_send: unique keys disctionary
91
97
  :type to_send: Dictionary {'feature_flag1': set(), 'feature_flag2': set(), .. }
92
98
  """
99
+ if len(to_send) == 0:
100
+ return
101
+
93
102
  try:
94
103
  resulted = 0
95
104
  counted = 0
@@ -140,6 +149,9 @@ class PluggableSenderAdapter(ImpressionsSenderAdapter):
140
149
  :param uniques: unique keys disctionary
141
150
  :type uniques: Dictionary {'feature_flag1': set(), 'feature_flag2': set(), .. }
142
151
  """
152
+ if len(uniques) == 0:
153
+ return
154
+
143
155
  bulk_mtks = _uniques_formatter(uniques)
144
156
  try:
145
157
  _LOGGER.debug("record_unique_keys")
@@ -159,6 +171,8 @@ class PluggableSenderAdapter(ImpressionsSenderAdapter):
159
171
  :param to_send: unique keys disctionary
160
172
  :type to_send: Dictionary {'feature_flag1': set(), 'feature_flag2': set(), .. }
161
173
  """
174
+ if len(to_send) == 0:
175
+ return
162
176
  try:
163
177
  resulted = 0
164
178
  for pf_count in to_send:
@@ -0,0 +1,6 @@
1
+ class MatcherNotFoundException(Exception):
2
+ """Exception to raise when a matcher is not found."""
3
+
4
+ def __init__(self, custom_message):
5
+ """Constructor."""
6
+ Exception.__init__(self, custom_message)
@@ -2,6 +2,7 @@
2
2
 
3
3
  from enum import Enum
4
4
 
5
+ from splitio.models import MatcherNotFoundException
5
6
  from splitio.models.grammar import matchers
6
7
  from splitio.models.grammar import partitions
7
8
 
@@ -124,6 +125,7 @@ def from_raw(raw_condition):
124
125
  ]
125
126
 
126
127
  matcher_objects = [matchers.from_raw(x) for x in raw_condition['matcherGroup']['matchers']]
128
+
127
129
  combiner = _MATCHER_COMBINERS[raw_condition['matcherGroup']['combiner']]
128
130
  label = raw_condition.get('label')
129
131
 
@@ -1,4 +1,5 @@
1
1
  """Matchers entrypoint module."""
2
+ from splitio.models import MatcherNotFoundException
2
3
  from splitio.models.grammar.matchers.keys import AllKeysMatcher, UserDefinedSegmentMatcher
3
4
  from splitio.models.grammar.matchers.numeric import BetweenMatcher, EqualToMatcher, \
4
5
  GreaterThanOrEqualMatcher, LessThanOrEqualMatcher
@@ -7,6 +8,8 @@ from splitio.models.grammar.matchers.sets import ContainsAllOfSetMatcher, \
7
8
  from splitio.models.grammar.matchers.string import ContainsStringMatcher, \
8
9
  EndsWithMatcher, RegexMatcher, StartsWithMatcher, WhitelistMatcher
9
10
  from splitio.models.grammar.matchers.misc import BooleanMatcher, DependencyMatcher
11
+ from splitio.models.grammar.matchers.semver import EqualToSemverMatcher, GreaterThanOrEqualToSemverMatcher, LessThanOrEqualToSemverMatcher, \
12
+ BetweenSemverMatcher, InListSemverMatcher
10
13
 
11
14
 
12
15
  MATCHER_TYPE_ALL_KEYS = 'ALL_KEYS'
@@ -26,6 +29,11 @@ MATCHER_TYPE_CONTAINS_STRING = 'CONTAINS_STRING'
26
29
  MATCHER_TYPE_IN_SPLIT_TREATMENT = 'IN_SPLIT_TREATMENT'
27
30
  MATCHER_TYPE_EQUAL_TO_BOOLEAN = 'EQUAL_TO_BOOLEAN'
28
31
  MATCHER_TYPE_MATCHES_STRING = 'MATCHES_STRING'
32
+ MATCHER_TYPE_EQUAL_TO_SEMVER = 'EQUAL_TO_SEMVER'
33
+ MATCHER_GREATER_THAN_OR_EQUAL_TO_SEMVER = 'GREATER_THAN_OR_EQUAL_TO_SEMVER'
34
+ MATCHER_LESS_THAN_OR_EQUAL_TO_SEMVER = 'LESS_THAN_OR_EQUAL_TO_SEMVER'
35
+ MATCHER_BETWEEN_SEMVER = 'BETWEEN_SEMVER'
36
+ MATCHER_INLIST_SEMVER = 'IN_LIST_SEMVER'
29
37
 
30
38
 
31
39
  _MATCHER_BUILDERS = {
@@ -45,10 +53,14 @@ _MATCHER_BUILDERS = {
45
53
  MATCHER_TYPE_CONTAINS_STRING: ContainsStringMatcher,
46
54
  MATCHER_TYPE_IN_SPLIT_TREATMENT: DependencyMatcher,
47
55
  MATCHER_TYPE_EQUAL_TO_BOOLEAN: BooleanMatcher,
48
- MATCHER_TYPE_MATCHES_STRING: RegexMatcher
56
+ MATCHER_TYPE_MATCHES_STRING: RegexMatcher,
57
+ MATCHER_TYPE_EQUAL_TO_SEMVER: EqualToSemverMatcher,
58
+ MATCHER_GREATER_THAN_OR_EQUAL_TO_SEMVER: GreaterThanOrEqualToSemverMatcher,
59
+ MATCHER_LESS_THAN_OR_EQUAL_TO_SEMVER: LessThanOrEqualToSemverMatcher,
60
+ MATCHER_BETWEEN_SEMVER: BetweenSemverMatcher,
61
+ MATCHER_INLIST_SEMVER: InListSemverMatcher
49
62
  }
50
63
 
51
-
52
64
  def from_raw(raw_matcher):
53
65
  """
54
66
  Parse a condition from a JSON portion of splitChanges.
@@ -63,5 +75,5 @@ def from_raw(raw_matcher):
63
75
  try:
64
76
  builder = _MATCHER_BUILDERS[matcher_type]
65
77
  except KeyError:
66
- raise ValueError('Invalid matcher type %s' % matcher_type)
78
+ raise MatcherNotFoundException('Invalid matcher type %s' % matcher_type)
67
79
  return builder(raw_matcher)
@@ -0,0 +1,260 @@
1
+ """Semver matcher classes."""
2
+ import logging
3
+
4
+ from splitio.models.grammar.matchers.base import Matcher
5
+ from splitio.models.grammar.matchers.string import Sanitizer
6
+ from splitio.models.grammar.matchers.utils.utils import build_semver_or_none
7
+
8
+
9
+ _LOGGER = logging.getLogger(__name__)
10
+
11
+
12
+ class EqualToSemverMatcher(Matcher):
13
+ """A matcher for Semver equal to."""
14
+
15
+ def _build(self, raw_matcher):
16
+ """
17
+ Build an EqualToSemverMatcher.
18
+
19
+ :param raw_matcher: raw matcher as fetched from splitChanges response.
20
+ :type raw_matcher: dict
21
+ """
22
+ self._data = raw_matcher.get('stringMatcherData')
23
+ self._semver = build_semver_or_none(raw_matcher.get('stringMatcherData'))
24
+
25
+ def _match(self, key, attributes=None, context=None):
26
+ """
27
+ Evaluate user input against a matcher and return whether the match is successful.
28
+
29
+ :param key: User key.
30
+ :type key: str.
31
+ :param attributes: Custom user attributes.
32
+ :type attributes: dict.
33
+ :param context: Evaluation context
34
+ :type context: dict
35
+
36
+ :returns: Wheter the match is successful.
37
+ :rtype: bool
38
+ """
39
+ if self._semver is None:
40
+ _LOGGER.error("stringMatcherData is required for EQUAL_TO_SEMVER matcher type")
41
+ return False
42
+
43
+ matching_data = Sanitizer.ensure_string(self._get_matcher_input(key, attributes))
44
+ if matching_data is None:
45
+ return False
46
+
47
+ matching_semver = build_semver_or_none(matching_data)
48
+ if matching_semver is None:
49
+ return False
50
+
51
+ return self._semver.version == matching_semver.version
52
+
53
+ def __str__(self):
54
+ """Return string Representation."""
55
+ return f'equal semver {self._data}'
56
+
57
+ def _add_matcher_specific_properties_to_json(self):
58
+ """Add matcher specific properties to base dict before returning it."""
59
+ return {'matcherType': 'EQUAL_TO_SEMVER', 'stringMatcherData': self._data}
60
+
61
+ class GreaterThanOrEqualToSemverMatcher(Matcher):
62
+ """A matcher for Semver greater than or equal to."""
63
+
64
+ def _build(self, raw_matcher):
65
+ """
66
+ Build a GreaterThanOrEqualToSemverMatcher.
67
+
68
+ :param raw_matcher: raw matcher as fetched from splitChanges response.
69
+ :type raw_matcher: dict
70
+ """
71
+ self._data = raw_matcher.get('stringMatcherData')
72
+ self._semver = build_semver_or_none(raw_matcher.get('stringMatcherData'))
73
+
74
+ def _match(self, key, attributes=None, context=None):
75
+ """
76
+ Evaluate user input against a matcher and return whether the match is successful.
77
+
78
+ :param key: User key.
79
+ :type key: str.
80
+ :param attributes: Custom user attributes.
81
+ :type attributes: dict.
82
+ :param context: Evaluation context
83
+ :type context: dict
84
+
85
+ :returns: Wheter the match is successful.
86
+ :rtype: bool
87
+ """
88
+ if self._semver is None:
89
+ _LOGGER.error("stringMatcherData is required for GREATER_THAN_OR_EQUAL_TO_SEMVER matcher type")
90
+ return False
91
+
92
+ matching_data = Sanitizer.ensure_string(self._get_matcher_input(key, attributes))
93
+ if matching_data is None:
94
+ return False
95
+
96
+ matching_semver = build_semver_or_none(matching_data)
97
+ if matching_semver is None:
98
+ return False
99
+
100
+ return matching_semver.compare(self._semver) in [0, 1]
101
+
102
+ def __str__(self):
103
+ """Return string Representation."""
104
+ return f'greater than or equal to semver {self._data}'
105
+
106
+ def _add_matcher_specific_properties_to_json(self):
107
+ """Add matcher specific properties to base dict before returning it."""
108
+ return {'matcherType': 'GREATER_THAN_OR_EQUAL_TO_SEMVER', 'stringMatcherData': self._data}
109
+
110
+
111
+ class LessThanOrEqualToSemverMatcher(Matcher):
112
+ """A matcher for Semver less than or equal to."""
113
+
114
+ def _build(self, raw_matcher):
115
+ """
116
+ Build a LessThanOrEqualToSemverMatcher.
117
+
118
+ :param raw_matcher: raw matcher as fetched from splitChanges response.
119
+ :type raw_matcher: dict
120
+ """
121
+ self._data = raw_matcher.get('stringMatcherData')
122
+ self._semver = build_semver_or_none(raw_matcher.get('stringMatcherData'))
123
+
124
+ def _match(self, key, attributes=None, context=None):
125
+ """
126
+ Evaluate user input against a matcher and return whether the match is successful.
127
+
128
+ :param key: User key.
129
+ :type key: str.
130
+ :param attributes: Custom user attributes.
131
+ :type attributes: dict.
132
+ :param context: Evaluation context
133
+ :type context: dict
134
+
135
+ :returns: Wheter the match is successful.
136
+ :rtype: bool
137
+ """
138
+ if self._semver is None:
139
+ _LOGGER.error("stringMatcherData is required for LESS_THAN_OR_EQUAL_TO_SEMVER matcher type")
140
+ return False
141
+
142
+ matching_data = Sanitizer.ensure_string(self._get_matcher_input(key, attributes))
143
+ if matching_data is None:
144
+ return False
145
+
146
+ matching_semver = build_semver_or_none(matching_data)
147
+ if matching_semver is None:
148
+ return False
149
+
150
+ return matching_semver.compare(self._semver) in [0, -1]
151
+
152
+ def __str__(self):
153
+ """Return string Representation."""
154
+ return f'less than or equal to semver {self._data}'
155
+
156
+ def _add_matcher_specific_properties_to_json(self):
157
+ """Add matcher specific properties to base dict before returning it."""
158
+ return {'matcherType': 'LESS_THAN_OR_EQUAL_TO_SEMVER', 'stringMatcherData': self._data}
159
+
160
+
161
+ class BetweenSemverMatcher(Matcher):
162
+ """A matcher for Semver between."""
163
+
164
+ def _build(self, raw_matcher):
165
+ """
166
+ Build a BetweenSemverMatcher.
167
+
168
+ :param raw_matcher: raw matcher as fetched from splitChanges response.
169
+ :type raw_matcher: dict
170
+ """
171
+ self._data = raw_matcher.get('betweenStringMatcherData')
172
+ self._semver_start = build_semver_or_none(self._data['start'])
173
+ self._semver_end = build_semver_or_none(self._data['end'])
174
+
175
+ def _match(self, key, attributes=None, context=None):
176
+ """
177
+ Evaluate user input against a matcher and return whether the match is successful.
178
+
179
+ :param key: User key.
180
+ :type key: str.
181
+ :param attributes: Custom user attributes.
182
+ :type attributes: dict.
183
+ :param context: Evaluation context
184
+ :type context: dict
185
+
186
+ :returns: Wheter the match is successful.
187
+ :rtype: bool
188
+ """
189
+ if self._semver_start is None or self._semver_end is None:
190
+ _LOGGER.error("betweenStringMatcherData is required for BETWEEN_SEMVER matcher type")
191
+ return False
192
+
193
+ matching_data = Sanitizer.ensure_string(self._get_matcher_input(key, attributes))
194
+ if matching_data is None:
195
+ return False
196
+
197
+ matching_semver = build_semver_or_none(matching_data)
198
+ if matching_semver is None:
199
+ return False
200
+
201
+ return (self._semver_start.compare(matching_semver) in [0, -1]) and (self._semver_end.compare(matching_semver) in [0, 1])
202
+
203
+ def __str__(self):
204
+ """Return string Representation."""
205
+ return 'between semver {start} and {end}'.format(start=self._data.get('start'), end=self._data.get('end'))
206
+
207
+ def _add_matcher_specific_properties_to_json(self):
208
+ """Add matcher specific properties to base dict before returning it."""
209
+ return {'matcherType': 'BETWEEN_SEMVER', 'betweenStringMatcherData': self._data}
210
+
211
+
212
+ class InListSemverMatcher(Matcher):
213
+ """A matcher for Semver in list."""
214
+
215
+ def _build(self, raw_matcher):
216
+ """
217
+ Build a InListSemverMatcher.
218
+
219
+ :param raw_matcher: raw matcher as fetched from splitChanges response.
220
+ :type raw_matcher: dict
221
+ """
222
+ self._data = raw_matcher['whitelistMatcherData']['whitelist']
223
+ semver_list = [build_semver_or_none(item) for item in self._data if item]
224
+ self._semver_list = frozenset([item.version for item in semver_list if item])
225
+
226
+ def _match(self, key, attributes=None, context=None):
227
+ """
228
+ Evaluate user input against a matcher and return whether the match is successful.
229
+
230
+ :param key: User key.
231
+ :type key: str.
232
+ :param attributes: Custom user attributes.
233
+ :type attributes: dict.
234
+ :param context: Evaluation context
235
+ :type context: dict
236
+
237
+ :returns: Wheter the match is successful.
238
+ :rtype: bool
239
+ """
240
+ if self._semver_list is None:
241
+ _LOGGER.error("whitelistMatcherData is required for IN_LIST_SEMVER matcher type")
242
+ return False
243
+
244
+ matching_data = Sanitizer.ensure_string(self._get_matcher_input(key, attributes))
245
+ if matching_data is None:
246
+ return False
247
+
248
+ matching_semver = build_semver_or_none(matching_data)
249
+ if matching_semver is None:
250
+ return False
251
+
252
+ return matching_semver.version in self._semver_list
253
+
254
+ def __str__(self):
255
+ """Return string Representation."""
256
+ return 'in list semver {data}'.format(data=self._data)
257
+
258
+ def _add_matcher_specific_properties_to_json(self):
259
+ """Add matcher specific properties to base dict before returning it."""
260
+ return {'matcherType': 'IN_LIST_SEMVER', 'whitelistMatcherData': {'whitelist': self._data}}