splitio-client 10.2.0__tar.gz → 10.3.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 (129) hide show
  1. splitio_client-10.3.0/PKG-INFO +101 -0
  2. {splitio_client-10.2.0 → splitio_client-10.3.0}/setup.py +3 -2
  3. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/api/client.py +34 -7
  4. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/api/commons.py +18 -2
  5. splitio_client-10.3.0/splitio/api/splits.py +202 -0
  6. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/client/client.py +2 -2
  7. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/client/factory.py +20 -7
  8. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/engine/evaluator.py +104 -41
  9. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/models/grammar/condition.py +6 -4
  10. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/models/grammar/matchers/__init__.py +4 -1
  11. splitio_client-10.3.0/splitio/models/grammar/matchers/prerequisites.py +38 -0
  12. splitio_client-10.3.0/splitio/models/grammar/matchers/rule_based_segment.py +72 -0
  13. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/models/impressions.py +5 -0
  14. splitio_client-10.3.0/splitio/models/rule_based_segments.py +195 -0
  15. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/models/splits.py +45 -5
  16. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/models/telemetry.py +1 -0
  17. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/push/parser.py +57 -3
  18. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/push/processor.py +6 -4
  19. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/push/workers.py +104 -30
  20. splitio_client-10.3.0/splitio/spec.py +1 -0
  21. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/storage/__init__.py +72 -1
  22. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/storage/inmemmory.py +274 -1
  23. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/storage/pluggable.py +283 -3
  24. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/storage/redis.py +291 -3
  25. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/sync/segment.py +11 -4
  26. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/sync/split.py +245 -97
  27. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/sync/util.py +4 -0
  28. splitio_client-10.3.0/splitio/util/storage_helper.py +200 -0
  29. splitio_client-10.3.0/splitio/version.py +1 -0
  30. splitio_client-10.3.0/splitio_client.egg-info/PKG-INFO +101 -0
  31. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio_client.egg-info/SOURCES.txt +3 -1
  32. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio_client.egg-info/requires.txt +2 -1
  33. splitio_client-10.2.0/LICENSE.txt +0 -13
  34. splitio_client-10.2.0/PKG-INFO +0 -125
  35. splitio_client-10.2.0/splitio/api/splits.py +0 -121
  36. splitio_client-10.2.0/splitio/spec.py +0 -1
  37. splitio_client-10.2.0/splitio/util/storage_helper.py +0 -99
  38. splitio_client-10.2.0/splitio/version.py +0 -1
  39. splitio_client-10.2.0/splitio_client.egg-info/PKG-INFO +0 -125
  40. {splitio_client-10.2.0 → splitio_client-10.3.0}/README.md +0 -0
  41. {splitio_client-10.2.0 → splitio_client-10.3.0}/setup.cfg +0 -0
  42. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/__init__.py +0 -0
  43. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/api/__init__.py +0 -0
  44. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/api/auth.py +0 -0
  45. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/api/events.py +0 -0
  46. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/api/impressions.py +0 -0
  47. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/api/segments.py +0 -0
  48. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/api/telemetry.py +0 -0
  49. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/client/__init__.py +0 -0
  50. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/client/config.py +0 -0
  51. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/client/input_validator.py +0 -0
  52. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/client/key.py +0 -0
  53. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/client/listener.py +0 -0
  54. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/client/localhost.py +0 -0
  55. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/client/manager.py +0 -0
  56. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/client/util.py +0 -0
  57. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/engine/__init__.py +0 -0
  58. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/engine/cache/__init__.py +0 -0
  59. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/engine/cache/lru.py +0 -0
  60. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/engine/filters.py +0 -0
  61. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/engine/hashfns/__init__.py +0 -0
  62. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/engine/hashfns/legacy.py +0 -0
  63. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/engine/hashfns/murmur3py.py +0 -0
  64. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/engine/impressions/__init__.py +0 -0
  65. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/engine/impressions/adapters.py +0 -0
  66. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/engine/impressions/impressions.py +0 -0
  67. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/engine/impressions/manager.py +0 -0
  68. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/engine/impressions/strategies.py +0 -0
  69. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/engine/impressions/unique_keys_tracker.py +0 -0
  70. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/engine/splitters.py +0 -0
  71. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/engine/telemetry.py +0 -0
  72. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/exceptions.py +0 -0
  73. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/factories.py +0 -0
  74. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/impressions.py +0 -0
  75. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/key.py +0 -0
  76. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/models/__init__.py +0 -0
  77. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/models/datatypes.py +0 -0
  78. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/models/events.py +0 -0
  79. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/models/grammar/__init__.py +0 -0
  80. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/models/grammar/matchers/base.py +0 -0
  81. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/models/grammar/matchers/keys.py +0 -0
  82. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/models/grammar/matchers/misc.py +0 -0
  83. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/models/grammar/matchers/numeric.py +0 -0
  84. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/models/grammar/matchers/semver.py +0 -0
  85. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/models/grammar/matchers/sets.py +0 -0
  86. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/models/grammar/matchers/string.py +0 -0
  87. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/models/grammar/matchers/utils/__init__.py +0 -0
  88. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/models/grammar/matchers/utils/utils.py +0 -0
  89. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/models/grammar/partitions.py +0 -0
  90. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/models/notification.py +0 -0
  91. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/models/segments.py +0 -0
  92. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/models/token.py +0 -0
  93. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/optional/__init__.py +0 -0
  94. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/optional/loaders.py +0 -0
  95. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/push/__init__.py +0 -0
  96. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/push/manager.py +0 -0
  97. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/push/splitsse.py +0 -0
  98. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/push/sse.py +0 -0
  99. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/push/status_tracker.py +0 -0
  100. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/recorder/__init__.py +0 -0
  101. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/recorder/recorder.py +0 -0
  102. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/storage/adapters/__init__.py +0 -0
  103. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/storage/adapters/cache_trait.py +0 -0
  104. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/storage/adapters/redis.py +0 -0
  105. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/storage/adapters/util.py +0 -0
  106. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/sync/__init__.py +0 -0
  107. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/sync/event.py +0 -0
  108. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/sync/impression.py +0 -0
  109. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/sync/manager.py +0 -0
  110. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/sync/synchronizer.py +0 -0
  111. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/sync/telemetry.py +0 -0
  112. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/sync/unique_keys.py +0 -0
  113. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/tasks/__init__.py +0 -0
  114. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/tasks/events_sync.py +0 -0
  115. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/tasks/impressions_sync.py +0 -0
  116. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/tasks/segment_sync.py +0 -0
  117. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/tasks/split_sync.py +0 -0
  118. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/tasks/telemetry_sync.py +0 -0
  119. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/tasks/unique_keys_sync.py +0 -0
  120. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/tasks/util/__init__.py +0 -0
  121. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/tasks/util/asynctask.py +0 -0
  122. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/tasks/util/workerpool.py +0 -0
  123. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/util/__init__.py +0 -0
  124. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/util/backoff.py +0 -0
  125. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/util/decorators.py +0 -0
  126. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/util/threadutil.py +0 -0
  127. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio/util/time.py +0 -0
  128. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio_client.egg-info/dependency_links.txt +0 -0
  129. {splitio_client-10.2.0 → splitio_client-10.3.0}/splitio_client.egg-info/top_level.txt +0 -0
