splitio-client 10.1.0rc2__tar.gz → 10.2.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.
- splitio_client-10.2.0/LICENSE.txt +13 -0
- splitio_client-10.2.0/PKG-INFO +125 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/api/client.py +235 -71
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/client/client.py +27 -24
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/client/factory.py +33 -26
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/engine/evaluator.py +2 -1
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/engine/impressions/__init__.py +20 -20
- splitio_client-10.2.0/splitio/engine/impressions/impressions.py +55 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/models/impressions.py +8 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/models/splits.py +17 -5
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/recorder/recorder.py +8 -8
- splitio_client-10.2.0/splitio/version.py +1 -0
- splitio_client-10.2.0/splitio_client.egg-info/PKG-INFO +125 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio_client.egg-info/SOURCES.txt +1 -0
- splitio_client-10.1.0rc2/PKG-INFO +0 -101
- splitio_client-10.1.0rc2/splitio/engine/impressions/impressions.py +0 -41
- splitio_client-10.1.0rc2/splitio/version.py +0 -1
- splitio_client-10.1.0rc2/splitio_client.egg-info/PKG-INFO +0 -101
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/README.md +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/setup.cfg +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/setup.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/__init__.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/api/__init__.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/api/auth.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/api/commons.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/api/events.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/api/impressions.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/api/segments.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/api/splits.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/api/telemetry.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/client/__init__.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/client/config.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/client/input_validator.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/client/key.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/client/listener.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/client/localhost.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/client/manager.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/client/util.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/engine/__init__.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/engine/cache/__init__.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/engine/cache/lru.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/engine/filters.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/engine/hashfns/__init__.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/engine/hashfns/legacy.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/engine/hashfns/murmur3py.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/engine/impressions/adapters.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/engine/impressions/manager.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/engine/impressions/strategies.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/engine/impressions/unique_keys_tracker.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/engine/splitters.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/engine/telemetry.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/exceptions.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/factories.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/impressions.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/key.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/models/__init__.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/models/datatypes.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/models/events.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/models/grammar/__init__.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/models/grammar/condition.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/models/grammar/matchers/__init__.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/models/grammar/matchers/base.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/models/grammar/matchers/keys.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/models/grammar/matchers/misc.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/models/grammar/matchers/numeric.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/models/grammar/matchers/semver.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/models/grammar/matchers/sets.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/models/grammar/matchers/string.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/models/grammar/matchers/utils/__init__.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/models/grammar/matchers/utils/utils.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/models/grammar/partitions.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/models/notification.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/models/segments.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/models/telemetry.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/models/token.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/optional/__init__.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/optional/loaders.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/push/__init__.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/push/manager.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/push/parser.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/push/processor.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/push/splitsse.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/push/sse.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/push/status_tracker.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/push/workers.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/recorder/__init__.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/spec.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/storage/__init__.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/storage/adapters/__init__.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/storage/adapters/cache_trait.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/storage/adapters/redis.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/storage/adapters/util.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/storage/inmemmory.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/storage/pluggable.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/storage/redis.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/sync/__init__.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/sync/event.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/sync/impression.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/sync/manager.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/sync/segment.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/sync/split.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/sync/synchronizer.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/sync/telemetry.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/sync/unique_keys.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/sync/util.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/tasks/__init__.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/tasks/events_sync.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/tasks/impressions_sync.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/tasks/segment_sync.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/tasks/split_sync.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/tasks/telemetry_sync.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/tasks/unique_keys_sync.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/tasks/util/__init__.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/tasks/util/asynctask.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/tasks/util/workerpool.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/util/__init__.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/util/backoff.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/util/decorators.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/util/storage_helper.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/util/threadutil.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio/util/time.py +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio_client.egg-info/dependency_links.txt +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio_client.egg-info/requires.txt +0 -0
- {splitio_client-10.1.0rc2 → splitio_client-10.2.0}/splitio_client.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright © 2025 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.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: splitio_client
|
|
3
|
+
Version: 10.2.0
|
|
4
|
+
Summary: Split.io Python Client
|
|
5
|
+
Home-page: https://github.com/splitio/python-client
|
|
6
|
+
Download-URL: https://github.com/splitio/python-client/tarball/10.2.0
|
|
7
|
+
Author: Patricio Echague, Sebastian Arrubia
|
|
8
|
+
Author-email: pato@split.io, sebastian@split.io
|
|
9
|
+
License: Apache License 2.0
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python
|
|
13
|
+
Classifier: Programming Language :: Python :: 2
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE.txt
|
|
18
|
+
Requires-Dist: requests
|
|
19
|
+
Requires-Dist: pyyaml
|
|
20
|
+
Requires-Dist: docopt>=0.6.2
|
|
21
|
+
Requires-Dist: enum34; python_version < "3.4"
|
|
22
|
+
Requires-Dist: bloom-filter2>=2.0.0
|
|
23
|
+
Provides-Extra: test
|
|
24
|
+
Requires-Dist: flake8; extra == "test"
|
|
25
|
+
Requires-Dist: pytest==7.0.1; extra == "test"
|
|
26
|
+
Requires-Dist: pytest-mock==3.11.1; extra == "test"
|
|
27
|
+
Requires-Dist: coverage; extra == "test"
|
|
28
|
+
Requires-Dist: pytest-cov==4.1.0; extra == "test"
|
|
29
|
+
Requires-Dist: importlib-metadata==6.7; extra == "test"
|
|
30
|
+
Requires-Dist: tomli==1.2.3; extra == "test"
|
|
31
|
+
Requires-Dist: iniconfig==1.1.1; extra == "test"
|
|
32
|
+
Requires-Dist: attrs==22.1.0; extra == "test"
|
|
33
|
+
Requires-Dist: pytest-asyncio==0.21.0; extra == "test"
|
|
34
|
+
Requires-Dist: aiohttp>=3.8.4; extra == "test"
|
|
35
|
+
Requires-Dist: aiofiles>=23.1.0; extra == "test"
|
|
36
|
+
Requires-Dist: requests-kerberos>=0.15.0; extra == "test"
|
|
37
|
+
Provides-Extra: redis
|
|
38
|
+
Requires-Dist: redis>=2.10.5; extra == "redis"
|
|
39
|
+
Provides-Extra: uwsgi
|
|
40
|
+
Requires-Dist: uwsgi>=2.0.0; extra == "uwsgi"
|
|
41
|
+
Provides-Extra: cpphash
|
|
42
|
+
Requires-Dist: mmh3cffi==0.2.1; extra == "cpphash"
|
|
43
|
+
Provides-Extra: asyncio
|
|
44
|
+
Requires-Dist: aiohttp>=3.8.4; extra == "asyncio"
|
|
45
|
+
Requires-Dist: aiofiles>=23.1.0; extra == "asyncio"
|
|
46
|
+
Provides-Extra: kerberos
|
|
47
|
+
Requires-Dist: requests-kerberos>=0.15.0; extra == "kerberos"
|
|
48
|
+
|
|
49
|
+
# Split Python SDK
|
|
50
|
+

