splitio-client 10.0.0__tar.gz → 10.1.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.0.0 → splitio_client-10.1.0}/PKG-INFO +3 -2
- {splitio_client-10.0.0 → splitio_client-10.1.0}/setup.py +4 -2
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/api/client.py +244 -42
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/client/config.py +21 -1
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/client/factory.py +23 -9
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/optional/loaders.py +12 -3
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/push/manager.py +6 -1
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/push/splitsse.py +5 -1
- splitio_client-10.1.0/splitio/version.py +1 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio_client.egg-info/PKG-INFO +3 -2
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio_client.egg-info/requires.txt +4 -0
- splitio_client-10.0.0/splitio/version.py +0 -1
- {splitio_client-10.0.0 → splitio_client-10.1.0}/README.md +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/setup.cfg +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/__init__.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/api/__init__.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/api/auth.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/api/commons.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/api/events.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/api/impressions.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/api/segments.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/api/splits.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/api/telemetry.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/client/__init__.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/client/client.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/client/input_validator.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/client/key.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/client/listener.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/client/localhost.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/client/manager.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/client/util.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/engine/__init__.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/engine/cache/__init__.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/engine/cache/lru.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/engine/evaluator.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/engine/filters.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/engine/hashfns/__init__.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/engine/hashfns/legacy.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/engine/hashfns/murmur3py.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/engine/impressions/__init__.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/engine/impressions/adapters.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/engine/impressions/impressions.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/engine/impressions/manager.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/engine/impressions/strategies.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/engine/impressions/unique_keys_tracker.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/engine/splitters.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/engine/telemetry.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/exceptions.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/factories.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/impressions.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/key.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/models/__init__.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/models/datatypes.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/models/events.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/models/grammar/__init__.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/models/grammar/condition.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/models/grammar/matchers/__init__.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/models/grammar/matchers/base.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/models/grammar/matchers/keys.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/models/grammar/matchers/misc.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/models/grammar/matchers/numeric.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/models/grammar/matchers/semver.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/models/grammar/matchers/sets.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/models/grammar/matchers/string.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/models/grammar/matchers/utils/__init__.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/models/grammar/matchers/utils/utils.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/models/grammar/partitions.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/models/impressions.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/models/notification.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/models/segments.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/models/splits.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/models/telemetry.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/models/token.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/optional/__init__.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/push/__init__.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/push/parser.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/push/processor.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/push/sse.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/push/status_tracker.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/push/workers.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/recorder/__init__.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/recorder/recorder.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/spec.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/storage/__init__.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/storage/adapters/__init__.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/storage/adapters/cache_trait.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/storage/adapters/redis.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/storage/adapters/util.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/storage/inmemmory.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/storage/pluggable.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/storage/redis.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/sync/__init__.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/sync/event.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/sync/impression.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/sync/manager.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/sync/segment.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/sync/split.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/sync/synchronizer.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/sync/telemetry.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/sync/unique_keys.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/sync/util.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/tasks/__init__.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/tasks/events_sync.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/tasks/impressions_sync.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/tasks/segment_sync.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/tasks/split_sync.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/tasks/telemetry_sync.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/tasks/unique_keys_sync.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/tasks/util/__init__.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/tasks/util/asynctask.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/tasks/util/workerpool.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/util/__init__.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/util/backoff.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/util/decorators.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/util/storage_helper.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/util/threadutil.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/util/time.py +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio_client.egg-info/SOURCES.txt +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio_client.egg-info/dependency_links.txt +0 -0
- {splitio_client-10.0.0 → splitio_client-10.1.0}/splitio_client.egg-info/top_level.txt +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: splitio_client
|
|
3
|
-
Version: 10.
|
|
3
|
+
Version: 10.1.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/10.
|
|
9
|
+
Download-URL: https://github.com/splitio/python-client/tarball/10.1.0
|
|
10
10
|
Description: # Split Python SDK
|
|
11
11
|