@@ -0,0 +1,101 @@
1
+ Metadata-Version: 2.1
2
+ Name: splitio_client
3
+ Version: 10.3.0
4
+ Summary: Split.io Python Client
5
+ Home-page: https://github.com/splitio/python-client
6
+ Author: Patricio Echague, Sebastian Arrubia
7
+ Author-email: pato@split.io, sebastian@split.io
8
+ License: Apache License 2.0
9
+ Download-URL: https://github.com/splitio/python-client/tarball/10.3.0
10
+ Description: # Split Python SDK
11
+ ![Build Status](https://github.com/splitio/python-client/actions/workflows/ci.yml/badge.svg?branch=master)
12
+
13
+ ## Overview
14
+ This SDK is designed to work with Split, the platform for controlled rollouts, which serves features to your users via a Split feature flag to manage your complete customer experience.
15
+
16
+ [![Twitter Follow](https://img.shields.io/twitter/follow/splitsoftware.svg?style=social&label=Follow&maxAge=1529000)](https://twitter.com/intent/follow?screen_name=splitsoftware)
17
+
18
+ ## Compatibility
19
+ This SDK is compatible with **Python 3.7 and higher**.
20
+
21
+ ## Getting started
22
+ Below is a simple example that describes the instantiation and most basic usage of our SDK:
23
+
24
+ Run `pip install splitio_client`
25
+
26
+
27
+ ```python
28
+ from splitio import get_factory
29
+ from splitio.exceptions import TimeoutException
30
+ factory = get_factory('YOUR_SDK_TYPE_API_KEY', config=config)
31
+ try:
32
+ factory.block_until_ready(5) # wait up to 5 seconds
33
+ split = factory.client()
34
+ treatment = split.get_treatment('CUSTOMER_ID', 'FEATURE_FLAG_NAME')
35
+ if treatment == "on":
36
+ # insert code here to show on treatment
37
+ elif treatment == "off":
38
+ # insert code here to show off treatment
39
+ else:
40
+ # insert your control treatment code here
41
+ except TimeoutException:
42
+ # Now the user can choose whether to abort the whole execution, or just keep going
43
+ # without a ready client, which if configured properly, should become ready at some point.
44
+ pass
45
+ ```
46
+
47
+ Please refer to [our official docs](https://help.split.io/hc/en-us/articles/360020359652-Python-SDK) to learn about all the functionality provided by our SDK and the configuration options available for tailoring it to your current application setup.
48
+
49
+ ## Submitting issues
50
+ The Split team monitors all issues submitted to this [issue tracker](https://github.com/splitio/python-client/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.
51
+
52
+ ## Contributing
53
+ Please see [Contributors Guide](CONTRIBUTORS-GUIDE.md) to find all you need to submit a Pull Request (PR).
54
+
55
+ ## License
56
+ Licensed under the Apache License, Version 2.0. See: [Apache License](http://www.apache.org/licenses/).
57
+
58
+ ## About Split
59
+
60
+ Split is the leading Feature Delivery Platform for engineering teams that want to confidently deploy features as fast as they can develop them. Split’s fine-grained management, real-time monitoring, and data-driven experimentation ensure that new features will improve the customer experience without breaking or degrading performance. Companies like Twilio, Salesforce, GoDaddy and WePay trust Split to power their feature delivery.
61
+
62
+ To learn more about Split, contact hello@split.io, or get started with feature flags for free at https://www.split.io/signup.
63
+
64
+ Split has built and maintains SDKs for:
65
+
66
+ * .NET [Github](https://github.com/splitio/dotnet-client) [Docs](https://help.split.io/hc/en-us/articles/360020240172--NET-SDK)
67
+ * Android [Github](https://github.com/splitio/android-client) [Docs](https://help.split.io/hc/en-us/articles/360020343291-Android-SDK)
68
+ * Angular [Github](https://github.com/splitio/angular-sdk-plugin) [Docs](https://help.split.io/hc/en-us/articles/6495326064397-Angular-utilities)
69
+ * GO [Github](https://github.com/splitio/go-client) [Docs](https://help.split.io/hc/en-us/articles/360020093652-Go-SDK)
70
+ * iOS [Github](https://github.com/splitio/ios-client) [Docs](https://help.split.io/hc/en-us/articles/360020401491-iOS-SDK)
71
+ * Java [Github](https://github.com/splitio/java-client) [Docs](https://help.split.io/hc/en-us/articles/360020405151-Java-SDK)
72
+ * JavaScript [Github](https://github.com/splitio/javascript-client) [Docs](https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK)
73
+ * JavaScript for Browser [Github](https://github.com/splitio/javascript-browser-client) [Docs](https://help.split.io/hc/en-us/articles/360058730852-Browser-SDK)
74
+ * Node [Github](https://github.com/splitio/javascript-client) [Docs](https://help.split.io/hc/en-us/articles/360020564931-Node-js-SDK)
75
+ * PHP [Github](https://github.com/splitio/php-client) [Docs](https://help.split.io/hc/en-us/articles/360020350372-PHP-SDK)
76
+ * Python [Github](https://github.com/splitio/python-client) [Docs](https://help.split.io/hc/en-us/articles/360020359652-Python-SDK)
77
+ * React [Github](https://github.com/splitio/react-client) [Docs](https://help.split.io/hc/en-us/articles/360038825091-React-SDK)
78
+ * React Native [Github](https://github.com/splitio/react-native-client) [Docs](https://help.split.io/hc/en-us/articles/4406066357901-React-Native-SDK)
79
+ * Redux [Github](https://github.com/splitio/redux-client) [Docs](https://help.split.io/hc/en-us/articles/360038851551-Redux-SDK)
80
+ * Ruby [Github](https://github.com/splitio/ruby-client) [Docs](https://help.split.io/hc/en-us/articles/360020673251-Ruby-SDK)
81
+
82
+ For a comprehensive list of open source projects visit our [Github page](https://github.com/splitio?utf8=%E2%9C%93&query=%20only%3Apublic%20).
83
+
84
+ **Learn more about Split:**
85
+
86
+ Visit [split.io/product](https://www.split.io/product) for an overview of Split, or visit our documentation at [help.split.io](http://help.split.io) for more detailed information.
87
+
88
+ Platform: UNKNOWN
89
+ Classifier: Environment :: Console
90
+ Classifier: Intended Audience :: Developers
91
+ Classifier: Programming Language :: Python
92
+ Classifier: Programming Language :: Python :: 2
93
+ Classifier: Programming Language :: Python :: 3
94
+ Classifier: Topic :: Software Development :: Libraries
95
+ Description-Content-Type: text/markdown
96
+ Provides-Extra: test
97
+ Provides-Extra: redis
98
+ Provides-Extra: uwsgi
99
+ Provides-Extra: cpphash
100
+ Provides-Extra: asyncio
101
+ Provides-Extra: kerberos
@@ -8,7 +8,7 @@ TESTS_REQUIRES = [
8
8
  'flake8',
9
9
  'pytest==7.0.1',
10
10
  'pytest-mock==3.11.1',
11
- 'coverage',
11
+ 'coverage==7.0.0',
12
12
  'pytest-cov==4.1.0',
13
13
  'importlib-metadata==6.7',
14
14
  'tomli==1.2.3',
@@ -17,7 +17,8 @@ TESTS_REQUIRES = [
17
17
  'pytest-asyncio==0.21.0',
18
18
  'aiohttp>=3.8.4',
19
19
  'aiofiles>=23.1.0',
20
- 'requests-kerberos>=0.15.0'
20
+ 'requests-kerberos>=0.15.0',
21
+ 'urllib3==2.0.7'
21
22
  ]
22
23
 
23
24
  INSTALL_REQUIRES = [
@@ -92,6 +92,25 @@ class HTTPAdapterWithProxyKerberosAuth(requests.adapters.HTTPAdapter):
92
92
  class HttpClientBase(object, metaclass=abc.ABCMeta):
93
93
  """HttpClient wrapper template."""
94
94
 
95
+ def __init__(self, timeout=None, sdk_url=None, events_url=None, auth_url=None, telemetry_url=None):
96
+ """
97
+ Class constructor.
98
+
99
+ :param timeout: How many milliseconds to wait until the server responds.
100
+ :type timeout: int
101
+ :param sdk_url: Optional alternative sdk URL.
102
+ :type sdk_url: str
103
+ :param events_url: Optional alternative events URL.
104
+ :type events_url: str
105
+ :param auth_url: Optional alternative auth URL.
106
+ :type auth_url: str
107
+ :param telemetry_url: Optional alternative telemetry URL.
108
+ :type telemetry_url: str
109
+ """
110
+ _LOGGER.debug("Initializing httpclient")
111
+ self._timeout = timeout/1000 if timeout else None # Convert ms to seconds.
112
+ self._urls = _construct_urls(sdk_url, events_url, auth_url, telemetry_url)
113
+
95
114
  @abc.abstractmethod
96
115
  def get(self, server, path, apikey):
97
116
  """http get request"""
@@ -113,6 +132,9 @@ class HttpClientBase(object, metaclass=abc.ABCMeta):
113
132
  self._telemetry_runtime_producer = telemetry_runtime_producer
114
133
  self._metric_name = metric_name
115
134
 
135
+ def is_sdk_endpoint_overridden(self):
136
+ return self._urls['sdk'] != SDK_URL
137
+
116
138
  def _get_headers(self, extra_headers, sdk_key):
117
139
  headers = _build_basic_headers(sdk_key)
118
140
  if extra_headers is not None:
@@ -154,10 +176,8 @@ class HttpClient(HttpClientBase):
154
176
  :param telemetry_url: Optional alternative telemetry URL.
155
177
  :type telemetry_url: str
156
178
  """
157
- _LOGGER.debug("Initializing httpclient")
158
- self._timeout = timeout/1000 if timeout else None # Convert ms to seconds.
159
- self._urls = _construct_urls(sdk_url, events_url, auth_url, telemetry_url)
160
-
179
+ HttpClientBase.__init__(self, timeout, sdk_url, events_url, auth_url, telemetry_url)
180
+
161
181
  def get(self, server, path, sdk_key, query=None, extra_headers=None): # pylint: disable=too-many-arguments
162
182
  """
163
183
  Issue a get request.
@@ -187,7 +207,11 @@ class HttpClient(HttpClientBase):
187
207
  self._record_telemetry(response.status_code, get_current_epoch_time_ms() - start)
188
208
  return HttpResponse(response.status_code, response.text, response.headers)
189
209
 
190
- except Exception as exc: # pylint: disable=broad-except
210
+ except requests.exceptions.ChunkedEncodingError as exc:
211
+ _LOGGER.error("IncompleteRead exception detected: %s", exc)
212
+ return HttpResponse(400, "", {})
213
+
214
+ except Exception as exc: # pylint: disable=broad-except
191
215
  raise HttpClientException(_EXC_MSG.format(source='request')) from exc
192
216
 
193
217
  def post(self, server, path, sdk_key, body, query=None, extra_headers=None): # pylint: disable=too-many-arguments
@@ -241,8 +265,7 @@ class HttpClientAsync(HttpClientBase):
241
265
  :param telemetry_url: Optional alternative telemetry URL.
242
266
  :type telemetry_url: str
243
267
  """
244
- self._timeout = timeout/1000 if timeout else None # Convert ms to seconds.
245
- self._urls = _construct_urls(sdk_url, events_url, auth_url, telemetry_url)
268
+ HttpClientBase.__init__(self, timeout, sdk_url, events_url, auth_url, telemetry_url)
246
269
  self._session = aiohttp.ClientSession()
247
270
 
248
271
  async def get(self, server, path, apikey, query=None, extra_headers=None): # pylint: disable=too-many-arguments
@@ -281,6 +304,10 @@ class HttpClientAsync(HttpClientBase):
281
304
  await self._record_telemetry(response.status, get_current_epoch_time_ms() - start)
282
305
  return HttpResponse(response.status, body, response.headers)
283
306
 
307
+ except aiohttp.ClientPayloadError as exc:
308
+ _LOGGER.error("ContentLengthError exception detected: %s", exc)
309
+ return HttpResponse(400, "", {})
310
+
284
311
  except aiohttp.ClientError as exc: # pylint: disable=broad-except
285
312
  raise HttpClientException(_EXC_MSG.format(source='aiohttp')) from exc
286
313
 
@@ -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, spec=SPEC_VERSION):
60
+ def __init__(self, cache_control_headers=False, change_number=None, rbs_change_number=None, sets=None, spec=SPEC_VERSION):
61
61
  """
62
62
  Class constructor.
63
63
 
@@ -72,6 +72,7 @@ class FetchOptions(object):
72
72
  """
73
73
  self._cache_control_headers = cache_control_headers
74
74
  self._change_number = change_number
75
+ self._rbs_change_number = rbs_change_number
75
76
  self._sets = sets
76
77
  self._spec = spec
77
78
 
@@ -85,6 +86,11 @@ class FetchOptions(object):
85
86
  """Return change number."""
86
87
  return self._change_number
87
88
 
89
+ @property
90
+ def rbs_change_number(self):
91
+ """Return change number."""
92
+ return self._rbs_change_number
93
+
88
94
  @property
89
95
  def sets(self):
90
96
  """Return sets."""
@@ -103,14 +109,19 @@ class FetchOptions(object):
103
109
  if self._change_number != other._change_number:
104
110
  return False
105
111
 
112
+ if self._rbs_change_number != other._rbs_change_number:
113
+ return False
114
+
106
115
  if self._sets != other._sets:
107
116
  return False
117
+
108
118
  if self._spec != other._spec:
109
119
  return False
120
+
110
121
  return True
111
122
 
112
123
 
113
- def build_fetch(change_number, fetch_options, metadata):
124
+ def build_fetch(change_number, fetch_options, metadata, rbs_change_number=None):
114
125
  """
115
126
  Build fetch with new flags if that is the case.
116
127
 
@@ -123,11 +134,16 @@ def build_fetch(change_number, fetch_options, metadata):
123
134
  :param metadata: Metadata Headers.
124
135
  :type metadata: dict
125
136
 
137
+ :param rbs_change_number: Last known timestamp of a rule based segment modification.
138
+ :type rbs_change_number: int
139
+
126
140
  :return: Objects for fetch
127
141
  :rtype: dict, dict
128
142
  """
129
143
  query = {'s': fetch_options.spec} if fetch_options.spec is not None else {}
130
144
  query['since'] = change_number
145
+ if rbs_change_number is not None:
146
+ query['rbSince'] = rbs_change_number
131
147
  extra_headers = metadata
132
148
  if fetch_options is None:
133
149
  return query, extra_headers
@@ -0,0 +1,202 @@
1
+ """Splits API module."""
2
+
3
+ import logging
4
+ import json
5
+
6
+ from splitio.api import APIException, headers_from_metadata
7
+ from splitio.api.commons import build_fetch, FetchOptions
8
+ from splitio.api.client import HttpClientException
9
+ from splitio.models.telemetry import HTTPExceptionsAndLatencies
10
+ from splitio.util.time import utctime_ms
11
+ from splitio.spec import SPEC_VERSION
12
+ from splitio.sync import util
13
+
14
+ _LOGGER = logging.getLogger(__name__)
15
+ _SPEC_1_1 = "1.1"
16
+ _PROXY_CHECK_INTERVAL_MILLISECONDS_SS = 24 * 60 * 60 * 1000
17
+
18
+ class SplitsAPIBase(object): # pylint: disable=too-few-public-methods
19
+ """Class that uses an httpClient to communicate with the splits API."""
20
+
21
+ def __init__(self, client, sdk_key, sdk_metadata, telemetry_runtime_producer):
22
+ """
23
+ Class constructor.
24
+
25
+ :param client: HTTP Client responsble for issuing calls to the backend.
26
+ :type client: HttpClient
27
+ :param sdk_key: User sdk_key token.
28
+ :type sdk_key: string
29
+ :param sdk_metadata: SDK version & machine name & IP.
30
+ :type sdk_metadata: splitio.client.util.SdkMetadata
31
+ """
32
+ self._client = client
33
+ self._sdk_key = sdk_key
34
+ self._metadata = headers_from_metadata(sdk_metadata)
35
+ self._telemetry_runtime_producer = telemetry_runtime_producer
36
+ self._client.set_telemetry_data(HTTPExceptionsAndLatencies.SPLIT, self._telemetry_runtime_producer)
37
+ self._spec_version = SPEC_VERSION
38
+ self._last_proxy_check_timestamp = 0
39
+ self.clear_storage = False
40
+ self._old_spec_since = None
41
+
42
+ def _check_last_proxy_check_timestamp(self, since):
43
+ if self._spec_version == _SPEC_1_1 and ((utctime_ms() - self._last_proxy_check_timestamp) >= _PROXY_CHECK_INTERVAL_MILLISECONDS_SS):
44
+ _LOGGER.info("Switching to new Feature flag spec (%s) and fetching.", SPEC_VERSION);
45
+ self._spec_version = SPEC_VERSION
46
+ self._old_spec_since = since
47
+
48
+ def _check_old_spec_since(self, change_number):
49
+ if self._spec_version == _SPEC_1_1 and self._old_spec_since is not None:
50
+ since = self._old_spec_since
51
+ self._old_spec_since = None
52
+ return since
53
+ return change_number
54
+
55
+
56
+ class SplitsAPI(SplitsAPIBase): # pylint: disable=too-few-public-methods
57
+ """Class that uses an httpClient to communicate with the splits API."""
58
+
59
+ def __init__(self, client, sdk_key, sdk_metadata, telemetry_runtime_producer):
60
+ """
61
+ Class constructor.
62
+
63
+ :param client: HTTP Client responsble for issuing calls to the backend.
64
+ :type client: HttpClient
65
+ :param sdk_key: User sdk_key token.
66
+ :type sdk_key: string
67
+ :param sdk_metadata: SDK version & machine name & IP.
68
+ :type sdk_metadata: splitio.client.util.SdkMetadata
69
+ """
70
+ SplitsAPIBase.__init__(self, client, sdk_key, sdk_metadata, telemetry_runtime_producer)
71
+
72
+ def fetch_splits(self, change_number, rbs_change_number, fetch_options):
73
+ """
74
+ Fetch feature flags from backend.
75
+
76
+ :param change_number: Last known timestamp of a split modification.
77
+ :type change_number: int
78
+
79
+ :param rbs_change_number: Last known timestamp of a rule based segment modification.
80
+ :type rbs_change_number: int
81
+
82
+ :param fetch_options: Fetch options for getting feature flag definitions.
83
+ :type fetch_options: splitio.api.commons.FetchOptions
84
+
85
+ :return: Json representation of a splitChanges response.
86
+ :rtype: dict
87
+ """
88
+ try:
89
+ self._check_last_proxy_check_timestamp(change_number)
90
+ change_number = self._check_old_spec_since(change_number)
91
+
92
+ if self._spec_version == _SPEC_1_1:
93
+ fetch_options = FetchOptions(fetch_options.cache_control_headers, fetch_options.change_number,
94
+ None, fetch_options.sets, self._spec_version)
95
+ rbs_change_number = None
96
+ query, extra_headers = build_fetch(change_number, fetch_options, self._metadata, rbs_change_number)
97
+ response = self._client.get(
98
+ 'sdk',
99
+ 'splitChanges',
100
+ self._sdk_key,
101
+ extra_headers=extra_headers,
102
+ query=query,
103
+ )
104
+ if 200 <= response.status_code < 300:
105
+ if self._spec_version == _SPEC_1_1:
106
+ return util.convert_to_new_spec(json.loads(response.body))
107
+
108
+ self.clear_storage = self._last_proxy_check_timestamp != 0
109
+ self._last_proxy_check_timestamp = 0
110
+ return json.loads(response.body)
111
+
112
+ else:
113
+ if response.status_code == 414:
114
+ _LOGGER.error('Error fetching feature flags; the amount of flag sets provided are too big, causing uri length error.')
115
+
116
+ if self._client.is_sdk_endpoint_overridden() and response.status_code == 400 and self._spec_version == SPEC_VERSION:
117
+ _LOGGER.warning('Detected proxy response error, changing spec version from %s to %s and re-fetching.', self._spec_version, _SPEC_1_1)
118
+ self._spec_version = _SPEC_1_1
119
+ self._last_proxy_check_timestamp = utctime_ms()
120
+ return self.fetch_splits(change_number, None, FetchOptions(fetch_options.cache_control_headers, fetch_options.change_number,
121
+ None, fetch_options.sets, self._spec_version))
122
+
123
+ raise APIException(response.body, response.status_code)
124
+
125
+ except HttpClientException as exc:
126
+ _LOGGER.error('Error fetching feature flags because an exception was raised by the HTTPClient')
127
+ _LOGGER.debug('Error: ', exc_info=True)
128
+ raise APIException('Feature flags not fetched correctly.') from exc
129
+
130
+ class SplitsAPIAsync(SplitsAPIBase): # pylint: disable=too-few-public-methods
131
+ """Class that uses an httpClient to communicate with the splits API."""
132
+
133
+ def __init__(self, client, sdk_key, sdk_metadata, telemetry_runtime_producer):
134
+ """
135
+ Class constructor.
136
+
137
+ :param client: HTTP Client responsble for issuing calls to the backend.
138
+ :type client: HttpClient
139
+ :param sdk_key: User sdk_key token.
140
+ :type sdk_key: string
141
+ :param sdk_metadata: SDK version & machine name & IP.
142
+ :type sdk_metadata: splitio.client.util.SdkMetadata
143
+ """
144
+ SplitsAPIBase.__init__(self, client, sdk_key, sdk_metadata, telemetry_runtime_producer)
145
+
146
+ async def fetch_splits(self, change_number, rbs_change_number, fetch_options):
147
+ """
148
+ Fetch feature flags from backend.
149
+
150
+ :param change_number: Last known timestamp of a split modification.
151
+ :type change_number: int
152
+
153
+ :param rbs_change_number: Last known timestamp of a rule based segment modification.
154
+ :type rbs_change_number: int
155
+
156
+ :param fetch_options: Fetch options for getting feature flag definitions.
157
+ :type fetch_options: splitio.api.commons.FetchOptions
158
+
159
+ :return: Json representation of a splitChanges response.
160
+ :rtype: dict
161
+ """
162
+ try:
163
+ self._check_last_proxy_check_timestamp(change_number)
164
+ change_number = self._check_old_spec_since(change_number)
165
+ if self._spec_version == _SPEC_1_1:
166
+ fetch_options = FetchOptions(fetch_options.cache_control_headers, fetch_options.change_number,
167
+ None, fetch_options.sets, self._spec_version)
168
+ rbs_change_number = None
169
+
170
+ query, extra_headers = build_fetch(change_number, fetch_options, self._metadata, rbs_change_number)
171
+ response = await self._client.get(
172
+ 'sdk',
173
+ 'splitChanges',
174
+ self._sdk_key,
175
+ extra_headers=extra_headers,
176
+ query=query,
177
+ )
178
+ if 200 <= response.status_code < 300:
179
+ if self._spec_version == _SPEC_1_1:
180
+ return util.convert_to_new_spec(json.loads(response.body))
181
+
182
+ self.clear_storage = self._last_proxy_check_timestamp != 0
183
+ self._last_proxy_check_timestamp = 0
184
+ return json.loads(response.body)
185
+
186
+ else:
187
+ if response.status_code == 414:
188
+ _LOGGER.error('Error fetching feature flags; the amount of flag sets provided are too big, causing uri length error.')
189
+
190
+ if self._client.is_sdk_endpoint_overridden() and response.status_code == 400 and self._spec_version == SPEC_VERSION:
191
+ _LOGGER.warning('Detected proxy response error, changing spec version from %s to %s and re-fetching.', self._spec_version, _SPEC_1_1)
192
+ self._spec_version = _SPEC_1_1
193
+ self._last_proxy_check_timestamp = utctime_ms()
194
+ return await self.fetch_splits(change_number, None, FetchOptions(fetch_options.cache_control_headers, fetch_options.change_number,
195
+ None, fetch_options.sets, self._spec_version))
196
+
197
+ raise APIException(response.body, response.status_code)
198
+
199
+ except HttpClientException as exc:
200
+ _LOGGER.error('Error fetching feature flags because an exception was raised by the HTTPClient')
201
+ _LOGGER.debug('Error: ', exc_info=True)
202
+ raise APIException('Feature flags not fetched correctly.') from exc
@@ -201,7 +201,7 @@ class Client(ClientBase): # pylint: disable=too-many-instance-attributes
201
201
  :rtype: Client
202
202
  """
203
203
  ClientBase.__init__(self, factory, recorder, labels_enabled)
204
- self._context_factory = EvaluationDataFactory(factory._get_storage('splits'), factory._get_storage('segments'))
204
+ self._context_factory = EvaluationDataFactory(factory._get_storage('splits'), factory._get_storage('segments'), factory._get_storage('rule_based_segments'))
205
205
 
206
206
  def destroy(self):
207
207
  """
@@ -668,7 +668,7 @@ class ClientAsync(ClientBase): # pylint: disable=too-many-instance-attributes
668
668
  :rtype: Client
669
669
  """
670
670
  ClientBase.__init__(self, factory, recorder, labels_enabled)
671
- self._context_factory = AsyncEvaluationDataFactory(factory._get_storage('splits'), factory._get_storage('segments'))
671
+ self._context_factory = AsyncEvaluationDataFactory(factory._get_storage('splits'), factory._get_storage('segments'), factory._get_storage('rule_based_segments'))
672
672
 
673
673
  async def destroy(self):
674
674
  """
@@ -23,14 +23,17 @@ from splitio.engine.impressions.unique_keys_tracker import UniqueKeysTracker, Un
23
23
  from splitio.storage.inmemmory import InMemorySplitStorage, InMemorySegmentStorage, \
24
24
  InMemoryImpressionStorage, InMemoryEventStorage, InMemoryTelemetryStorage, LocalhostTelemetryStorage, \
25
25
  InMemorySplitStorageAsync, InMemorySegmentStorageAsync, InMemoryImpressionStorageAsync, \
26
- InMemoryEventStorageAsync, InMemoryTelemetryStorageAsync, LocalhostTelemetryStorageAsync
26
+ InMemoryEventStorageAsync, InMemoryTelemetryStorageAsync, LocalhostTelemetryStorageAsync, \
27
+ InMemoryRuleBasedSegmentStorage, InMemoryRuleBasedSegmentStorageAsync
27
28
  from splitio.storage.adapters import redis
28
29
  from splitio.storage.redis import RedisSplitStorage, RedisSegmentStorage, RedisImpressionsStorage, \
29
30
  RedisEventsStorage, RedisTelemetryStorage, RedisSplitStorageAsync, RedisEventsStorageAsync,\
30
- RedisSegmentStorageAsync, RedisImpressionsStorageAsync, RedisTelemetryStorageAsync
31
+ RedisSegmentStorageAsync, RedisImpressionsStorageAsync, RedisTelemetryStorageAsync, \
32
+ RedisRuleBasedSegmentsStorage, RedisRuleBasedSegmentsStorageAsync
31
33
  from splitio.storage.pluggable import PluggableEventsStorage, PluggableImpressionsStorage, PluggableSegmentStorage, \
32
34
  PluggableSplitStorage, PluggableTelemetryStorage, PluggableTelemetryStorageAsync, PluggableEventsStorageAsync, \
33
- PluggableImpressionsStorageAsync, PluggableSegmentStorageAsync, PluggableSplitStorageAsync
35
+ PluggableImpressionsStorageAsync, PluggableSegmentStorageAsync, PluggableSplitStorageAsync, \
36
+ PluggableRuleBasedSegmentsStorage, PluggableRuleBasedSegmentsStorageAsync
34
37
 
35
38
  # APIs
36
39
  from splitio.api.client import HttpClient, HttpClientAsync, HttpClientKerberos
@@ -543,6 +546,7 @@ def _build_in_memory_factory(api_key, cfg, sdk_url=None, events_url=None, # pyl
543
546
  storages = {
544
547
  'splits': InMemorySplitStorage(cfg['flagSetsFilter'] if cfg['flagSetsFilter'] is not None else []),
545
548
  'segments': InMemorySegmentStorage(),
549
+ 'rule_based_segments': InMemoryRuleBasedSegmentStorage(),
546
550
  'impressions': InMemoryImpressionStorage(cfg['impressionsQueueSize'], telemetry_runtime_producer),
547
551
  'events': InMemoryEventStorage(cfg['eventsQueueSize'], telemetry_runtime_producer),
548
552
  }
@@ -559,8 +563,8 @@ def _build_in_memory_factory(api_key, cfg, sdk_url=None, events_url=None, # pyl
559
563
  imp_strategy, none_strategy, telemetry_runtime_producer)
560
564
 
561
565
  synchronizers = SplitSynchronizers(
562
- SplitSynchronizer(apis['splits'], storages['splits']),
563
- SegmentSynchronizer(apis['segments'], storages['splits'], storages['segments']),
566
+ SplitSynchronizer(apis['splits'], storages['splits'], storages['rule_based_segments']),
567
+ SegmentSynchronizer(apis['segments'], storages['splits'], storages['segments'], storages['rule_based_segments']),
564
568
  ImpressionSynchronizer(apis['impressions'], storages['impressions'],
565
569
  cfg['impressionsBulkSize']),
566
570
  EventSynchronizer(apis['events'], storages['events'], cfg['eventsBulkSize']),
@@ -671,6 +675,7 @@ async def _build_in_memory_factory_async(api_key, cfg, sdk_url=None, events_url=
671
675
  storages = {
672
676
  'splits': InMemorySplitStorageAsync(cfg['flagSetsFilter'] if cfg['flagSetsFilter'] is not None else []),
673
677
  'segments': InMemorySegmentStorageAsync(),
678
+ 'rule_based_segments': InMemoryRuleBasedSegmentStorageAsync(),
674
679
  'impressions': InMemoryImpressionStorageAsync(cfg['impressionsQueueSize'], telemetry_runtime_producer),
675
680
  'events': InMemoryEventStorageAsync(cfg['eventsQueueSize'], telemetry_runtime_producer),
676
681
  }
@@ -687,8 +692,8 @@ async def _build_in_memory_factory_async(api_key, cfg, sdk_url=None, events_url=
687
692
  imp_strategy, none_strategy, telemetry_runtime_producer)
688
693
 
689
694
  synchronizers = SplitSynchronizers(
690
- SplitSynchronizerAsync(apis['splits'], storages['splits']),
691
- SegmentSynchronizerAsync(apis['segments'], storages['splits'], storages['segments']),
695
+ SplitSynchronizerAsync(apis['splits'], storages['splits'], storages['rule_based_segments']),
696
+ SegmentSynchronizerAsync(apis['segments'], storages['splits'], storages['segments'], storages['rule_based_segments']),
692
697
  ImpressionSynchronizerAsync(apis['impressions'], storages['impressions'],
693
698
  cfg['impressionsBulkSize']),
694
699
  EventSynchronizerAsync(apis['events'], storages['events'], cfg['eventsBulkSize']),
@@ -756,6 +761,7 @@ def _build_redis_factory(api_key, cfg):
756
761
  storages = {
757
762
  'splits': RedisSplitStorage(redis_adapter, cache_enabled, cache_ttl, []),
758
763
  'segments': RedisSegmentStorage(redis_adapter),
764
+ 'rule_based_segments': RedisRuleBasedSegmentsStorage(redis_adapter),
759
765
  'impressions': RedisImpressionsStorage(redis_adapter, sdk_metadata),
760
766
  'events': RedisEventsStorage(redis_adapter, sdk_metadata),
761
767
  'telemetry': RedisTelemetryStorage(redis_adapter, sdk_metadata)
@@ -839,6 +845,7 @@ async def _build_redis_factory_async(api_key, cfg):
839
845
  storages = {
840
846
  'splits': RedisSplitStorageAsync(redis_adapter, cache_enabled, cache_ttl),
841
847
  'segments': RedisSegmentStorageAsync(redis_adapter),
848
+ 'rule_based_segments': RedisRuleBasedSegmentsStorageAsync(redis_adapter),
842
849
  'impressions': RedisImpressionsStorageAsync(redis_adapter, sdk_metadata),
843
850
  'events': RedisEventsStorageAsync(redis_adapter, sdk_metadata),
844
851
  'telemetry': await RedisTelemetryStorageAsync.create(redis_adapter, sdk_metadata)
@@ -922,6 +929,7 @@ def _build_pluggable_factory(api_key, cfg):
922
929
  storages = {
923
930
  'splits': PluggableSplitStorage(pluggable_adapter, storage_prefix, []),
924
931
  'segments': PluggableSegmentStorage(pluggable_adapter, storage_prefix),
932
+ 'rule_based_segments': PluggableRuleBasedSegmentsStorage(pluggable_adapter, storage_prefix),
925
933
  'impressions': PluggableImpressionsStorage(pluggable_adapter, sdk_metadata, storage_prefix),
926
934
  'events': PluggableEventsStorage(pluggable_adapter, sdk_metadata, storage_prefix),
927
935
  'telemetry': PluggableTelemetryStorage(pluggable_adapter, sdk_metadata, storage_prefix)
@@ -1003,6 +1011,7 @@ async def _build_pluggable_factory_async(api_key, cfg):
1003
1011
  storages = {
1004
1012
  'splits': PluggableSplitStorageAsync(pluggable_adapter, storage_prefix),
1005
1013
  'segments': PluggableSegmentStorageAsync(pluggable_adapter, storage_prefix),
1014
+ 'rule_based_segments': PluggableRuleBasedSegmentsStorageAsync(pluggable_adapter, storage_prefix),
1006
1015
  'impressions': PluggableImpressionsStorageAsync(pluggable_adapter, sdk_metadata, storage_prefix),
1007
1016
  'events': PluggableEventsStorageAsync(pluggable_adapter, sdk_metadata, storage_prefix),
1008
1017
  'telemetry': await PluggableTelemetryStorageAsync.create(pluggable_adapter, sdk_metadata, storage_prefix)
@@ -1081,6 +1090,7 @@ def _build_localhost_factory(cfg):
1081
1090
  storages = {
1082
1091
  'splits': InMemorySplitStorage(cfg['flagSetsFilter'] if cfg['flagSetsFilter'] is not None else []),
1083
1092
  'segments': InMemorySegmentStorage(), # not used, just to avoid possible future errors.
1093
+ 'rule_based_segments': InMemoryRuleBasedSegmentStorage(),
1084
1094
  'impressions': LocalhostImpressionsStorage(),
1085
1095
  'events': LocalhostEventsStorage(),
1086
1096
  }
@@ -1088,6 +1098,7 @@ def _build_localhost_factory(cfg):
1088
1098
  synchronizers = SplitSynchronizers(
1089
1099
  LocalSplitSynchronizer(cfg['splitFile'],
1090
1100
  storages['splits'],
1101
+ storages['rule_based_segments'],
1091
1102
  localhost_mode),
1092
1103
  LocalSegmentSynchronizer(cfg['segmentDirectory'], storages['splits'], storages['segments']),
1093
1104
  None, None, None,
@@ -1151,6 +1162,7 @@ async def _build_localhost_factory_async(cfg):
1151
1162
  storages = {
1152
1163
  'splits': InMemorySplitStorageAsync(),
1153
1164
  'segments': InMemorySegmentStorageAsync(), # not used, just to avoid possible future errors.
1165
+ 'rule_based_segments': InMemoryRuleBasedSegmentStorageAsync(),
1154
1166
  'impressions': LocalhostImpressionsStorageAsync(),
1155
1167
  'events': LocalhostEventsStorageAsync(),
1156
1168
  }
@@ -1158,6 +1170,7 @@ async def _build_localhost_factory_async(cfg):
1158
1170
  synchronizers = SplitSynchronizers(
1159
1171
  LocalSplitSynchronizerAsync(cfg['splitFile'],
1160
1172
  storages['splits'],
1173
+ storages['rule_based_segments'],
1161
1174
  localhost_mode),
1162
1175
  LocalSegmentSynchronizerAsync(cfg['segmentDirectory'], storages['splits'], storages['segments']),
1163
1176
  None, None, None,