|
|
51
|
+
|
|
52
|
+
## Overview
|
|
53
|
+
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.
|
|
54
|
+
|
|
55
|
+
[](https://twitter.com/intent/follow?screen_name=splitsoftware)
|
|
56
|
+
|
|
57
|
+
## Compatibility
|
|
58
|
+
This SDK is compatible with **Python 3.7 and higher**.
|
|
59
|
+
|
|
60
|
+
## Getting started
|
|
61
|
+
Below is a simple example that describes the instantiation and most basic usage of our SDK:
|
|
62
|
+
|
|
63
|
+
Run `pip install splitio_client`
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from splitio import get_factory
|
|
68
|
+
from splitio.exceptions import TimeoutException
|
|
69
|
+
factory = get_factory('YOUR_SDK_TYPE_API_KEY', config=config)
|
|
70
|
+
try:
|
|
71
|
+
factory.block_until_ready(5) # wait up to 5 seconds
|
|
72
|
+
split = factory.client()
|
|
73
|
+
treatment = split.get_treatment('CUSTOMER_ID', 'FEATURE_FLAG_NAME')
|
|
74
|
+
if treatment == "on":
|
|
75
|
+
# insert code here to show on treatment
|
|
76
|
+
elif treatment == "off":
|
|
77
|
+
# insert code here to show off treatment
|
|
78
|
+
else:
|
|
79
|
+
# insert your control treatment code here
|
|
80
|
+
except TimeoutException:
|
|
81
|
+
# Now the user can choose whether to abort the whole execution, or just keep going
|
|
82
|
+
# without a ready client, which if configured properly, should become ready at some point.
|
|
83
|
+
pass
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
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.
|
|
87
|
+
|
|
88
|
+
## Submitting issues
|
|
89
|
+
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.
|
|
90
|
+
|
|
91
|
+
## Contributing
|
|
92
|
+
Please see [Contributors Guide](CONTRIBUTORS-GUIDE.md) to find all you need to submit a Pull Request (PR).
|
|
93
|
+
|
|
94
|
+
## License
|
|
95
|
+
Licensed under the Apache License, Version 2.0. See: [Apache License](http://www.apache.org/licenses/).
|
|
96
|
+
|
|
97
|
+
## About Split
|
|
98
|
+
|
|
99
|
+
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.
|
|
100
|
+
|
|
101
|
+
To learn more about Split, contact hello@split.io, or get started with feature flags for free at https://www.split.io/signup.
|
|
102
|
+
|
|
103
|
+
Split has built and maintains SDKs for:
|
|
104
|
+
|
|
105
|
+
* .NET [Github](https://github.com/splitio/dotnet-client) [Docs](https://help.split.io/hc/en-us/articles/360020240172--NET-SDK)
|
|
106
|
+
* Android [Github](https://github.com/splitio/android-client) [Docs](https://help.split.io/hc/en-us/articles/360020343291-Android-SDK)
|
|
107
|
+
* Angular [Github](https://github.com/splitio/angular-sdk-plugin) [Docs](https://help.split.io/hc/en-us/articles/6495326064397-Angular-utilities)
|
|
108
|
+
* GO [Github](https://github.com/splitio/go-client) [Docs](https://help.split.io/hc/en-us/articles/360020093652-Go-SDK)
|
|
109
|
+
* iOS [Github](https://github.com/splitio/ios-client) [Docs](https://help.split.io/hc/en-us/articles/360020401491-iOS-SDK)
|
|
110
|
+
* Java [Github](https://github.com/splitio/java-client) [Docs](https://help.split.io/hc/en-us/articles/360020405151-Java-SDK)
|
|
111
|
+
* JavaScript [Github](https://github.com/splitio/javascript-client) [Docs](https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK)
|
|
112
|
+
* JavaScript for Browser [Github](https://github.com/splitio/javascript-browser-client) [Docs](https://help.split.io/hc/en-us/articles/360058730852-Browser-SDK)
|
|
113
|
+
* Node [Github](https://github.com/splitio/javascript-client) [Docs](https://help.split.io/hc/en-us/articles/360020564931-Node-js-SDK)
|
|
114
|
+
* PHP [Github](https://github.com/splitio/php-client) [Docs](https://help.split.io/hc/en-us/articles/360020350372-PHP-SDK)
|
|
115
|
+
* Python [Github](https://github.com/splitio/python-client) [Docs](https://help.split.io/hc/en-us/articles/360020359652-Python-SDK)
|
|
116
|
+
* React [Github](https://github.com/splitio/react-client) [Docs](https://help.split.io/hc/en-us/articles/360038825091-React-SDK)
|
|
117
|
+
* React Native [Github](https://github.com/splitio/react-native-client) [Docs](https://help.split.io/hc/en-us/articles/4406066357901-React-Native-SDK)
|
|
118
|
+
* Redux [Github](https://github.com/splitio/redux-client) [Docs](https://help.split.io/hc/en-us/articles/360038851551-Redux-SDK)
|
|
119
|
+
* Ruby [Github](https://github.com/splitio/ruby-client) [Docs](https://help.split.io/hc/en-us/articles/360020673251-Ruby-SDK)
|
|
120
|
+
|
|
121
|
+
For a comprehensive list of open source projects visit our [Github page](https://github.com/splitio?utf8=%E2%9C%93&query=%20only%3Apublic%20).
|
|
122
|
+
|
|
123
|
+
**Learn more about Split:**
|
|
124
|
+
|
|
125
|
+
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.
|
|
@@ -19,7 +19,7 @@ AUTH_URL = 'https://auth.split.io/api'
|
|
|
19
19
|
TELEMETRY_URL = 'https://telemetry.split.io/api'
|
|
20
20
|
|
|
21
21
|
_LOGGER = logging.getLogger(__name__)
|
|
22
|
-
|
|
22
|
+
_EXC_MSG = '{source} library is throwing exceptions'
|
|
23
23
|
|
|
24
24
|
HttpResponse = namedtuple('HttpResponse', ['status_code', 'body', 'headers'])
|
|
25
25
|
|
|
@@ -119,10 +119,27 @@ class HttpClientBase(object, metaclass=abc.ABCMeta):
|
|
|
119
119
|
headers.update(extra_headers)
|
|
120
120
|
return headers
|
|
121
121
|
|
|
122
|
+
def _record_telemetry(self, status_code, elapsed):
|
|
123
|
+
"""
|
|
124
|
+
Record Telemetry info
|
|
125
|
+
|
|
126
|
+
:param status_code: http request status code
|
|
127
|
+
:type status_code: int
|
|
128
|
+
|
|
129
|
+
:param elapsed: response time elapsed.
|
|
130
|
+
:type status_code: int
|
|
131
|
+
"""
|
|
132
|
+
self._telemetry_runtime_producer.record_sync_latency(self._metric_name, elapsed)
|
|
133
|
+
if 200 <= status_code < 300:
|
|
134
|
+
self._telemetry_runtime_producer.record_successful_sync(self._metric_name, get_current_epoch_time_ms())
|
|
135
|
+
return
|
|
136
|
+
|
|
137
|
+
self._telemetry_runtime_producer.record_sync_error(self._metric_name, status_code)
|
|
138
|
+
|
|
122
139
|
class HttpClient(HttpClientBase):
|
|
123
140
|
"""HttpClient wrapper."""
|
|
124
141
|
|
|
125
|
-
def __init__(self, timeout=None, sdk_url=None, events_url=None, auth_url=None, telemetry_url=None
|
|
142
|
+
def __init__(self, timeout=None, sdk_url=None, events_url=None, auth_url=None, telemetry_url=None):
|
|
126
143
|
"""
|
|
127
144
|
Class constructor.
|
|
128
145
|
|
|
@@ -140,9 +157,6 @@ class HttpClient(HttpClientBase):
|
|
|
140
157
|
_LOGGER.debug("Initializing httpclient")
|
|
141
158
|
self._timeout = timeout/1000 if timeout else None # Convert ms to seconds.
|
|
142
159
|
self._urls = _construct_urls(sdk_url, events_url, auth_url, telemetry_url)
|
|
143
|
-
self._authentication_scheme = authentication_scheme
|
|
144
|
-
self._authentication_params = authentication_params
|
|
145
|
-
self._lock = threading.RLock()
|
|
146
160
|
|
|
147
161
|
def get(self, server, path, sdk_key, query=None, extra_headers=None): # pylint: disable=too-many-arguments
|
|
148
162
|
"""
|
|
@@ -162,22 +176,19 @@ class HttpClient(HttpClientBase):
|
|
|
162
176
|
:return: Tuple of status_code & response text
|
|
163
177
|
:rtype: HttpResponse
|
|
164
178
|
"""
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
self.
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
except Exception as exc: # pylint: disable=broad-except
|
|
180
|
-
raise HttpClientException('requests library is throwing exceptions') from exc
|
|
179
|
+
start = get_current_epoch_time_ms()
|
|
180
|
+
try:
|
|
181
|
+
response = requests.get(
|
|
182
|
+
_build_url(server, path, self._urls),
|
|
183
|
+
params=query,
|
|
184
|
+
headers=self._get_headers(extra_headers, sdk_key),
|
|
185
|
+
timeout=self._timeout
|
|
186
|
+
)
|
|
187
|
+
self._record_telemetry(response.status_code, get_current_epoch_time_ms() - start)
|
|
188
|
+
return HttpResponse(response.status_code, response.text, response.headers)
|
|
189
|
+
|
|
190
|
+
except Exception as exc: # pylint: disable=broad-except
|
|
191
|
+
raise HttpClientException(_EXC_MSG.format(source='request')) from exc
|
|
181
192
|
|
|
182
193
|
def post(self, server, path, sdk_key, body, query=None, extra_headers=None): # pylint: disable=too-many-arguments
|
|
183
194
|
"""
|
|
@@ -199,54 +210,19 @@ class HttpClient(HttpClientBase):
|
|
|
199
210
|
:return: Tuple of status_code & response text
|
|
200
211
|
:rtype: HttpResponse
|
|
201
212
|
"""
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
self.
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
return HttpResponse(response.status_code, response.text, response.headers)
|
|
216
|
-
except Exception as exc: # pylint: disable=broad-except
|
|
217
|
-
raise HttpClientException('requests library is throwing exceptions') from exc
|
|
218
|
-
|
|
219
|
-
def _set_authentication(self, session):
|
|
220
|
-
if self._authentication_scheme == AuthenticateScheme.KERBEROS_SPNEGO:
|
|
221
|
-
_LOGGER.debug("Using Kerberos Spnego Authentication")
|
|
222
|
-
if self._authentication_params != [None, None]:
|
|
223
|
-
session.auth = HTTPKerberosAuth(principal=self._authentication_params[0], password=self._authentication_params[1], mutual_authentication=OPTIONAL)
|
|
224
|
-
else:
|
|
225
|
-
session.auth = HTTPKerberosAuth(mutual_authentication=OPTIONAL)
|
|
226
|
-
elif self._authentication_scheme == AuthenticateScheme.KERBEROS_PROXY:
|
|
227
|
-
_LOGGER.debug("Using Kerberos Proxy Authentication")
|
|
228
|
-
if self._authentication_params != [None, None]:
|
|
229
|
-
session.mount('https://', HTTPAdapterWithProxyKerberosAuth(principal=self._authentication_params[0], password=self._authentication_params[1]))
|
|
230
|
-
else:
|
|
231
|
-
session.mount('https://', HTTPAdapterWithProxyKerberosAuth())
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
def _record_telemetry(self, status_code, elapsed):
|
|
235
|
-
"""
|
|
236
|
-
Record Telemetry info
|
|
237
|
-
|
|
238
|
-
:param status_code: http request status code
|
|
239
|
-
:type status_code: int
|
|
240
|
-
|
|
241
|
-
:param elapsed: response time elapsed.
|
|
242
|
-
:type status_code: int
|
|
243
|
-
"""
|
|
244
|
-
self._telemetry_runtime_producer.record_sync_latency(self._metric_name, elapsed)
|
|
245
|
-
if 200 <= status_code < 300:
|
|
246
|
-
self._telemetry_runtime_producer.record_successful_sync(self._metric_name, get_current_epoch_time_ms())
|
|
247
|
-
return
|
|
248
|
-
|
|
249
|
-
self._telemetry_runtime_producer.record_sync_error(self._metric_name, status_code)
|
|
213
|
+
start = get_current_epoch_time_ms()
|
|
214
|
+
try:
|
|
215
|
+
response = requests.post(
|
|
216
|
+
_build_url(server, path, self._urls),
|
|
217
|
+
json=body,
|
|
218
|
+
params=query,
|
|
219
|
+
headers=self._get_headers(extra_headers, sdk_key),
|
|
220
|
+
timeout=self._timeout,
|
|
221
|
+
)
|
|
222
|
+
self._record_telemetry(response.status_code, get_current_epoch_time_ms() - start)
|
|
223
|
+
return HttpResponse(response.status_code, response.text, response.headers)
|
|
224
|
+
except Exception as exc: # pylint: disable=broad-except
|
|
225
|
+
raise HttpClientException(_EXC_MSG.format(source='request')) from exc
|
|
250
226
|
|
|
251
227
|
class HttpClientAsync(HttpClientBase):
|
|
252
228
|
"""HttpClientAsync wrapper."""
|
|
@@ -306,7 +282,7 @@ class HttpClientAsync(HttpClientBase):
|
|
|
306
282
|
return HttpResponse(response.status, body, response.headers)
|
|
307
283
|
|
|
308
284
|
except aiohttp.ClientError as exc: # pylint: disable=broad-except
|
|
309
|
-
raise HttpClientException('aiohttp
|
|
285
|
+
raise HttpClientException(_EXC_MSG.format(source='aiohttp')) from exc
|
|
310
286
|
|
|
311
287
|
async def post(self, server, path, apikey, body, query=None, extra_headers=None): # pylint: disable=too-many-arguments
|
|
312
288
|
"""
|
|
@@ -350,7 +326,7 @@ class HttpClientAsync(HttpClientBase):
|
|
|
350
326
|
return HttpResponse(response.status, body, response.headers)
|
|
351
327
|
|
|
352
328
|
except aiohttp.ClientError as exc: # pylint: disable=broad-except
|
|
353
|
-
raise HttpClientException('aiohttp
|
|
329
|
+
raise HttpClientException(_EXC_MSG.format(source='aiohttp')) from exc
|
|
354
330
|
|
|
355
331
|
async def _record_telemetry(self, status_code, elapsed):
|
|
356
332
|
"""
|
|
@@ -372,3 +348,191 @@ class HttpClientAsync(HttpClientBase):
|
|
|
372
348
|
async def close_session(self):
|
|
373
349
|
if not self._session.closed:
|
|
374
350
|
await self._session.close()
|
|
351
|
+
|
|
352
|
+
class HttpClientKerberos(HttpClientBase):
|
|
353
|
+
"""HttpClient wrapper."""
|
|
354
|
+
|
|
355
|
+
def __init__(self, timeout=None, sdk_url=None, events_url=None, auth_url=None, telemetry_url=None, authentication_scheme=None, authentication_params=None):
|
|
356
|
+
"""
|
|
357
|
+
Class constructor.
|
|
358
|
+
|
|
359
|
+
:param timeout: How many milliseconds to wait until the server responds.
|
|
360
|
+
:type timeout: int
|
|
361
|
+
:param sdk_url: Optional alternative sdk URL.
|
|
362
|
+
:type sdk_url: str
|
|
363
|
+
:param events_url: Optional alternative events URL.
|
|
364
|
+
:type events_url: str
|
|
365
|
+
:param auth_url: Optional alternative auth URL.
|
|
366
|
+
:type auth_url: str
|
|
367
|
+
:param telemetry_url: Optional alternative telemetry URL.
|
|
368
|
+
:type telemetry_url: str
|
|
369
|
+
:param authentication_scheme: Optional authentication scheme to use.
|
|
370
|
+
:type authentication_scheme: splitio.client.config.AuthenticateScheme
|
|
371
|
+
:param authentication_params: Optional authentication username and password to use.
|
|
372
|
+
:type authentication_params: [str, str]
|
|
373
|
+
"""
|
|
374
|
+
_LOGGER.debug("Initializing httpclient for Kerberos auth")
|
|
375
|
+
self._timeout = timeout/1000 if timeout else None # Convert ms to seconds.
|
|
376
|
+
self._urls = _construct_urls(sdk_url, events_url, auth_url, telemetry_url)
|
|
377
|
+
self._authentication_scheme = authentication_scheme
|
|
378
|
+
self._authentication_params = authentication_params
|
|
379
|
+
self._lock = threading.RLock()
|
|
380
|
+
self._sessions = {'sdk': requests.Session(),
|
|
381
|
+
'events': requests.Session(),
|
|
382
|
+
'auth': requests.Session(),
|
|
383
|
+
'telemetry': requests.Session()}
|
|
384
|
+
self._set_authentication()
|
|
385
|
+
|
|
386
|
+
def get(self, server, path, sdk_key, query=None, extra_headers=None): # pylint: disable=too-many-arguments
|
|
387
|
+
"""
|
|
388
|
+
Issue a get request.
|
|
389
|
+
:param server: Whether the request is for SDK server, Events server or Auth server.
|
|
390
|
+
:typee server: str
|
|
391
|
+
:param path: path to append to the host url.
|
|
392
|
+
:type path: str
|
|
393
|
+
:param sdk_key: sdk key.
|
|
394
|
+
:type sdk_key: str
|
|
395
|
+
:param query: Query string passed as dictionary.
|
|
396
|
+
:type query: dict
|
|
397
|
+
:param extra_headers: key/value pairs of possible extra headers.
|
|
398
|
+
:type extra_headers: dict
|
|
399
|
+
|
|
400
|
+
:return: Tuple of status_code & response text
|
|
401
|
+
:rtype: HttpResponse
|
|
402
|
+
"""
|
|
403
|
+
with self._lock:
|
|
404
|
+
start = get_current_epoch_time_ms()
|
|
405
|
+
try:
|
|
406
|
+
return self._do_get(server, path, sdk_key, query, extra_headers, start)
|
|
407
|
+
|
|
408
|
+
except requests.exceptions.ProxyError as exc:
|
|
409
|
+
_LOGGER.debug("Proxy Exception caught, resetting the http session")
|
|
410
|
+
self._sessions[server].close()
|
|
411
|
+
self._sessions[server] = requests.Session()
|
|
412
|
+
self._set_authentication(server_name=server)
|
|
413
|
+
try:
|
|
414
|
+
return self._do_get(server, path, sdk_key, query, extra_headers, start)
|
|
415
|
+
|
|
416
|
+
except Exception as exc:
|
|
417
|
+
raise HttpClientException(_EXC_MSG.format(source='request')) from exc
|
|
418
|
+
|
|
419
|
+
except Exception as exc: # pylint: disable=broad-except
|
|
420
|
+
raise HttpClientException(_EXC_MSG.format(source='request')) from exc
|
|
421
|
+
|
|
422
|
+
def _do_get(self, server, path, sdk_key, query, extra_headers, start):
|
|
423
|
+
"""
|
|
424
|
+
Issue a get request.
|
|
425
|
+
:param server: Whether the request is for SDK server, Events server or Auth server.
|
|
426
|
+
:typee server: str
|
|
427
|
+
:param path: path to append to the host url.
|
|
428
|
+
:type path: str
|
|
429
|
+
:param sdk_key: sdk key.
|
|
430
|
+
:type sdk_key: str
|
|
431
|
+
:param query: Query string passed as dictionary.
|
|
432
|
+
:type query: dict
|
|
433
|
+
:param extra_headers: key/value pairs of possible extra headers.
|
|
434
|
+
:type extra_headers: dict
|
|
435
|
+
|
|
436
|
+
:return: Tuple of status_code & response text
|
|
437
|
+
:rtype: HttpResponse
|
|
438
|
+
"""
|
|
439
|
+
with self._sessions[server].get(
|
|
440
|
+
_build_url(server, path, self._urls),
|
|
441
|
+
headers=self._get_headers(extra_headers, sdk_key),
|
|
442
|
+
params=query,
|
|
443
|
+
timeout=self._timeout
|
|
444
|
+
) as response:
|
|
445
|
+
self._record_telemetry(response.status_code, get_current_epoch_time_ms() - start)
|
|
446
|
+
return HttpResponse(response.status_code, response.text, response.headers)
|
|
447
|
+
|
|
448
|
+
def post(self, server, path, sdk_key, body, query=None, extra_headers=None): # pylint: disable=too-many-arguments
|
|
449
|
+
"""
|
|
450
|
+
Issue a POST request.
|
|
451
|
+
|
|
452
|
+
:param server: Whether the request is for SDK server or Events server.
|
|
453
|
+
:typee server: str
|
|
454
|
+
:param path: path to append to the host url.
|
|
455
|
+
:type path: str
|
|
456
|
+
:param sdk_key: sdk key.
|
|
457
|
+
:type sdk_key: str
|
|
458
|
+
:param body: body sent in the request.
|
|
459
|
+
:type body: str
|
|
460
|
+
:param query: Query string passed as dictionary.
|
|
461
|
+
:type query: dict
|
|
462
|
+
:param extra_headers: key/value pairs of possible extra headers.
|
|
463
|
+
:type extra_headers: dict
|
|
464
|
+
|
|
465
|
+
:return: Tuple of status_code & response text
|
|
466
|
+
:rtype: HttpResponse
|
|
467
|
+
"""
|
|
468
|
+
with self._lock:
|
|
469
|
+
start = get_current_epoch_time_ms()
|
|
470
|
+
try:
|
|
471
|
+
return self._do_post(server, path, sdk_key, query, extra_headers, body, start)
|
|
472
|
+
|
|
473
|
+
except requests.exceptions.ProxyError as exc:
|
|
474
|
+
_LOGGER.debug("Proxy Exception caught, resetting the http session")
|
|
475
|
+
self._sessions[server].close()
|
|
476
|
+
self._sessions[server] = requests.Session()
|
|
477
|
+
self._set_authentication(server_name=server)
|
|
478
|
+
try:
|
|
479
|
+
return self._do_post(server, path, sdk_key, query, extra_headers, body, start)
|
|
480
|
+
|
|
481
|
+
except Exception as exc:
|
|
482
|
+
raise HttpClientException(_EXC_MSG.format(source='request')) from exc
|
|
483
|
+
|
|
484
|
+
except Exception as exc: # pylint: disable=broad-except
|
|
485
|
+
raise HttpClientException(_EXC_MSG.format(source='request')) from exc
|
|
486
|
+
|
|
487
|
+
def _do_post(self, server, path, sdk_key, query, extra_headers, body, start):
|
|
488
|
+
"""
|
|
489
|
+
Issue a POST request.
|
|
490
|
+
|
|
491
|
+
:param server: Whether the request is for SDK server or Events server.
|
|
492
|
+
:typee server: str
|
|
493
|
+
:param path: path to append to the host url.
|
|
494
|
+
:type path: str
|
|
495
|
+
:param sdk_key: sdk key.
|
|
496
|
+
:type sdk_key: str
|
|
497
|
+
:param body: body sent in the request.
|
|
498
|
+
:type body: str
|
|
499
|
+
:param query: Query string passed as dictionary.
|
|
500
|
+
:type query: dict
|
|
501
|
+
:param extra_headers: key/value pairs of possible extra headers.
|
|
502
|
+
:type extra_headers: dict
|
|
503
|
+
|
|
504
|
+
:return: Tuple of status_code & response text
|
|
505
|
+
:rtype: HttpResponse
|
|
506
|
+
"""
|
|
507
|
+
with self._sessions[server].post(
|
|
508
|
+
_build_url(server, path, self._urls),
|
|
509
|
+
params=query,
|
|
510
|
+
headers=self._get_headers(extra_headers, sdk_key),
|
|
511
|
+
json=body,
|
|
512
|
+
timeout=self._timeout,
|
|
513
|
+
) as response:
|
|
514
|
+
self._record_telemetry(response.status_code, get_current_epoch_time_ms() - start)
|
|
515
|
+
return HttpResponse(response.status_code, response.text, response.headers)
|
|
516
|
+
|
|
517
|
+
def _set_authentication(self, server_name=None):
|
|
518
|
+
"""
|
|
519
|
+
Set the authentication for all self._sessions variables based on authentication scheme.
|
|
520
|
+
|
|
521
|
+
:param server: If set, will only add the auth for its session variable, otherwise will set all sessions.
|
|
522
|
+
:typee server: str
|
|
523
|
+
"""
|
|
524
|
+
for server in ['sdk', 'events', 'auth', 'telemetry']:
|
|
525
|
+
if server_name is not None and server_name != server:
|
|
526
|
+
continue
|
|
527
|
+
if self._authentication_scheme == AuthenticateScheme.KERBEROS_SPNEGO:
|
|
528
|
+
_LOGGER.debug("Using Kerberos Spnego Authentication")
|
|
529
|
+
if self._authentication_params != [None, None]:
|
|
530
|
+
self._sessions[server].auth = HTTPKerberosAuth(principal=self._authentication_params[0], password=self._authentication_params[1], mutual_authentication=OPTIONAL)
|
|
531
|
+
else:
|
|
532
|
+
self._sessions[server].auth = HTTPKerberosAuth(mutual_authentication=OPTIONAL)
|
|
533
|
+
elif self._authentication_scheme == AuthenticateScheme.KERBEROS_PROXY:
|
|
534
|
+
_LOGGER.debug("Using Kerberos Proxy Authentication")
|
|
535
|
+
if self._authentication_params != [None, None]:
|
|
536
|
+
self._sessions[server].mount('https://', HTTPAdapterWithProxyKerberosAuth(principal=self._authentication_params[0], password=self._authentication_params[1]))
|
|
537
|
+
else:
|
|
538
|
+
self._sessions[server].mount('https://', HTTPAdapterWithProxyKerberosAuth())
|
|
@@ -3,7 +3,7 @@ import logging
|
|
|
3
3
|
|
|
4
4
|
from splitio.engine.evaluator import Evaluator, CONTROL, EvaluationDataFactory, AsyncEvaluationDataFactory
|
|
5
5
|
from splitio.engine.splitters import Splitter
|
|
6
|
-
from splitio.models.impressions import Impression, Label
|
|
6
|
+
from splitio.models.impressions import Impression, Label, ImpressionDecorated
|
|
7
7
|
from splitio.models.events import Event, EventWrapper
|
|
8
8
|
from splitio.models.telemetry import get_latency_bucket_index, MethodExceptionsAndLatencies
|
|
9
9
|
from splitio.client import input_validator
|
|
@@ -22,7 +22,8 @@ class ClientBase(object): # pylint: disable=too-many-instance-attributes
|
|
|
22
22
|
'impression': {
|
|
23
23
|
'label': Label.EXCEPTION,
|
|
24
24
|
'change_number': None,
|
|
25
|
-
}
|
|
25
|
+
},
|
|
26
|
+
'impressions_disabled': False
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
_NON_READY_EVAL_RESULT = {
|
|
@@ -31,7 +32,8 @@ class ClientBase(object): # pylint: disable=too-many-instance-attributes
|
|
|
31
32
|
'impression': {
|
|
32
33
|
'label': Label.NOT_READY,
|
|
33
34
|
'change_number': None
|
|
34
|
-
}
|
|
35
|
+
},
|
|
36
|
+
'impressions_disabled': False
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
def __init__(self, factory, recorder, labels_enabled=True):
|
|
@@ -116,14 +118,15 @@ class ClientBase(object): # pylint: disable=too-many-instance-attributes
|
|
|
116
118
|
|
|
117
119
|
def _build_impression(self, key, bucketing, feature, result):
|
|
118
120
|
"""Build an impression based on evaluation data & it's result."""
|
|
119
|
-
return
|
|
120
|
-
matching_key=key,
|
|
121
|
+
return ImpressionDecorated(
|
|
122
|
+
Impression(matching_key=key,
|
|
121
123
|
feature_name=feature,
|
|
122
124
|
treatment=result['treatment'],
|
|
123
125
|
label=result['impression']['label'] if self._labels_enabled else None,
|
|
124
126
|
change_number=result['impression']['change_number'],
|
|
125
127
|
bucketing_key=bucketing,
|
|
126
|
-
time=utctime_ms())
|
|
128
|
+
time=utctime_ms()),
|
|
129
|
+
disabled=result['impressions_disabled'])
|
|
127
130
|
|
|
128
131
|
def _build_impressions(self, key, bucketing, results):
|
|
129
132
|
"""Build an impression based on evaluation data & it's result."""
|
|
@@ -296,8 +299,8 @@ class Client(ClientBase): # pylint: disable=too-many-instance-attributes
|
|
|
296
299
|
result = self._FAILED_EVAL_RESULT
|
|
297
300
|
|
|
298
301
|
if result['impression']['label'] != Label.SPLIT_NOT_FOUND:
|
|
299
|
-
|
|
300
|
-
self._record_stats([(
|
|
302
|
+
impression_decorated = self._build_impression(key, bucketing, feature, result)
|
|
303
|
+
self._record_stats([(impression_decorated, attributes)], start, method)
|
|
301
304
|
|
|
302
305
|
return result['treatment'], result['configurations']
|
|
303
306
|
|
|
@@ -571,23 +574,23 @@ class Client(ClientBase): # pylint: disable=too-many-instance-attributes
|
|
|
571
574
|
self._telemetry_evaluation_producer.record_exception(method)
|
|
572
575
|
results = {n: self._FAILED_EVAL_RESULT for n in features}
|
|
573
576
|
|
|
574
|
-
|
|
577
|
+
imp_decorated_attrs = [
|
|
575
578
|
(i, attributes) for i in self._build_impressions(key, bucketing, results)
|
|
576
|
-
if i.label != Label.SPLIT_NOT_FOUND
|
|
579
|
+
if i.Impression.label != Label.SPLIT_NOT_FOUND
|
|
577
580
|
]
|
|
578
|
-
self._record_stats(
|
|
581
|
+
self._record_stats(imp_decorated_attrs, start, method)
|
|
579
582
|
|
|
580
583
|
return {
|
|
581
584
|
feature: (results[feature]['treatment'], results[feature]['configurations'])
|
|
582
585
|
for feature in results
|
|
583
586
|
}
|
|
584
587
|
|
|
585
|
-
def _record_stats(self,
|
|
588
|
+
def _record_stats(self, impressions_decorated, start, operation):
|
|
586
589
|
"""
|
|
587
590
|
Record impressions.
|
|
588
591
|
|
|
589
|
-
:param
|
|
590
|
-
:type
|
|
592
|
+
:param impressions_decorated: Generated impressions
|
|
593
|
+
:type impressions_decorated: list[tuple[splitio.models.impression.ImpressionDecorated, dict]]
|
|
591
594
|
|
|
592
595
|
:param start: timestamp when get_treatment or get_treatments was called
|
|
593
596
|
:type start: int
|
|
@@ -596,7 +599,7 @@ class Client(ClientBase): # pylint: disable=too-many-instance-attributes
|
|
|
596
599
|
:type operation: str
|
|
597
600
|
"""
|
|
598
601
|
end = get_current_epoch_time_ms()
|
|
599
|
-
self._recorder.record_treatment_stats(
|
|
602
|
+
self._recorder.record_treatment_stats(impressions_decorated, get_latency_bucket_index(end - start),
|
|
600
603
|
operation, 'get_' + operation.value)
|
|
601
604
|
|
|
602
605
|
def track(self, key, traffic_type, event_type, value=None, properties=None):
|
|
@@ -763,8 +766,8 @@ class ClientAsync(ClientBase): # pylint: disable=too-many-instance-attributes
|
|
|
763
766
|
result = self._FAILED_EVAL_RESULT
|
|
764
767
|
|
|
765
768
|
if result['impression']['label'] != Label.SPLIT_NOT_FOUND:
|
|
766
|
-
|
|
767
|
-
await self._record_stats([(
|
|
769
|
+
impression_decorated = self._build_impression(key, bucketing, feature, result)
|
|
770
|
+
await self._record_stats([(impression_decorated, attributes)], start, method)
|
|
768
771
|
return result['treatment'], result['configurations']
|
|
769
772
|
|
|
770
773
|
async def get_treatments(self, key, feature_flag_names, attributes=None):
|
|
@@ -960,23 +963,23 @@ class ClientAsync(ClientBase): # pylint: disable=too-many-instance-attributes
|
|
|
960
963
|
await self._telemetry_evaluation_producer.record_exception(method)
|
|
961
964
|
results = {n: self._FAILED_EVAL_RESULT for n in features}
|
|
962
965
|
|
|
963
|
-
|
|
966
|
+
imp_decorated_attrs = [
|
|
964
967
|
(i, attributes) for i in self._build_impressions(key, bucketing, results)
|
|
965
|
-
if i.label != Label.SPLIT_NOT_FOUND
|
|
968
|
+
if i.Impression.label != Label.SPLIT_NOT_FOUND
|
|
966
969
|
]
|
|
967
|
-
await self._record_stats(
|
|
970
|
+
await self._record_stats(imp_decorated_attrs, start, method)
|
|
968
971
|
|
|
969
972
|
return {
|
|
970
973
|
feature: (res['treatment'], res['configurations'])
|
|
971
974
|
for feature, res in results.items()
|
|
972
975
|
}
|
|
973
976
|
|
|
974
|
-
async def _record_stats(self,
|
|
977
|
+
async def _record_stats(self, impressions_decorated, start, operation):
|
|
975
978
|
"""
|
|
976
979
|
Record impressions for async calls
|
|
977
980
|
|
|
978
|
-
:param
|
|
979
|
-
:type
|
|
981
|
+
:param impressions_decorated: Generated impressions decorated
|
|
982
|
+
:type impressions_decorated: list[tuple[splitio.models.impression.Impression, dict]]
|
|
980
983
|
|
|
981
984
|
:param start: timestamp when get_treatment or get_treatments was called
|
|
982
985
|
:type start: int
|
|
@@ -985,7 +988,7 @@ class ClientAsync(ClientBase): # pylint: disable=too-many-instance-attributes
|
|
|
985
988
|
:type operation: str
|
|
986
989
|
"""
|
|
987
990
|
end = get_current_epoch_time_ms()
|
|
988
|
-
await self._recorder.record_treatment_stats(
|
|
991
|
+
await self._recorder.record_treatment_stats(impressions_decorated, get_latency_bucket_index(end - start),
|
|
989
992
|
operation, 'get_' + operation.value)
|
|
990
993
|
|
|
991
994
|
async def track(self, key, traffic_type, event_type, value=None, properties=None):
|