devcycle-python-server-sdk 3.6.0__py3-none-any.whl → 3.7.1__py3-none-any.whl

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.
@@ -1 +1 @@
1
- 3.6.0
1
+ 3.7.1
@@ -30,7 +30,7 @@ class ConfigAPIClient:
30
30
  self.max_config_retries = 2
31
31
  self.config_file_url = (
32
32
  slash_join(
33
- self.options.config_cdn_uri, "config", "v1", "server", self.sdk_key
33
+ self.options.config_cdn_uri, "config", "v2", "server", self.sdk_key
34
34
  )
35
35
  + ".json"
36
36
  )
@@ -289,10 +289,13 @@ class LocalBucketing:
289
289
 
290
290
  return bytes(ret)
291
291
 
292
- def init_event_queue(self, options_json: str) -> None:
292
+ def init_event_queue(self, client_uuid, options_json: str) -> None:
293
293
  with self.wasm_lock:
294
+ client_uuid_addr = self._new_assembly_script_string(client_uuid)
294
295
  options_addr = self._new_assembly_script_string(options_json)
295
- self.initEventQueue(self.wasm_store, self.sdk_key_addr, options_addr)
296
+ self.initEventQueue(
297
+ self.wasm_store, self.sdk_key_addr, client_uuid_addr, options_addr
298
+ )
296
299
 
297
300
  def get_variable_for_user_protobuf(
298
301
  self, user: DevCycleUser, key: str, default_value: Any
@@ -1,5 +1,6 @@
1
1
  import json
2
2
  import logging
3
+ import uuid
3
4
  from numbers import Real
4
5
  from typing import Any, Dict, Union
5
6
 
@@ -28,6 +29,7 @@ class DevCycleLocalClient(AbstractDevCycleClient):
28
29
  def __init__(self, sdk_key: str, options: DevCycleLocalOptions):
29
30
  _validate_sdk_key(sdk_key)
30
31
  self._sdk_key = sdk_key
32
+ self.client_uuid = str(uuid.uuid4())
31
33
 
32
34
  if options is None:
33
35
  self.options: DevCycleLocalOptions = DevCycleLocalOptions()
@@ -45,7 +47,7 @@ class DevCycleLocalClient(AbstractDevCycleClient):
45
47
  sdk_key, self.options, self.local_bucketing
46
48
  )
47
49
  self.event_queue_manager: EventQueueManager = EventQueueManager(
48
- sdk_key, self.options, self.local_bucketing
50
+ sdk_key, self.client_uuid, self.options, self.local_bucketing
49
51
  )
50
52
 
51
53
  self._openfeature_provider = DevCycleProvider(self)
@@ -1,16 +1,21 @@
1
+ import json
2
+ import logging
1
3
  import threading
2
4
  import time
3
- import logging
4
- import json
5
+ from datetime import datetime
5
6
  from typing import Optional
6
7
 
7
- from devcycle_python_sdk.options import DevCycleLocalOptions
8
- from devcycle_python_sdk.api.local_bucketing import LocalBucketing
8
+ import ld_eventsource.actions
9
+
9
10
  from devcycle_python_sdk.api.config_client import ConfigAPIClient
11
+ from devcycle_python_sdk.api.local_bucketing import LocalBucketing
10
12
  from devcycle_python_sdk.exceptions import (
11
13
  CloudClientUnauthorizedError,
12
14
  CloudClientError,
13
15
  )
16
+ from wsgiref.handlers import format_date_time
17
+ from devcycle_python_sdk.options import DevCycleLocalOptions
18
+ from devcycle_python_sdk.managers.sse_manager import SSEManager
14
19
 
15
20
  logger = logging.getLogger(__name__)
16
21
 
@@ -27,7 +32,9 @@ class EnvironmentConfigManager(threading.Thread):
27
32
  self._sdk_key = sdk_key
28
33
  self._options = options
29
34
  self._local_bucketing = local_bucketing
30
-
35
+ self._sse_manager: Optional[SSEManager] = None
36
+ self._sse_polling_interval = 1000 * 60 * 15 * 60
37
+ self._sse_connected = False
31
38
  self._config: Optional[dict] = None
32
39
  self._config_etag: Optional[str] = None
33
40
  self._config_lastmodified: Optional[str] = None
@@ -41,10 +48,15 @@ class EnvironmentConfigManager(threading.Thread):
41
48
  def is_initialized(self) -> bool:
42
49
  return self._config is not None
43
50
 
44
- def _get_config(self):
51
+ def _get_config(self, last_modified: Optional[float] = None):
45
52
  try:
53
+ lm_header = self._config_lastmodified
54
+ if last_modified is not None:
55
+ lm_timestamp = datetime.fromtimestamp(last_modified)
56
+ lm_header = format_date_time(time.mktime(lm_timestamp.timetuple()))
57
+
46
58
  new_config, new_etag, new_lastmodified = self._config_api_client.get_config(
47
- config_etag=self._config_etag, last_modified=self._config_lastmodified
59
+ config_etag=self._config_etag, last_modified=lm_header
48
60
  )
49
61
 
50
62
  # Abort early if the last modified is before the sent one.
@@ -68,6 +80,14 @@ class EnvironmentConfigManager(threading.Thread):
68
80
 
69
81
  json_config = json.dumps(self._config)
70
82
  self._local_bucketing.store_config(json_config)
83
+ if self._options.enable_beta_realtime_updates:
84
+ if self._sse_manager is None:
85
+ self._sse_manager = SSEManager(
86
+ self.sse_state,
87
+ self.sse_error,
88
+ self.sse_message,
89
+ )
90
+ self._sse_manager.update(self._config)
71
91
 
