Skip to content

Commit 37845e6

Browse files
committed
Release Common v3.1.0
1 parent 51de2f5 commit 37845e6

File tree

6 files changed

+235
-48
lines changed

6 files changed

+235
-48
lines changed

common/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changelog
22

3+
## 3.1.0 - 2025-09-12
4+
5+
### Changed (3)
6+
7+
- Updated WebsocketStream user data stream response handler
8+
- Updated Encoded request matrix handler
9+
- Fix decimal type in request parameters
10+
311
## 3.0.0 - 2025-09-05
412

513
### Added (2)

common/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "binance-common"
3-
version = "3.0.0"
3+
version = "3.1.0"
44
description = "Binance Common Types and Utilities for Binance Connectors"
55
authors = ["Binance"]
66
license = "MIT"

common/src/binance_common/utils.py

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ def hmac_hashing(api_secret: str, payload: str) -> str:
7878
return m.hexdigest()
7979

8080

81-
def cleanNoneValue(d: dict) -> dict:
81+
def clean_none_value(d: dict) -> dict:
8282
"""Removes any `None` values from the input dictionary `d` and returns a new
8383
dictionary with only non-`None` values.
8484
@@ -89,11 +89,24 @@ def cleanNoneValue(d: dict) -> dict:
8989
dict: A new dictionary with only non-`None` values from the input dictionary.
9090
"""
9191

92-
out = {}
93-
for k in d.keys():
94-
if d[k] is not None:
95-
out[k] = d[k]
96-
return out
92+
if isinstance(d, dict):
93+
out = {}
94+
for k, v in d.items():
95+
cleaned = clean_none_value(v)
96+
if cleaned not in (None, {}, []):
97+
out[k] = cleaned
98+
return out
99+
elif isinstance(d, list):
100+
out_list = []
101+
for item in d:
102+
cleaned_item = clean_none_value(item)
103+
if cleaned_item not in (None, {}, []):
104+
out_list.append(cleaned_item)
105+
return out_list
106+
elif hasattr(d, "__dict__"):
107+
return clean_none_value(d.__dict__)
108+
else:
109+
return d
97110

98111

99112
def get_timestamp() -> int:
@@ -128,11 +141,35 @@ def make_serializable(val) -> Union[dict, list, str, int, float, bool]:
128141
return [v.__dict__ if hasattr(v, "__dict__") else v for v in val]
129142
if isinstance(val, bool):
130143
return str(val).lower()
131-
if isinstance(val , Enum):
144+
if isinstance(val, float):
145+
return str(val)
146+
if isinstance(val, Enum):
132147
return val.value
148+
if hasattr(val, "__dict__"):
149+
return transform_query(val.__dict__)
133150
return val
134151

135152

153+
def transform_query(data: Union[dict, list, str, int, float, bool]) -> Union[dict, list, str, int, float, bool]:
154+
"""Recursively transform keys to camelCase and values to serializable.
155+
156+
Args:
157+
data (Union[dict, list, str, int, float, bool]): The data to transform.
158+
Returns:
159+
Union[dict, list, str, int, float, bool]: The transformed data.
160+
"""
161+
162+
if isinstance(data, dict):
163+
return {
164+
snake_to_camel(k): transform_query(v)
165+
for k, v in data.items()
166+
}
167+
elif isinstance(data, list):
168+
return [transform_query(v) for v in data]
169+
else:
170+
return make_serializable(data)
171+
172+
136173
def encoded_string(query: str) -> str:
137174
"""Encodes the given query string.
138175
@@ -143,10 +180,9 @@ def encoded_string(query: str) -> str:
143180
str: The encoded query string.
144181
"""
145182

183+
query = transform_query(query)
146184
query = {
147-
snake_to_camel(k): json.dumps(make_serializable(v), separators=(",", ":"))
148-
if isinstance(v, (list, dict))
149-
else make_serializable(v)
185+
k: json.dumps(v, separators=(",", ":")) if isinstance(v, (dict, list)) else v
150186
for k, v in query.items()
151187
}
152188
return urlencode(query, True)
@@ -263,7 +299,7 @@ def send_request(
263299
payload = {}
264300

265301
if is_signed:
266-
cleaned_payload = cleanNoneValue(payload)
302+
cleaned_payload = clean_none_value(payload)
267303
cleaned_payload["timestamp"] = get_timestamp()
268304
query_string = encoded_string(cleaned_payload)
269305
cleaned_payload["signature"] = get_signature(configuration, query_string, signer)
@@ -305,7 +341,7 @@ def send_request(
305341
response = session.request(
306342
method=method,
307343
url=url,
308-
params=encoded_string(cleanNoneValue(payload)),
344+
params=encoded_string(clean_none_value(payload)),
309345
headers=headers,
310346
timeout=timeout,
311347
proxies=proxies,

common/src/binance_common/websocket.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,14 +212,16 @@ async def receive_loop(self, connection: WebSocketConnection):
212212

213213
for callback in callbacks:
214214
if response_model:
215-
if isinstance(payload, list):
215+
if response_model.__pydantic_fields__.get("one_of_schemas"):
216+
parsed = payload
217+
elif isinstance(payload, list):
216218
parsed = [
217219
response_model.model_validate_json(json.dumps(item))
218220
for item in payload
219221
]
220-
callback(parsed)
221222
else:
222-
callback(response_model.model_validate_json(json.dumps(payload)))
223+
parsed = response_model.model_validate_json(json.dumps(payload))
224+
callback(parsed)
223225
else:
224226
callback(payload)
225227
else:

0 commit comments

Comments
 (0)