|
|
12
12
|
|
|
@@ -98,3 +98,4 @@ Provides-Extra: redis
|
|
|
98
98
|
Provides-Extra: uwsgi
|
|
99
99
|
Provides-Extra: cpphash
|
|
100
100
|
Provides-Extra: asyncio
|
|
101
|
+
Provides-Extra: kerberos
|
|
@@ -16,7 +16,8 @@ TESTS_REQUIRES = [
|
|
|
16
16
|
'attrs==22.1.0',
|
|
17
17
|
'pytest-asyncio==0.21.0',
|
|
18
18
|
'aiohttp>=3.8.4',
|
|
19
|
-
'aiofiles>=23.1.0'
|
|
19
|
+
'aiofiles>=23.1.0',
|
|
20
|
+
'requests-kerberos>=0.15.0'
|
|
20
21
|
]
|
|
21
22
|
|
|
22
23
|
INSTALL_REQUIRES = [
|
|
@@ -46,7 +47,8 @@ setup(
|
|
|
46
47
|
'redis': ['redis>=2.10.5'],
|
|
47
48
|
'uwsgi': ['uwsgi>=2.0.0'],
|
|
48
49
|
'cpphash': ['mmh3cffi==0.2.1'],
|
|
49
|
-
'asyncio': ['aiohttp>=3.8.4', 'aiofiles>=23.1.0']
|
|
50
|
+
'asyncio': ['aiohttp>=3.8.4', 'aiofiles>=23.1.0'],
|
|
51
|
+
'kerberos': ['requests-kerberos>=0.15.0']
|
|
50
52
|
},
|
|
51
53
|
setup_requires=['pytest-runner', 'pluggy==1.0.0;python_version<"3.8"'],
|
|
52
54
|
classifiers=[
|
|
@@ -5,7 +5,11 @@ import urllib
|
|
|
5
5
|
import abc
|
|
6
6
|
import logging
|
|
7
7
|
import json
|
|
8
|
+
import threading
|
|
9
|
+
from urllib3.util import parse_url
|
|
8
10
|
|
|
11
|
+
from splitio.optional.loaders import HTTPKerberosAuth, OPTIONAL
|
|
12
|
+
from splitio.client.config import AuthenticateScheme
|
|
9
13
|
from splitio.optional.loaders import aiohttp
|
|
10
14
|
from splitio.util.time import get_current_epoch_time_ms
|
|
11
15
|
|
|
@@ -15,7 +19,7 @@ AUTH_URL = 'https://auth.split.io/api'
|
|
|
15
19
|
TELEMETRY_URL = 'https://telemetry.split.io/api'
|
|
16
20
|
|
|
17
21
|
_LOGGER = logging.getLogger(__name__)
|
|
18
|
-
|
|
22
|
+
_EXC_MSG = '{source} library is throwing exceptions'
|
|
19
23
|
|
|
20
24
|
HttpResponse = namedtuple('HttpResponse', ['status_code', 'body', 'headers'])
|
|
21
25
|
|
|
@@ -67,6 +71,24 @@ class HttpClientException(Exception):
|
|
|
67
71
|
"""
|
|
68
72
|
Exception.__init__(self, message)
|
|
69
73
|
|
|
74
|
+
class HTTPAdapterWithProxyKerberosAuth(requests.adapters.HTTPAdapter):
|
|
75
|
+
"""HTTPAdapter override for Kerberos Proxy auth"""
|
|
76
|
+
|
|
77
|
+
def __init__(self, principal=None, password=None):
|
|
78
|
+
requests.adapters.HTTPAdapter.__init__(self)
|
|
79
|
+
self._principal = principal
|
|
80
|
+
self._password = password
|
|
81
|
+
|
|
82
|
+
def proxy_headers(self, proxy):
|
|
83
|
+
headers = {}
|
|
84
|
+
if self._principal is not None:
|
|
85
|
+
auth = HTTPKerberosAuth(principal=self._principal, password=self._password)
|
|
86
|
+
else:
|
|
87
|
+
auth = HTTPKerberosAuth()
|
|
88
|
+
negotiate_details = auth.generate_request_header(None, parse_url(proxy).host, is_preemptive=True)
|
|
89
|
+
headers['Proxy-Authorization'] = negotiate_details
|
|
90
|
+
return headers
|
|
91
|
+
|
|
70
92
|
class HttpClientBase(object, metaclass=abc.ABCMeta):
|
|
71
93
|
"""HttpClient wrapper template."""
|
|
72
94
|
|
|
@@ -91,6 +113,28 @@ class HttpClientBase(object, metaclass=abc.ABCMeta):
|
|
|
91
113
|
self._telemetry_runtime_producer = telemetry_runtime_producer
|
|
92
114
|
self._metric_name = metric_name
|
|
93
115
|
|
|
116
|
+
def _get_headers(self, extra_headers, sdk_key):
|
|
117
|
+
headers = _build_basic_headers(sdk_key)
|
|
118
|
+
if extra_headers is not None:
|
|
119
|
+
headers.update(extra_headers)
|
|
120
|
+
return headers
|
|
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)
|
|
94
138
|
|
|
95
139
|
class HttpClient(HttpClientBase):
|
|
96
140
|
"""HttpClient wrapper."""
|
|
@@ -110,6 +154,7 @@ class HttpClient(HttpClientBase):
|
|
|
110
154
|
:param telemetry_url: Optional alternative telemetry URL.
|
|
111
155
|
:type telemetry_url: str
|
|
112
156
|
"""
|
|
157
|
+
_LOGGER.debug("Initializing httpclient")
|
|
113
158
|
self._timeout = timeout/1000 if timeout else None # Convert ms to seconds.
|
|
114
159
|
self._urls = _construct_urls(sdk_url, events_url, auth_url, telemetry_url)
|
|
115
160
|
|
|
@@ -131,23 +176,19 @@ class HttpClient(HttpClientBase):
|
|
|
131
176
|
:return: Tuple of status_code & response text
|
|
132
177
|
:rtype: HttpResponse
|
|
133
178
|
"""
|
|
134
|
-
headers = _build_basic_headers(sdk_key)
|
|
135
|
-
if extra_headers is not None:
|
|
136
|
-
headers.update(extra_headers)
|
|
137
|
-
|
|
138
179
|
start = get_current_epoch_time_ms()
|
|
139
180
|
try:
|
|
140
181
|
response = requests.get(
|
|
141
182
|
_build_url(server, path, self._urls),
|
|
142
183
|
params=query,
|
|
143
|
-
headers=
|
|
184
|
+
headers=self._get_headers(extra_headers, sdk_key),
|
|
144
185
|
timeout=self._timeout
|
|
145
186
|
)
|
|
146
187
|
self._record_telemetry(response.status_code, get_current_epoch_time_ms() - start)
|
|
147
188
|
return HttpResponse(response.status_code, response.text, response.headers)
|
|
148
189
|
|
|
149
190
|
except Exception as exc: # pylint: disable=broad-except
|
|
150
|
-
raise HttpClientException('
|
|
191
|
+
raise HttpClientException(_EXC_MSG.format(source='request')) from exc
|
|
151
192
|
|
|
152
193
|
def post(self, server, path, sdk_key, body, query=None, extra_headers=None): # pylint: disable=too-many-arguments
|
|
153
194
|
"""
|
|
@@ -169,42 +210,19 @@ class HttpClient(HttpClientBase):
|
|
|
169
210
|
:return: Tuple of status_code & response text
|
|
170
211
|
:rtype: HttpResponse
|
|
171
212
|
"""
|
|
172
|
-
headers = _build_basic_headers(sdk_key)
|
|
173
|
-
|
|
174
|
-
if extra_headers is not None:
|
|
175
|
-
headers.update(extra_headers)
|
|
176
|
-
|
|
177
213
|
start = get_current_epoch_time_ms()
|
|
178
214
|
try:
|
|
179
215
|
response = requests.post(
|
|
180
216
|
_build_url(server, path, self._urls),
|
|
181
217
|
json=body,
|
|
182
218
|
params=query,
|
|
183
|
-
headers=
|
|
184
|
-
timeout=self._timeout
|
|
219
|
+
headers=self._get_headers(extra_headers, sdk_key),
|
|
220
|
+
timeout=self._timeout,
|
|
185
221
|
)
|
|
186
222
|
self._record_telemetry(response.status_code, get_current_epoch_time_ms() - start)
|
|
187
223
|
return HttpResponse(response.status_code, response.text, response.headers)
|
|
188
|
-
|
|
189
224
|
except Exception as exc: # pylint: disable=broad-except
|
|
190
|
-
raise HttpClientException('
|
|
191
|
-
|
|
192
|
-
def _record_telemetry(self, status_code, elapsed):
|
|
193
|
-
"""
|
|
194
|
-
Record Telemetry info
|
|
195
|
-
|
|
196
|
-
:param status_code: http request status code
|
|
197
|
-
:type status_code: int
|
|
198
|
-
|
|
199
|
-
:param elapsed: response time elapsed.
|
|
200
|
-
:type status_code: int
|
|
201
|
-
"""
|
|
202
|
-
self._telemetry_runtime_producer.record_sync_latency(self._metric_name, elapsed)
|
|
203
|
-
if 200 <= status_code < 300:
|
|
204
|
-
self._telemetry_runtime_producer.record_successful_sync(self._metric_name, get_current_epoch_time_ms())
|
|
205
|
-
return
|
|
206
|
-
self._telemetry_runtime_producer.record_sync_error(self._metric_name, status_code)
|
|
207
|
-
|
|
225
|
+
raise HttpClientException(_EXC_MSG.format(source='request')) from exc
|
|
208
226
|
|
|
209
227
|
class HttpClientAsync(HttpClientBase):
|
|
210
228
|
"""HttpClientAsync wrapper."""
|
|
@@ -243,10 +261,8 @@ class HttpClientAsync(HttpClientBase):
|
|
|
243
261
|
:return: Tuple of status_code & response text
|
|
244
262
|
:rtype: HttpResponse
|
|
245
263
|
"""
|
|
246
|
-
headers = _build_basic_headers(apikey)
|
|
247
|
-
if extra_headers is not None:
|
|
248
|
-
headers.update(extra_headers)
|
|
249
264
|
start = get_current_epoch_time_ms()
|
|
265
|
+
headers = self._get_headers(extra_headers, apikey)
|
|
250
266
|
try:
|
|
251
267
|
url = _build_url(server, path, self._urls)
|
|
252
268
|
_LOGGER.debug("GET request: %s", url)
|
|
@@ -266,7 +282,7 @@ class HttpClientAsync(HttpClientBase):
|
|
|
266
282
|
return HttpResponse(response.status, body, response.headers)
|
|
267
283
|
|
|
268
284
|
except aiohttp.ClientError as exc: # pylint: disable=broad-except
|
|
269
|
-
raise HttpClientException('aiohttp
|
|
285
|
+
raise HttpClientException(_EXC_MSG.format(source='aiohttp')) from exc
|
|
270
286
|
|
|
271
287
|
async def post(self, server, path, apikey, body, query=None, extra_headers=None): # pylint: disable=too-many-arguments
|
|
272
288
|
"""
|
|
@@ -286,9 +302,7 @@ class HttpClientAsync(HttpClientBase):
|
|
|
286
302
|
:return: Tuple of status_code & response text
|
|
287
303
|
:rtype: HttpResponse
|
|
288
304
|
"""
|
|
289
|
-
headers =
|
|
290
|
-
if extra_headers is not None:
|
|
291
|
-
headers.update(extra_headers)
|
|
305
|
+
headers = self._get_headers(extra_headers, apikey)
|
|
292
306
|
start = get_current_epoch_time_ms()
|
|
293
307
|
try:
|
|
294
308
|
headers['Accept-Encoding'] = 'gzip'
|
|
@@ -312,7 +326,7 @@ class HttpClientAsync(HttpClientBase):
|
|
|
312
326
|
return HttpResponse(response.status, body, response.headers)
|
|
313
327
|
|
|
314
328
|
except aiohttp.ClientError as exc: # pylint: disable=broad-except
|
|
315
|
-
raise HttpClientException('aiohttp
|
|
329
|
+
raise HttpClientException(_EXC_MSG.format(source='aiohttp')) from exc
|
|
316
330
|
|
|
317
331
|
async def _record_telemetry(self, status_code, elapsed):
|
|
318
332
|
"""
|
|
@@ -333,4 +347,192 @@ class HttpClientAsync(HttpClientBase):
|
|
|
333
347
|
|
|
334
348
|
async def close_session(self):
|
|
335
349
|
if not self._session.closed:
|
|
336
|
-
await self._session.close()
|
|
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())
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Default settings for the Split.IO SDK Python client."""
|
|
2
2
|
import os.path
|
|
3
3
|
import logging
|
|
4
|
+
from enum import Enum
|
|
4
5
|
|
|
5
6
|
from splitio.engine.impressions import ImpressionsMode
|
|
6
7
|
from splitio.client.input_validator import validate_flag_sets
|
|
@@ -8,6 +9,12 @@ from splitio.client.input_validator import validate_flag_sets
|
|
|
8
9
|
_LOGGER = logging.getLogger(__name__)
|
|
9
10
|
DEFAULT_DATA_SAMPLING = 1
|
|
10
11
|
|
|
12
|
+
class AuthenticateScheme(Enum):
|
|
13
|
+
"""Authentication Scheme."""
|
|
14
|
+
NONE = 'NONE'
|
|
15
|
+
KERBEROS_SPNEGO = 'KERBEROS_SPNEGO'
|
|
16
|
+
KERBEROS_PROXY = 'KERBEROS_PROXY'
|
|
17
|
+
|
|
11
18
|
DEFAULT_CONFIG = {
|
|
12
19
|
'operationMode': 'standalone',
|
|
13
20
|
'connectionTimeout': 1500,
|
|
@@ -59,7 +66,10 @@ DEFAULT_CONFIG = {
|
|
|
59
66
|
'storageWrapper': None,
|
|
60
67
|
'storagePrefix': None,
|
|
61
68
|
'storageType': None,
|
|
62
|
-
'flagSetsFilter': None
|
|
69
|
+
'flagSetsFilter': None,
|
|
70
|
+
'httpAuthenticateScheme': AuthenticateScheme.NONE,
|
|
71
|
+
'kerberosPrincipalUser': None,
|
|
72
|
+
'kerberosPrincipalPassword': None
|
|
63
73
|
}
|
|
64
74
|
|
|
65
75
|
def _parse_operation_mode(sdk_key, config):
|
|
@@ -148,4 +158,14 @@ def sanitize(sdk_key, config):
|
|
|
148
158
|
else:
|
|
149
159
|
processed['flagSetsFilter'] = sorted(validate_flag_sets(processed['flagSetsFilter'], 'SDK Config')) if processed['flagSetsFilter'] is not None else None
|
|
150
160
|
|
|
161
|
+
if config.get('httpAuthenticateScheme') is not None:
|
|
162
|
+
try:
|
|
163
|
+
authenticate_scheme = AuthenticateScheme(config['httpAuthenticateScheme'].upper())
|
|
164
|
+
except (ValueError, AttributeError):
|
|
165
|
+
authenticate_scheme = AuthenticateScheme.NONE
|
|
166
|
+
_LOGGER.warning('You passed an invalid HttpAuthenticationScheme, HttpAuthenticationScheme should be ' \
|
|
167
|
+
'one of the following values: `none`, `kerberos_proxy` or `kerberos_spnego`. '
|
|
168
|
+
' Defaulting to `none` mode.')
|
|
169
|
+
processed["httpAuthenticateScheme"] = authenticate_scheme
|
|
170
|
+
|
|
151
171
|
return processed
|
|
@@ -7,8 +7,8 @@ from enum import Enum
|
|
|
7
7
|
from splitio.optional.loaders import asyncio
|
|
8
8
|
from splitio.client.client import Client, ClientAsync
|
|
9
9
|
from splitio.client import input_validator
|
|
10
|
+
from splitio.client.config import sanitize as sanitize_config, DEFAULT_DATA_SAMPLING, AuthenticateScheme
|
|
10
11
|
from splitio.client.manager import SplitManager, SplitManagerAsync
|
|
11
|
-
from splitio.client.config import sanitize as sanitize_config, DEFAULT_DATA_SAMPLING
|
|
12
12
|
from splitio.client import util
|
|
13
13
|
from splitio.client.listener import ImpressionListenerWrapper, ImpressionListenerWrapperAsync
|
|
14
14
|
from splitio.engine.impressions.impressions import Manager as ImpressionsManager
|
|
@@ -33,7 +33,7 @@ from splitio.storage.pluggable import PluggableEventsStorage, PluggableImpressio
|
|
|
33
33
|
PluggableImpressionsStorageAsync, PluggableSegmentStorageAsync, PluggableSplitStorageAsync
|
|
34
34
|
|
|
35
35
|
# APIs
|
|
36
|
-
from splitio.api.client import HttpClient, HttpClientAsync
|
|
36
|
+
from splitio.api.client import HttpClient, HttpClientAsync, HttpClientKerberos
|
|
37
37
|
from splitio.api.splits import SplitsAPI, SplitsAPIAsync
|
|
38
38
|
from splitio.api.segments import SegmentsAPI, SegmentsAPIAsync
|
|
39
39
|
from splitio.api.impressions import ImpressionsAPI, ImpressionsAPIAsync
|
|
@@ -508,13 +508,27 @@ def _build_in_memory_factory(api_key, cfg, sdk_url=None, events_url=None, # pyl
|
|
|
508
508
|
telemetry_evaluation_producer = telemetry_producer.get_telemetry_evaluation_producer()
|
|
509
509
|
telemetry_init_producer = telemetry_producer.get_telemetry_init_producer()
|
|
510
510
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
511
|
+
authentication_params = None
|
|
512
|
+
if cfg.get("httpAuthenticateScheme") in [AuthenticateScheme.KERBEROS_SPNEGO, AuthenticateScheme.KERBEROS_PROXY]:
|
|
513
|
+
authentication_params = [cfg.get("kerberosPrincipalUser"),
|
|
514
|
+
cfg.get("kerberosPrincipalPassword")]
|
|
515
|
+
http_client = HttpClientKerberos(
|
|
516
|
+
sdk_url=sdk_url,
|
|
517
|
+
events_url=events_url,
|
|
518
|
+
auth_url=auth_api_base_url,
|
|
519
|
+
telemetry_url=telemetry_api_base_url,
|
|
520
|
+
timeout=cfg.get('connectionTimeout'),
|
|
521
|
+
authentication_scheme = cfg.get("httpAuthenticateScheme"),
|
|
522
|
+
authentication_params = authentication_params
|
|
523
|
+
)
|
|
524
|
+
else:
|
|
525
|
+
http_client = HttpClient(
|
|
526
|
+
sdk_url=sdk_url,
|
|
527
|
+
events_url=events_url,
|
|
528
|
+
auth_url=auth_api_base_url,
|
|
529
|
+
telemetry_url=telemetry_api_base_url,
|
|
530
|
+
timeout=cfg.get('connectionTimeout'),
|
|
531
|
+
)
|
|
518
532
|
|
|
519
533
|
sdk_metadata = util.get_metadata(cfg)
|
|
520
534
|
apis = {
|
|
@@ -14,8 +14,17 @@ except ImportError:
|
|
|
14
14
|
asyncio = missing_asyncio_dependencies
|
|
15
15
|
aiofiles = missing_asyncio_dependencies
|
|
16
16
|
|
|
17
|
+
try:
|
|
18
|
+
from requests_kerberos import HTTPKerberosAuth, OPTIONAL
|
|
19
|
+
except ImportError:
|
|
20
|
+
def missing_auth_dependencies(*_, **__):
|
|
21
|
+
"""Fail if missing dependencies are used."""
|
|
22
|
+
raise NotImplementedError(
|
|
23
|
+
'Missing kerberos auth dependency. '
|
|
24
|
+
'Please use `pip install splitio_client[kerberos]` to install the sdk with kerberos auth support'
|
|
25
|
+
)
|
|
26
|
+
HTTPKerberosAuth = missing_auth_dependencies
|
|
27
|
+
OPTIONAL = missing_auth_dependencies
|
|
28
|
+
|
|
17
29
|
async def _anext(it):
|
|
18
30
|
return await it.__anext__()
|
|
19
|
-
|
|
20
|
-
if sys.version_info.major < 3 or sys.version_info.minor < 10:
|
|
21
|
-
anext = _anext
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
import logging
|
|
3
3
|
from threading import Timer
|
|
4
4
|
import abc
|
|
5
|
-
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
from splitio.optional.loaders import asyncio
|
|
6
8
|
from splitio.api import APIException
|
|
7
9
|
from splitio.util.time import get_current_epoch_time_ms
|
|
8
10
|
from splitio.push import AuthException
|
|
@@ -14,6 +16,9 @@ from splitio.push.processor import MessageProcessor, MessageProcessorAsync
|
|
|
14
16
|
from splitio.push.status_tracker import PushStatusTracker, Status, PushStatusTrackerAsync
|
|
15
17
|
from splitio.models.telemetry import StreamingEventTypes
|
|
16
18
|
|
|
19
|
+
if sys.version_info.major == 3 and sys.version_info.minor < 10:
|
|
20
|
+
from splitio.optional.loaders import _anext as anext
|
|
21
|
+
|
|
17
22
|
_TOKEN_REFRESH_GRACE_PERIOD = 10 * 60 # 10 minutes
|
|
18
23
|
|
|
19
24
|
_LOGGER = logging.getLogger(__name__)
|
|
@@ -3,11 +3,15 @@ import logging
|
|
|
3
3
|
import threading
|
|
4
4
|
from enum import Enum
|
|
5
5
|
import abc
|
|
6
|
+
import sys
|
|
6
7
|
|
|
7
8
|
from splitio.push.sse import SSEClient, SSEClientAsync, SSE_EVENT_ERROR
|
|
8
9
|
from splitio.util.threadutil import EventGroup
|
|
9
10
|
from splitio.api import headers_from_metadata
|
|
10
|
-
from splitio.optional.loaders import
|
|
11
|
+
from splitio.optional.loaders import asyncio
|
|
12
|
+
|
|
13
|
+
if sys.version_info.major == 3 and sys.version_info.minor < 10:
|
|
14
|
+
from splitio.optional.loaders import _anext as anext
|
|
11
15
|
|
|
12
16
|
_LOGGER = logging.getLogger(__name__)
|
|
13
17
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '10.1.0'
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: splitio-client
|
|
3
|
-
Version: 10.
|
|
3
|
+
Version: 10.1.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/10.
|
|
9
|
+
Download-URL: https://github.com/splitio/python-client/tarball/10.1.0
|
|
10
10
|
Description: # Split Python SDK
|
|
11
11
|

|
|
12
12
|
|
|
@@ -98,3 +98,4 @@ Provides-Extra: redis
|
|
|
98
98
|
Provides-Extra: uwsgi
|
|
99
99
|
Provides-Extra: cpphash
|
|
100
100
|
Provides-Extra: asyncio
|
|
101
|
+
Provides-Extra: kerberos
|
|
@@ -13,6 +13,9 @@ aiofiles>=23.1.0
|
|
|
13
13
|
[cpphash]
|
|
14
14
|
mmh3cffi==0.2.1
|
|
15
15
|
|
|
16
|
+
[kerberos]
|
|
17
|
+
requests-kerberos>=0.15.0
|
|
18
|
+
|
|
16
19
|
[redis]
|
|
17
20
|
redis>=2.10.5
|
|
18
21
|
|
|
@@ -29,6 +32,7 @@ attrs==22.1.0
|
|
|
29
32
|
pytest-asyncio==0.21.0
|
|
30
33
|
aiohttp>=3.8.4
|
|
31
34
|
aiofiles>=23.1.0
|
|
35
|
+
requests-kerberos>=0.15.0
|
|
32
36
|
|
|
33
37
|
[uwsgi]
|
|
34
38
|
uwsgi>=2.0.0
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '10.0.0'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/engine/impressions/unique_keys_tracker.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/models/grammar/matchers/utils/__init__.py
RENAMED
|
File without changes
|
{splitio_client-10.0.0 → splitio_client-10.1.0}/splitio/models/grammar/matchers/utils/utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{splitio_client-10.0.0 → splitio_client-10.1.0}/splitio_client.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|