72
92
  if (
73
93
  trigger_on_client_initialized
@@ -98,7 +118,32 @@ class EnvironmentConfigManager(threading.Thread):
98
118
  logger.warning(
99
119
  f"DevCycle: Error polling for config changes: {str(e)}"
100
120
  )
101
- time.sleep(self._options.config_polling_interval_ms / 1000.0)
121
+ if self._sse_connected:
122
+ time.sleep(self._sse_polling_interval / 1000.0)
123
+ else:
124
+ time.sleep(self._options.config_polling_interval_ms / 1000.0)
125
+
126
+ def sse_message(self, message: ld_eventsource.actions.Event):
127
+ if self._sse_connected is False:
128
+ self._sse_connected = True
129
+ logger.info("DevCycle: Connected to SSE stream")
130
+ logger.info(f"DevCycle: Received message: {message.data}")
131
+ sse_message = json.loads(message.data)
132
+ dvc_data = json.loads(sse_message.get("data"))
133
+ if (
134
+ dvc_data.get("type") == "refetchConfig"
135
+ or dvc_data.get("type") == ""
136
+ or dvc_data.get("type") is None
137
+ ):
138
+ logger.info("DevCycle: Received refetchConfig message - updating config")
139
+ self._get_config(dvc_data["lastModified"])
140
+
141
+ def sse_error(self, error: ld_eventsource.actions.Fault):
142
+ logger.warning(f"DevCycle: Received SSE error: {error}")
143
+
144
+ def sse_state(self, state: ld_eventsource.actions.Start):
145
+ self._sse_connected = True
146
+ logger.info("DevCycle: Connected to SSE stream")
102
147
 
103
148
  def close(self):
104
149
  self._polling_enabled = False
@@ -31,6 +31,7 @@ class EventQueueManager(threading.Thread):
31
31
  def __init__(
32
32
  self,
33
33
  sdk_key: str,
34
+ client_uuid: str,
34
35
  options: DevCycleLocalOptions,
35
36
  local_bucketing: LocalBucketing,
36
37
  ):
@@ -49,7 +50,7 @@ class EventQueueManager(threading.Thread):
49
50
 
50
51
  # Setup the event queue inside the WASM module
51
52
  event_options_json = json.dumps(self._options.event_queue_options())
52
- self._local_bucketing.init_event_queue(event_options_json)
53
+ self._local_bucketing.init_event_queue(client_uuid, event_options_json)
53
54
 
54
55
  # Only start event processing thread if event logging is enabled
55
56
  if not (
@@ -0,0 +1,63 @@
1
+ import threading
2
+
3
+ import ld_eventsource
4
+ import ld_eventsource.actions
5
+ import ld_eventsource.config
6
+ from typing import Callable
7
+
8
+
9
+ class SSEManager:
10
+ def __init__(
11
+ self,
12
+ handlestate: Callable[[ld_eventsource.actions.Start], None],
13
+ handleerror: Callable[[ld_eventsource.actions.Fault], None],
14
+ handlemessage: Callable[[ld_eventsource.actions.Event], None],
15
+ ):
16
+ self.client: ld_eventsource.SSEClient = None
17
+ self.url = ""
18
+ self.handlestate = handlestate
19
+ self.handleerror = handleerror
20
+ self.handlemessage = handlemessage
21
+
22
+ self.read_thread = threading.Thread(
23
+ target=self.read_events,
24
+ args=(self.handlestate, self.handleerror, self.handlemessage),
25
+ )
26
+
27
+ def read_events(
28
+ self,
29
+ handlestate: Callable[[ld_eventsource.actions.Start], None],
30
+ handleerror: Callable[[ld_eventsource.actions.Fault], None],
31
+ handlemessage: Callable[[ld_eventsource.actions.Event], None],
32
+ ):
33
+ self.client.start()
34
+ for event in self.client.all:
35
+ if isinstance(event, ld_eventsource.actions.Start):
36
+ handlestate(event)
37
+ elif isinstance(event, ld_eventsource.actions.Fault):
38
+ handleerror(event)
39
+ elif isinstance(event, ld_eventsource.actions.Event):
40
+ handlemessage(event)
41
+
42
+ def update(self, config: dict):
43
+ if self.use_new_config(config["sse"]):
44
+ self.url = config["sse"]["hostname"] + config["sse"]["path"]
45
+ if self.client is not None:
46
+ self.client.close()
47
+ if self.read_thread.is_alive():
48
+ self.read_thread.join()
49
+ self.client = ld_eventsource.SSEClient(
50
+ connect=ld_eventsource.config.ConnectStrategy.http(self.url),
51
+ error_strategy=ld_eventsource.config.ErrorStrategy.CONTINUE,
52
+ )
53
+ self.read_thread = threading.Thread(
54
+ target=self.read_events,
55
+ args=(self.handlestate, self.handleerror, self.handlemessage),
56
+ )
57
+ self.read_thread.start()
58
+
59
+ def use_new_config(self, config: dict) -> bool:
60
+ new_url = config["hostname"] + config["path"]
61
+ if self.url == "" or self.url is None and new_url != "":
62
+ return True
63
+ return self.url != new_url
@@ -45,6 +45,7 @@ class DevCycleLocalOptions:
45
45
  event_retry_delay_ms: int = 200, # milliseconds
46
46
  disable_automatic_event_logging: bool = False,
47
47
  disable_custom_event_logging: bool = False,
48
+ enable_beta_realtime_updates: bool = False,
48
49
  ):
49
50
  self.events_api_uri = events_api_uri
50
51
  self.config_cdn_uri = config_cdn_uri
@@ -60,6 +61,7 @@ class DevCycleLocalOptions:
60
61
  self.on_client_initialized = on_client_initialized
61
62
  self.event_request_timeout_ms = event_request_timeout_ms
62
63
  self.event_retry_delay_ms = event_retry_delay_ms
64
+ self.enable_beta_realtime_updates = enable_beta_realtime_updates
63
65
 
64
66
  if self.flush_event_queue_size >= self.max_event_queue_size:
65
67
  logger.warning(
@@ -0,0 +1,120 @@
1
+ from google.protobuf.internal import containers as _containers
2
+ from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
3
+ from google.protobuf import descriptor as _descriptor
4
+ from google.protobuf import message as _message
5
+ from typing import ClassVar as _ClassVar, Mapping as _Mapping, Optional as _Optional, Union as _Union
6
+
7
+ Bool: CustomDataType
8
+ Boolean: VariableType_PB
9
+ DESCRIPTOR: _descriptor.FileDescriptor
10
+ JSON: VariableType_PB
11
+ Null: CustomDataType
12
+ Num: CustomDataType
13
+ Number: VariableType_PB
14
+ Str: CustomDataType
15
+ String: VariableType_PB
16
+
17
+ class CustomDataValue(_message.Message):
18
+ __slots__ = ["boolValue", "doubleValue", "stringValue", "type"]
19
+ BOOLVALUE_FIELD_NUMBER: _ClassVar[int]
20
+ DOUBLEVALUE_FIELD_NUMBER: _ClassVar[int]
21
+ STRINGVALUE_FIELD_NUMBER: _ClassVar[int]
22
+ TYPE_FIELD_NUMBER: _ClassVar[int]
23
+ boolValue: bool
24
+ doubleValue: float
25
+ stringValue: str
26
+ type: CustomDataType
27
+ def __init__(self, type: _Optional[_Union[CustomDataType, str]] = ..., boolValue: bool = ..., doubleValue: _Optional[float] = ..., stringValue: _Optional[str] = ...) -> None: ...
28
+
29
+ class DVCUser_PB(_message.Message):
30
+ __slots__ = ["appBuild", "appVersion", "country", "customData", "deviceModel", "email", "language", "name", "privateCustomData", "user_id"]
31
+ APPBUILD_FIELD_NUMBER: _ClassVar[int]
32
+ APPVERSION_FIELD_NUMBER: _ClassVar[int]
33
+ COUNTRY_FIELD_NUMBER: _ClassVar[int]
34
+ CUSTOMDATA_FIELD_NUMBER: _ClassVar[int]
35
+ DEVICEMODEL_FIELD_NUMBER: _ClassVar[int]
36
+ EMAIL_FIELD_NUMBER: _ClassVar[int]
37
+ LANGUAGE_FIELD_NUMBER: _ClassVar[int]
38
+ NAME_FIELD_NUMBER: _ClassVar[int]
39
+ PRIVATECUSTOMDATA_FIELD_NUMBER: _ClassVar[int]
40
+ USER_ID_FIELD_NUMBER: _ClassVar[int]
41
+ appBuild: NullableDouble
42
+ appVersion: NullableString
43
+ country: NullableString
44
+ customData: NullableCustomData
45
+ deviceModel: NullableString
46
+ email: NullableString
47
+ language: NullableString
48
+ name: NullableString
49
+ privateCustomData: NullableCustomData
50
+ user_id: str
51
+ def __init__(self, user_id: _Optional[str] = ..., email: _Optional[_Union[NullableString, _Mapping]] = ..., name: _Optional[_Union[NullableString, _Mapping]] = ..., language: _Optional[_Union[NullableString, _Mapping]] = ..., country: _Optional[_Union[NullableString, _Mapping]] = ..., appBuild: _Optional[_Union[NullableDouble, _Mapping]] = ..., appVersion: _Optional[_Union[NullableString, _Mapping]] = ..., deviceModel: _Optional[_Union[NullableString, _Mapping]] = ..., customData: _Optional[_Union[NullableCustomData, _Mapping]] = ..., privateCustomData: _Optional[_Union[NullableCustomData, _Mapping]] = ...) -> None: ...
52
+
53
+ class NullableCustomData(_message.Message):
54
+ __slots__ = ["isNull", "value"]
55
+ class ValueEntry(_message.Message):
56
+ __slots__ = ["key", "value"]
57
+ KEY_FIELD_NUMBER: _ClassVar[int]
58
+ VALUE_FIELD_NUMBER: _ClassVar[int]
59
+ key: str
60
+ value: CustomDataValue
61
+ def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[CustomDataValue, _Mapping]] = ...) -> None: ...
62
+ ISNULL_FIELD_NUMBER: _ClassVar[int]
63
+ VALUE_FIELD_NUMBER: _ClassVar[int]
64
+ isNull: bool
65
+ value: _containers.MessageMap[str, CustomDataValue]
66
+ def __init__(self, value: _Optional[_Mapping[str, CustomDataValue]] = ..., isNull: bool = ...) -> None: ...
67
+
68
+ class NullableDouble(_message.Message):
69
+ __slots__ = ["isNull", "value"]
70
+ ISNULL_FIELD_NUMBER: _ClassVar[int]
71
+ VALUE_FIELD_NUMBER: _ClassVar[int]
72
+ isNull: bool
73
+ value: float
74
+ def __init__(self, value: _Optional[float] = ..., isNull: bool = ...) -> None: ...
75
+
76
+ class NullableString(_message.Message):
77
+ __slots__ = ["isNull", "value"]
78
+ ISNULL_FIELD_NUMBER: _ClassVar[int]
79
+ VALUE_FIELD_NUMBER: _ClassVar[int]
80
+ isNull: bool
81
+ value: str
82
+ def __init__(self, value: _Optional[str] = ..., isNull: bool = ...) -> None: ...
83
+
84
+ class SDKVariable_PB(_message.Message):
85
+ __slots__ = ["_id", "boolValue", "doubleValue", "evalReason", "key", "stringValue", "type"]
86
+ BOOLVALUE_FIELD_NUMBER: _ClassVar[int]
87
+ DOUBLEVALUE_FIELD_NUMBER: _ClassVar[int]
88
+ EVALREASON_FIELD_NUMBER: _ClassVar[int]
89
+ KEY_FIELD_NUMBER: _ClassVar[int]
90
+ STRINGVALUE_FIELD_NUMBER: _ClassVar[int]
91
+ TYPE_FIELD_NUMBER: _ClassVar[int]
92
+ _ID_FIELD_NUMBER: _ClassVar[int]
93
+ _id: str
94
+ boolValue: bool
95
+ doubleValue: float
96
+ evalReason: NullableString
97
+ key: str
98
+ stringValue: str
99
+ type: VariableType_PB
100
+ def __init__(self, _id: _Optional[str] = ..., type: _Optional[_Union[VariableType_PB, str]] = ..., key: _Optional[str] = ..., boolValue: bool = ..., doubleValue: _Optional[float] = ..., stringValue: _Optional[str] = ..., evalReason: _Optional[_Union[NullableString, _Mapping]] = ...) -> None: ...
101
+
102
+ class VariableForUserParams_PB(_message.Message):
103
+ __slots__ = ["sdkKey", "shouldTrackEvent", "user", "variableKey", "variableType"]
104
+ SDKKEY_FIELD_NUMBER: _ClassVar[int]
105
+ SHOULDTRACKEVENT_FIELD_NUMBER: _ClassVar[int]
106
+ USER_FIELD_NUMBER: _ClassVar[int]
107
+ VARIABLEKEY_FIELD_NUMBER: _ClassVar[int]
108
+ VARIABLETYPE_FIELD_NUMBER: _ClassVar[int]
109
+ sdkKey: str
110
+ shouldTrackEvent: bool
111
+ user: DVCUser_PB
112
+ variableKey: str
113
+ variableType: VariableType_PB
114
+ def __init__(self, sdkKey: _Optional[str] = ..., variableKey: _Optional[str] = ..., variableType: _Optional[_Union[VariableType_PB, str]] = ..., user: _Optional[_Union[DVCUser_PB, _Mapping]] = ..., shouldTrackEvent: bool = ...) -> None: ...
115
+
116
+ class VariableType_PB(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
117
+ __slots__ = []
118
+
119
+ class CustomDataType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
120
+ __slots__ = []
@@ -0,0 +1,20 @@
1
+ Metadata-Version: 2.1
2
+ Name: devcycle-python-server-sdk
3
+ Version: 3.7.1
4
+ Summary: DevCycle Python SDK
5
+ Home-page: https://github.com/devcycleHQ/python-server-sdk
6
+ Author-email:
7
+ Keywords: DevCycle
8
+ License-File: LICENSE
9
+ Requires-Dist: setuptools>=21.0.0
10
+ Requires-Dist: urllib3>=1.15.1
11
+ Requires-Dist: requests~=2.31
12
+ Requires-Dist: wasmtime==23.0.0
13
+ Requires-Dist: protobuf>=4.23.3
14
+ Requires-Dist: openfeature-sdk>=0.7.0
15
+ Requires-Dist: launchdarkly-eventsource>=1.2.0
16
+ Requires-Dist: responses~=0.23.1
17
+ Requires-Dist: dataclasses~=0.6
18
+
19
+ The DevCycle Python SDK used for feature management.
20
+
@@ -1,21 +1,22 @@
1
- devcycle_python_sdk/VERSION.txt,sha256=E4ID4_YEoI-tDE0KKX7-LEhiGYINzdBT5yt9hDUEx6E,6
1
+ devcycle_python_sdk/VERSION.txt,sha256=Tpnn6sIcK3GpcTnKzKaV2YyT-9fdJlBfBPUnGAmsS5Q,6
2
2
  devcycle_python_sdk/__init__.py,sha256=30TcYb9PbvVi97gwk16omCGG1UoUI9lmmU7xpvxNMvc,336
3
- devcycle_python_sdk/bucketing-lib.release.wasm,sha256=yrNDrAxZS1h9VlTzsdOrNLP4ngnM04PmKxcWop1yrt4,202623
3
+ devcycle_python_sdk/bucketing-lib.release.wasm,sha256=AJyYTuQs-PRjAiH9yzSgn-iESa9W7Vu0AQHLJHC5QS0,214842
4
4
  devcycle_python_sdk/cloud_client.py,sha256=UMfXvXwO4v3pZW9ZA52ivxPH1Lp4Goy5gpV_s0qCpxQ,7352
5
5
  devcycle_python_sdk/devcycle_client.py,sha256=W5MeOslkXjTqZtoSy_Bbbug8gVoicjxshpKjDteEr6E,917
6
6
  devcycle_python_sdk/exceptions.py,sha256=Et7fS8pEFbHkFY86T2pJoEUstwjh4_rzhJxVH8fzAns,1053
7
- devcycle_python_sdk/local_client.py,sha256=5PFEUmBs-QsgaiXWBQfhhjcN2TbB64JH3iVwpJteG7o,9373
8
- devcycle_python_sdk/options.py,sha256=Ejr6HbdyqzZajFVkEqWLk5XLfdtqCIkvsyWaRt_lgwU,4575
7
+ devcycle_python_sdk/local_client.py,sha256=WeXt-gWhFsaPjAp2UEmygQhzx_ygx2Jaq0z0yRHaah0,9448
8
+ devcycle_python_sdk/options.py,sha256=TRofo42DvtWlVlGiUfOygIBzidei5oaCZRWpZgDgr0Q,4700
9
9
  devcycle_python_sdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  devcycle_python_sdk/api/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
11
11
  devcycle_python_sdk/api/backoff.py,sha256=F6WYCloaZD9CjNu6mKBMq7K20S8-i2WVU6X05ZRoawE,303
12
12
  devcycle_python_sdk/api/bucketing_client.py,sha256=-S0y9uAffippCTfjYgxI5j4j7YNfMxQJYUba1219nps,5111
13
- devcycle_python_sdk/api/config_client.py,sha256=NnqHmPcwK__LNqPzmKo5EBqULAQEteH1YmzRE_eIXJs,5077
13
+ devcycle_python_sdk/api/config_client.py,sha256=73aQAq130rTHpQrVDX6n_DwFKz0WuKxRoLDiE3vSoAM,5077
14
14
  devcycle_python_sdk/api/event_client.py,sha256=WxkuV6geEC9MgG4FN36HeEQQa22ilq2mkOVMZkpiEk4,3356
15
- devcycle_python_sdk/api/local_bucketing.py,sha256=-TfG3Jb0syVF3sqzzlFyKxHqLQ6a37C4mW0oj53J-yQ,16332
15
+ devcycle_python_sdk/api/local_bucketing.py,sha256=_MAMCHviIS1n4GiU-CnnLBeEVX8BUFRqwNbA1IQi84U,16470
16
16
  devcycle_python_sdk/managers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
- devcycle_python_sdk/managers/config_manager.py,sha256=YfgGKW4mtMhqdwWZiZ_Y67AYLC9VTpTjYhpDgfGFA4g,3599
18
- devcycle_python_sdk/managers/event_queue_manager.py,sha256=06TcdlRoB89C7vGH1UTnnoDFsSnT71tPwf0gn1NK-W0,8071
17
+ devcycle_python_sdk/managers/config_manager.py,sha256=U8ok13HjEi5to-GxH6hDuHEq09O5e7h51Aeui05XbdQ,5627
18
+ devcycle_python_sdk/managers/event_queue_manager.py,sha256=uPy1vO6OAisZjUNAhWELE_g17alTiOcPv42z-WwkGck,8110
19
+ devcycle_python_sdk/managers/sse_manager.py,sha256=aJKHPmvv8SR4DU9IOi-KrUSCxQoHdMwoemb2lSVfmVQ,2369
19
20
  devcycle_python_sdk/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
21
  devcycle_python_sdk/models/bucketed_config.py,sha256=NsdT-6RbEuDy5QuK5qlr6UqNKQQnwjoXVYN-uQp0P-E,3999
21
22
  devcycle_python_sdk/models/error_response.py,sha256=r_dRUDAbral9UGaMYcnBsZOr9Hmkyi1SQbhGmZilgpA,442
@@ -29,38 +30,39 @@ devcycle_python_sdk/open_feature_provider/provider.py,sha256=VjUMhCYiMLixXUSSIjQ
29
30
  devcycle_python_sdk/protobuf/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
31
  devcycle_python_sdk/protobuf/utils.py,sha256=zi-kVx6yvgS2c04cUobLd2IDc_ZqKkWduay9YcpjoGc,4684
31
32
  devcycle_python_sdk/protobuf/variableForUserParams_pb2.py,sha256=Zf4-8n67UnulC0Sz90aqLYsXCFEgywOqfiSHuXZomLk,4244
33
+ devcycle_python_sdk/protobuf/variableForUserParams_pb2.pyi,sha256=gjTD1KkBe5NaXixSq78eNTNbjdDjBQ0h5L4dUKKgDWQ,5659
32
34
  devcycle_python_sdk/util/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
33
35
  devcycle_python_sdk/util/strings.py,sha256=gH0Xm3gIXNqn6hczKPk10Jn5_9knuD7OB5MrB7bqztw,252
34
36
  devcycle_python_sdk/util/version.py,sha256=Cz7Us_EuXP-T8GI47g2WHYdBJj_zKo-5y545IDHJAjI,253
35
37
  example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
38
  example/cloud_client_example.py,sha256=YD8ZsTv3dPYfbFUKraRpRT0VsJGrPwjm8ZCuA5H54yI,2463
37
- example/local_bucketing_client_example.py,sha256=TaIfy3VnW9fXii9NPYs5UOuP4fLKruXMjnZApveqzl4,2796
39
+ example/local_bucketing_client_example.py,sha256=4BKNp7KE_Co9xxRTbJOfG2jQFjtFagPYsvytu93Q5_A,2829
38
40
  example/openfeature_example.py,sha256=kO6F-Xk20l6CPiMfIxSRreshEGmHMFHRg3zbeLXa3AI,2410
39
41
  test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
42
  test/test_cloud_client.py,sha256=zjk302m7qKMCQryolkc1WKLFmbGWUxA0VtHlZk0rY-0,11442
41
- test/test_local_client.py,sha256=Ds6VIfLcaefboyvGrhQTGsdJj_DGB4IVnR2_16xDpSY,13022
43
+ test/test_local_client.py,sha256=kDB5-cPJGyQtVdHV4MUM-CBBeOpN-oScr6I0OuyGPmE,13022
42
44
  test/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
45
  test/api/test_bucketing_client.py,sha256=M2mjACowLvbsIjXdP-w8-fwY02xqv1X1r_h9v-pXa7A,7739
44
- test/api/test_config_client.py,sha256=jT48_llov5QQs0jNDbHANRqySVT1HamXm82uPZXKSrE,5129
46
+ test/api/test_config_client.py,sha256=3BwMm4y-4NQcdW2DDGppoJLIF-ffgjlUJti2Qjw1D1M,5129
45
47
  test/api/test_event_client.py,sha256=v4Xoum-PdywfczrfMV1llM5vrIJoqHybzxPLVokh09g,3310
46
- test/api/test_local_bucketing.py,sha256=lPL3ThZcGG-8-67jFxRvGrwL8NB6-sM5qGJb0p2O__Q,15254
48
+ test/api/test_local_bucketing.py,sha256=0mReZ4bsFUoJWPAIcQrWXDZ9UoC5mF9MuI-9BVB6qjM,15561
47
49
  test/fixture/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
48
50
  test/fixture/data.py,sha256=6ReLxWPMhCDbJ5Ex24_iR0PJNjKzPFV1I9j_v0FVbBU,1462
49
51
  test/managers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
50
52
  test/managers/test_config_manager.py,sha256=X0DxzWdjkvkUmarJSgzTDvO2Ma0TU6RNwL0WUwX2uA8,5963
51
- test/managers/test_event_queue_manager.py,sha256=KquHBuYiDdoVStm5Ga9mIB-wvmJnChiFRBNC8sMKaxQ,11053
53
+ test/managers/test_event_queue_manager.py,sha256=z850atk8zAlD7sYaA1OJwgCFcfC0ps4NEk4XUnNCq1U,11629
52
54
  test/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
55
  test/models/test_bucketed_config.py,sha256=8wCEyxaHxY21nl7ZbiFzp-IzZQBZIPIVPPzeREjumQk,858
54
56
  test/models/test_user.py,sha256=ZNTBSKLDrqrsqZuaw5AHtc_FW4empSTaeNPZHZaFlI4,6736
55
57
  test/openfeature_test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
56
58
  test/openfeature_test/test_provider.py,sha256=UPxAD5e7NvpIhmqNFCrTY_yrKrMyYxBc2xuMAgdSGvw,7653
57
- test/openfeature_test/test_provider_local_sdk.py,sha256=5NjfUDalW4F7Oe8n1740VO7l5OIBEOldnSryM98uO2w,4426
59
+ test/openfeature_test/test_provider_local_sdk.py,sha256=ad0QizKSS-lbqlIjiQvwtKHP7PCFfYam3OKHtvsFBkU,4426
58
60
  test/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
59
61
  test/util/test_strings.py,sha256=T5H5U0af7_ld1yJC8IdDmay_Mx-iJQ5Fi5fbvKcAc7A,862
60
62
  test/util/test_utils.py,sha256=tVBJm_qnqg-4PssLpXIxqlk0JehuiAIPs4zpi2oNVnY,6671
61
63
  test/util/test_version.py,sha256=3Xg0_84m0bdHXz6pxd2Z85Oz9DoUC1T0Ol_WqEH_jac,335
62
- devcycle_python_server_sdk-3.6.0.dist-info/LICENSE,sha256=tHQMqu-Jic2UpMGqb-vNFbGygysl8IG4l1clnnh0OFo,1071
63
- devcycle_python_server_sdk-3.6.0.dist-info/METADATA,sha256=mEHWiA0xrfnZ4-GLr2uyc0_HOPyEc2ENqvD6CLe8SKo,508
64
- devcycle_python_server_sdk-3.6.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
65
- devcycle_python_server_sdk-3.6.0.dist-info/top_level.txt,sha256=34f8zDzihAHoH7siAFlmscyO_QLNWWq9HApFlwtYL4k,33
66
- devcycle_python_server_sdk-3.6.0.dist-info/RECORD,,
64
+ devcycle_python_server_sdk-3.7.1.dist-info/LICENSE,sha256=tHQMqu-Jic2UpMGqb-vNFbGygysl8IG4l1clnnh0OFo,1071
65
+ devcycle_python_server_sdk-3.7.1.dist-info/METADATA,sha256=7iRcy6f-15UNPk8GalpC-RJM-Ls_qJOKtBxjZ0tSD9o,586
66
+ devcycle_python_server_sdk-3.7.1.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
67
+ devcycle_python_server_sdk-3.7.1.dist-info/top_level.txt,sha256=34f8zDzihAHoH7siAFlmscyO_QLNWWq9HApFlwtYL4k,33
68
+ devcycle_python_server_sdk-3.7.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: setuptools (75.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -21,7 +21,7 @@ def main():
21
21
 
22
22
  # create an instance of the DevCycle Client object
23
23
  server_sdk_key = os.environ["DEVCYCLE_SERVER_SDK_KEY"]
24
- options = DevCycleLocalOptions()
24
+ options = DevCycleLocalOptions(enable_beta_realtime_updates=True)
25
25
  client = DevCycleLocalClient(server_sdk_key, options)
26
26
 
27
27
  # Wait for DevCycle to initialize and load the configuration
@@ -30,7 +30,7 @@ class ConfigAPIClientTest(unittest.TestCase):
30
30
  slash_join(
31
31
  "https://config-cdn.devcycle.com/",
32
32
  "config",
33
- "v1",
33
+ "v2",
34
34
  "server",
35
35
  self.sdk_key,
36
36
  )
@@ -1,6 +1,7 @@
1
1
  import json
2
2
  import logging
3
3
  import unittest
4
+ import uuid
4
5
 
5
6
  from devcycle_python_sdk.api.local_bucketing import LocalBucketing, WASMAbortError
6
7
  from devcycle_python_sdk.models.bucketed_config import (
@@ -24,6 +25,7 @@ class LocalBucketingTest(unittest.TestCase):
24
25
  def setUp(self) -> None:
25
26
  self.test_sdk_key = "dvc_server_testkey"
26
27
  self.local_bucketing = LocalBucketing(self.test_sdk_key)
28
+ self.client_uuid = str(uuid.uuid4())
27
29
 
28
30
  def test_init(self):
29
31
  local_bucketing = LocalBucketing(self.test_sdk_key)
@@ -93,7 +95,7 @@ class LocalBucketingTest(unittest.TestCase):
93
95
  self.local_bucketing.store_config(small_config())
94
96
  platform_json = json.dumps(default_platform_data().to_json())
95
97
  self.local_bucketing.set_platform_data(platform_json)
96
- self.local_bucketing.init_event_queue("{}")
98
+ self.local_bucketing.init_event_queue(self.client_uuid, "{}")
97
99
  user = DevCycleUser(user_id="test_user_id")
98
100
  result = self.local_bucketing.get_variable_for_user_protobuf(
99
101
  user=user, key="string-var", default_value="default value"
@@ -108,7 +110,7 @@ class LocalBucketingTest(unittest.TestCase):
108
110
  self.local_bucketing.store_config(special_character_config())
109
111
  platform_json = json.dumps(default_platform_data().to_json())
110
112
  self.local_bucketing.set_platform_data(platform_json)
111
- self.local_bucketing.init_event_queue("{}")
113
+ self.local_bucketing.init_event_queue(self.client_uuid, "{}")
112
114
  user = DevCycleUser(user_id="test_user_id")
113
115
  result = self.local_bucketing.get_variable_for_user_protobuf(
114
116
  user=user, key="string-var", default_value="default value"
@@ -123,7 +125,7 @@ class LocalBucketingTest(unittest.TestCase):
123
125
  self.local_bucketing.store_config(small_config())
124
126
  platform_json = json.dumps(default_platform_data().to_json())
125
127
  self.local_bucketing.set_platform_data(platform_json)
126
- self.local_bucketing.init_event_queue("{}")
128
+ self.local_bucketing.init_event_queue(self.client_uuid, "{}")
127
129
  user = DevCycleUser(user_id="test_user_id")
128
130
 
129
131
  # type mismatch is handled inside the WASM and will return
@@ -137,7 +139,7 @@ class LocalBucketingTest(unittest.TestCase):
137
139
  self.local_bucketing.store_config(small_config())
138
140
  platform_json = json.dumps(default_platform_data().to_json())
139
141
  self.local_bucketing.set_platform_data(platform_json)
140
- self.local_bucketing.init_event_queue("{}")
142
+ self.local_bucketing.init_event_queue(self.client_uuid, "{}")
141
143
  user = DevCycleUser(user_id="test_user_id")
142
144
 
143
145
  result = self.local_bucketing.generate_bucketed_config(user=user)
@@ -265,7 +267,7 @@ class LocalBucketingTest(unittest.TestCase):
265
267
  self.local_bucketing.store_config(small_config())
266
268
  platform_json = json.dumps(default_platform_data().to_json())
267
269
  self.local_bucketing.set_platform_data(platform_json)
268
- self.local_bucketing.init_event_queue("{}")
270
+ self.local_bucketing.init_event_queue(self.client_uuid, "{}")
269
271
 
270
272
  result = self.local_bucketing.get_event_queue_size()
271
273
  self.assertEqual(result, 0)
@@ -276,13 +278,14 @@ class LocalBucketingTest(unittest.TestCase):
276
278
  self.local_bucketing.set_platform_data(platform_json)
277
279
 
278
280
  self.local_bucketing.init_event_queue(
281
+ self.client_uuid,
279
282
  json.dumps(
280
283
  {
281
284
  "disableAutomaticEventLogging": False,
282
285
  "disableCustomEventLogging": False,
283
286
  "minEventsPerFlush": 1,
284
287
  }
285
- )
288
+ ),
286
289
  )
287
290
  results = self.local_bucketing.flush_event_queue()
288
291
  self.assertIsNotNone(results)
@@ -294,13 +297,14 @@ class LocalBucketingTest(unittest.TestCase):
294
297
  self.local_bucketing.set_platform_data(platform_json)
295
298
 
296
299
  self.local_bucketing.init_event_queue(
300
+ self.client_uuid,
297
301
  json.dumps(
298
302
  {
299
303
  "disableAutomaticEventLogging": False,
300
304
  "disableCustomEventLogging": False,
301
305
  "minEventsPerFlush": 1,
302
306
  }
303
- )
307
+ ),
304
308
  )
305
309
 
306
310
  # trigger two events for a single user
@@ -326,13 +330,14 @@ class LocalBucketingTest(unittest.TestCase):
326
330
  self.local_bucketing.set_platform_data(platform_json)
327
331
 
328
332
  self.local_bucketing.init_event_queue(
333
+ self.client_uuid,
329
334
  json.dumps(
330
335
  {
331
336
  "disableAutomaticEventLogging": False,
332
337
  "disableCustomEventLogging": False,
333
338
  "minEventsPerFlush": 1,
334
339
  }
335
- )
340
+ ),
336
341
  )
337
342
 
338
343
  with self.assertRaises(WASMAbortError):
@@ -344,13 +349,14 @@ class LocalBucketingTest(unittest.TestCase):
344
349
  self.local_bucketing.set_platform_data(platform_json)
345
350
 
346
351
  self.local_bucketing.init_event_queue(
352
+ self.client_uuid,
347
353
  json.dumps(
348
354
  {
349
355
  "disableAutomaticEventLogging": False,
350
356
  "disableCustomEventLogging": False,
351
357
  "minEventsPerFlush": 1,
352
358
  }
353
- )
359
+ ),
354
360
  )
355
361
 
356
362
  with self.assertRaises(WASMAbortError):
@@ -360,7 +366,7 @@ class LocalBucketingTest(unittest.TestCase):
360
366
  self.local_bucketing.store_config(small_config())
361
367
  platform_json = json.dumps(default_platform_data().to_json())
362
368
  self.local_bucketing.set_platform_data(platform_json)
363
- self.local_bucketing.init_event_queue("{}")
369
+ self.local_bucketing.init_event_queue(self.client_uuid, "{}")
364
370
  user = DevCycleUser(user_id="test_user_id")
365
371
  event = DevCycleEvent(
366
372
  type=EventType.CustomEvent,
@@ -376,7 +382,7 @@ class LocalBucketingTest(unittest.TestCase):
376
382
  self.local_bucketing.store_config(small_config())
377
383
  platform_json = json.dumps(default_platform_data().to_json())
378
384
  self.local_bucketing.set_platform_data(platform_json)
379
- self.local_bucketing.init_event_queue("{}")
385
+ self.local_bucketing.init_event_queue(self.client_uuid, "{}")
380
386
 
381
387
  event = DevCycleEvent(
382
388
  type=EventType.AggVariableDefaulted,
@@ -26,6 +26,7 @@ logger = logging.getLogger(__name__)
26
26
  class EventQueueManagerTest(unittest.TestCase):
27
27
  def setUp(self) -> None:
28
28
  self.sdk_key = "dvc_server_" + str(uuid.uuid4())
29
+ self.client_uuid = str(uuid.uuid4())
29
30
 
30
31
  # mock out the local bucketing to test event queue logic directly
31
32
  self.test_local_bucketing = MagicMock()
@@ -84,7 +85,7 @@ class EventQueueManagerTest(unittest.TestCase):
84
85
  self.test_local_bucketing.flush_event_queue.return_value = []
85
86
  self.test_options.event_flush_interval_ms = 100
86
87
  manager = EventQueueManager(
87
- self.sdk_key, self.test_options, self.test_local_bucketing
88
+ self.sdk_key, self.client_uuid, self.test_options, self.test_local_bucketing
88
89
  )
89
90
  self.assertTrue(manager._should_run())
90
91
  self.assertTrue(manager.is_alive())
@@ -94,7 +95,7 @@ class EventQueueManagerTest(unittest.TestCase):
94
95
  self.test_local_bucketing.flush_event_queue.return_value = []
95
96
  self.test_options.event_flush_interval_ms = 100
96
97
  manager = EventQueueManager(
97
- self.sdk_key, self.test_options, self.test_local_bucketing
98
+ self.sdk_key, self.client_uuid, self.test_options, self.test_local_bucketing
98
99
  )
99
100
 
100
101
  manager.close()
@@ -111,7 +112,10 @@ class EventQueueManagerTest(unittest.TestCase):
111
112
  mock_publish_events.side_effect = APIClientError("Some retryable error")
112
113
 
113
114
  manager = EventQueueManager(
114
- self.sdk_key, self.test_options_no_thread, self.test_local_bucketing
115
+ self.sdk_key,
116
+ self.client_uuid,
117
+ self.test_options_no_thread,
118
+ self.test_local_bucketing,
115
119
  )
116
120
  manager._publish_event_payload(self.test_payload)
117
121
 
@@ -129,7 +133,10 @@ class EventQueueManagerTest(unittest.TestCase):
129
133
  )
130
134
 
131
135
  manager = EventQueueManager(
132
- self.sdk_key, self.test_options_no_thread, self.test_local_bucketing
136
+ self.sdk_key,
137
+ self.client_uuid,
138
+ self.test_options_no_thread,
139
+ self.test_local_bucketing,
133
140
  )
134
141
  manager._publish_event_payload(self.test_payload)
135
142
 
@@ -146,7 +153,10 @@ class EventQueueManagerTest(unittest.TestCase):
146
153
  self.test_local_bucketing.flush_event_queue.return_value = []
147
154
 
148
155
  manager = EventQueueManager(
149
- self.sdk_key, self.test_options_no_thread, self.test_local_bucketing
156
+ self.sdk_key,
157
+ self.client_uuid,
158
+ self.test_options_no_thread,
159
+ self.test_local_bucketing,
150
160
  )
151
161
  manager._flush_events()
152
162
 
@@ -158,7 +168,10 @@ class EventQueueManagerTest(unittest.TestCase):
158
168
  self.test_local_bucketing.flush_event_queue.return_value = [self.test_payload]
159
169
 
160
170
  manager = EventQueueManager(
161
- self.sdk_key, self.test_options_no_thread, self.test_local_bucketing
171
+ self.sdk_key,
172
+ self.client_uuid,
173
+ self.test_options_no_thread,
174
+ self.test_local_bucketing,
162
175
  )
163
176
  result = manager._flush_events()
164
177
 
@@ -169,7 +182,10 @@ class EventQueueManagerTest(unittest.TestCase):
169
182
 
170
183
  def test_queue_event_bad_data(self):
171
184
  manager = EventQueueManager(
172
- self.sdk_key, self.test_options_no_thread, self.test_local_bucketing
185
+ self.sdk_key,
186
+ self.client_uuid,
187
+ self.test_options_no_thread,
188
+ self.test_local_bucketing,
173
189
  )
174
190
  with self.assertRaises(ValueError):
175
191
  manager.queue_event(user=None, event=None)
@@ -188,7 +204,10 @@ class EventQueueManagerTest(unittest.TestCase):
188
204
  self.test_local_bucketing.get_event_queue_size.return_value = 1
189
205
 
190
206
  manager = EventQueueManager(
191
- self.sdk_key, self.test_options_no_thread, self.test_local_bucketing
207
+ self.sdk_key,
208
+ self.client_uuid,
209
+ self.test_options_no_thread,
210
+ self.test_local_bucketing,
192
211
  )
193
212
  DevCycleUser(user_id="test_user_id")
194
213
  event = DevCycleEvent(
@@ -207,7 +226,10 @@ class EventQueueManagerTest(unittest.TestCase):
207
226
  self.test_options_no_thread.max_event_queue_size = 100
208
227
 
209
228
  manager = EventQueueManager(
210
- self.sdk_key, self.test_options_no_thread, self.test_local_bucketing
229
+ self.sdk_key,
230
+ self.client_uuid,
231
+ self.test_options_no_thread,
232
+ self.test_local_bucketing,
211
233
  )
212
234
 
213
235
  # queue empty, no issues
@@ -231,7 +253,10 @@ class EventQueueManagerTest(unittest.TestCase):
231
253
 
232
254
  def test_queue_aggregate_event_bad_data(self):
233
255
  manager = EventQueueManager(
234
- self.sdk_key, self.test_options_no_thread, self.test_local_bucketing
256
+ self.sdk_key,
257
+ self.client_uuid,
258
+ self.test_options_no_thread,
259
+ self.test_local_bucketing,
235
260
  )
236
261
  with self.assertRaises(ValueError):
237
262
  manager.queue_aggregate_event(event=None, bucketed_config=None)
@@ -252,7 +277,10 @@ class EventQueueManagerTest(unittest.TestCase):
252
277
  self.test_local_bucketing.get_event_queue_size.return_value = 1
253
278
 
254
279
  manager = EventQueueManager(
255
- self.sdk_key, self.test_options_no_thread, self.test_local_bucketing
280
+ self.sdk_key,
281
+ self.client_uuid,
282
+ self.test_options_no_thread,
283
+ self.test_local_bucketing,
256
284
  )
257
285
  event = DevCycleEvent(
258
286
  type=EventType.AggVariableDefaulted,
@@ -23,7 +23,7 @@ class DevCycleProviderWithLocalSDKTest(unittest.TestCase):
23
23
  self.test_config_json = small_config_json()
24
24
  self.test_etag = "2f71454e-3279-4ca7-a8e7-802ce97bef43"
25
25
 
26
- config_url = "http://localhost/config/v1/server/" + self.sdk_key + ".json"
26
+ config_url = "http://localhost/config/v2/server/" + self.sdk_key + ".json"
27
27
 
28
28
  responses.add(
29
29
  responses.GET,
test/test_local_client.py CHANGED
@@ -25,7 +25,7 @@ class DevCycleLocalClientTest(unittest.TestCase):
25
25
  self.test_config_json = small_config_json()
26
26
  self.test_etag = "2f71454e-3279-4ca7-a8e7-802ce97bef43"
27
27
 
28
- config_url = "http://localhost/config/v1/server/" + self.sdk_key + ".json"
28
+ config_url = "http://localhost/config/v2/server/" + self.sdk_key + ".json"
29
29
 
30
30
  responses.add(
31
31
  responses.GET,
@@ -371,7 +371,7 @@ def test_benchmark_variable_call(benchmark):
371
371
  test_config_json = small_config_json()
372
372
  test_etag = "2f71454e-3279-4ca7-a8e7-802ce97bef43"
373
373
 
374
- config_url = "http://localhost/config/v1/server/" + sdk_key + ".json"
374
+ config_url = "http://localhost/config/v2/server/" + sdk_key + ".json"
375
375
 
376
376
  responses.add(
377
377
  responses.GET,
@@ -1,20 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: devcycle-python-server-sdk
3
- Version: 3.6.0
4
- Summary: DevCycle Python SDK
5
- Home-page: https://github.com/devcycleHQ/python-server-sdk
6
- Author: UNKNOWN
7
- Author-email: UNKNOWN
8
- License: UNKNOWN
9
- Keywords: DevCycle
10
- Platform: UNKNOWN
11
- Requires-Dist: setuptools >=21.0.0
12
- Requires-Dist: urllib3 >=1.15.1
13
- Requires-Dist: requests ~=2.31
14
- Requires-Dist: wasmtime ==9.0.0
15
- Requires-Dist: protobuf >=4.23.3
16
- Requires-Dist: openfeature-sdk >=0.7.0
17
-
18
- The DevCycle Python SDK used for feature management.
19
-
20
-