Skip to content

Commit

Permalink
3.3.0 release (#248)
Browse files Browse the repository at this point in the history
  • Loading branch information
urbanogt committed Aug 7, 2023
1 parent cf631e7 commit bcb7bb0
Show file tree
Hide file tree
Showing 12 changed files with 159 additions and 7 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## 3.3.0 - 2023-08-07

### Changed
- Add support for proxy in Websocket clients
- Remove support for python 3.7


## 3.2.0 - 2023-08-01

### Changed
Expand Down
61 changes: 61 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,67 @@ logging.info("closing ws connection")
my_client.stop()
```

#### Proxy

Proxy is supported for both WebSocket API and WebSocket Stream.

To use it, pass in the `proxies` parameter when initializing the client.

The format of the `proxies` parameter is the same as the one used in the Spot RESTful API.

It consists on a dictionary with the following format, where the key is the type of the proxy and the value is the proxy URL:

For websockets, the proxy type is `http`.

```python
proxies = { 'http': 'http://1.2.3.4:8080' }
```

You can also use authentication for the proxy by adding the `username` and `password` parameters to the proxy URL:

```python
proxies = { 'http': 'http://username:password@host:port' }
```


```python

# WebSocket API Client
from binance.websocket.spot.websocket_api import SpotWebsocketAPIClient

def message_handler(_, message):
logging.info(message)

proxies = { 'http': 'http://1.2.3.4:8080' }

my_client = SpotWebsocketAPIClient(on_message=message_handler, proxies=proxies)

my_client.ticker(symbol="BNBBUSD", type="FULL")

time.sleep(5)
logging.info("closing ws connection")
my_client.stop()
```

```python

# WebSocket Stream Client
from binance.websocket.spot.websocket_stream import SpotWebsocketStreamClient

def message_handler(_, message):
logging.info(message)

proxies = { 'http': 'http://1.2.3.4:8080' }

my_client = SpotWebsocketStreamClient(on_message=message_handler, proxies=proxies)

# Subscribe to a single symbol stream
my_client.agg_trade(symbol="bnbusdt")
time.sleep(5)
logging.info("closing ws connection")
my_client.stop()
```

#### Request Id

Client can assign a request id to each request. The request id will be returned in the response message. Not mandatory in the library, it generates a uuid format string if not provided.
Expand Down
2 changes: 1 addition & 1 deletion binance/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "3.2.0"
__version__ = "3.3.0"
18 changes: 18 additions & 0 deletions binance/lib/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import json
import time
import uuid

from urllib.parse import urlparse
from collections import OrderedDict
from urllib.parse import urlencode
from binance.lib.authentication import hmac_hashing
Expand Down Expand Up @@ -112,3 +114,19 @@ def websocket_api_signature(api_key: str, api_secret: str, parameters: dict):
parameters["signature"] = hmac_hashing(api_secret, urlencode(parameters))

return parameters


def parse_proxies(proxies: dict):
"""Parse proxy url from dict, only support http and https proxy, not support socks5 proxy"""
proxy_url = proxies.get("http") or proxies.get("https")
if not proxy_url:
return {}

parsed = urlparse(proxy_url)
return {
"http_proxy_host": parsed.hostname,
"http_proxy_port": parsed.port,
"http_proxy_auth": (parsed.username, parsed.password)
if parsed.username and parsed.password
else None,
}
14 changes: 11 additions & 3 deletions binance/websocket/binance_socket_manager.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Optional

import logging
import threading
from websocket import (
Expand All @@ -6,6 +8,7 @@
WebSocketException,
WebSocketConnectionClosedException,
)
from binance.lib.utils import parse_proxies


class BinanceSocketManager(threading.Thread):
Expand All @@ -19,6 +22,7 @@ def __init__(
on_ping=None,
on_pong=None,
logger=None,
proxies: Optional[dict] = None,
):
threading.Thread.__init__(self)
if not logger:
Expand All @@ -31,15 +35,19 @@ def __init__(
self.on_ping = on_ping
self.on_pong = on_pong
self.on_error = on_error
self.proxies = proxies

self._proxy_params = parse_proxies(proxies) if proxies else {}

self.create_ws_connection()

def create_ws_connection(self):
self.logger.debug(
"Creating connection with WebSocket Server: %s", self.stream_url
f"Creating connection with WebSocket Server: {self.stream_url}, proxies: {self.proxies}",
)
self.ws = create_connection(self.stream_url)
self.ws = create_connection(self.stream_url, **self._proxy_params)
self.logger.debug(
"WebSocket connection has been established: %s", self.stream_url
f"WebSocket connection has been established: {self.stream_url}, proxies: {self.proxies}",
)
self._callback(self.on_open)

Expand Down
4 changes: 4 additions & 0 deletions binance/websocket/spot/websocket_api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Optional

from binance.websocket.websocket_client import BinanceWebsocketClient


Expand All @@ -13,6 +15,7 @@ def __init__(
on_error=None,
on_ping=None,
on_pong=None,
proxies: Optional[dict] = None,
):
self.api_key = api_key
self.api_secret = api_secret
Expand All @@ -25,6 +28,7 @@ def __init__(
on_error=on_error,
on_ping=on_ping,
on_pong=on_pong,
proxies=proxies,
)

# Market
Expand Down
4 changes: 4 additions & 0 deletions binance/websocket/spot/websocket_stream.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Optional

from binance.websocket.websocket_client import BinanceWebsocketClient


Expand All @@ -12,6 +14,7 @@ def __init__(
on_ping=None,
on_pong=None,
is_combined=False,
proxies: Optional[dict] = None,
):
if is_combined:
stream_url = stream_url + "/stream"
Expand All @@ -25,6 +28,7 @@ def __init__(
on_error=on_error,
on_ping=on_ping,
on_pong=on_pong,
proxies=proxies,
)

def agg_trade(self, symbol: str, id=None, action=None, **kwargs):
Expand Down
6 changes: 6 additions & 0 deletions binance/websocket/websocket_client.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Optional

import json
import logging
from binance.lib.utils import get_timestamp
Expand All @@ -18,6 +20,7 @@ def __init__(
on_ping=None,
on_pong=None,
logger=None,
proxies: Optional[dict] = None,
):
if not logger:
logger = logging.getLogger(__name__)
Expand All @@ -31,6 +34,7 @@ def __init__(
on_ping,
on_pong,
logger,
proxies,
)

# start the thread
Expand All @@ -47,6 +51,7 @@ def _initialize_socket(
on_ping,
on_pong,
logger,
proxies,
):
return BinanceSocketManager(
stream_url,
Expand All @@ -57,6 +62,7 @@ def _initialize_socket(
on_ping=on_ping,
on_pong=on_pong,
logger=logger,
proxies=proxies,
)

def _single_stream(self, stream):
Expand Down
10 changes: 10 additions & 0 deletions docs/source/CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@
Changelog
=========

3.3.0 - 2023-08-07
------------------

Changed
^^^^^^^

* Add support for proxy in Websocket clients
* Remove support for python 3.7


3.2.0 - 2023-08-01
------------------

Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@
"Intended Audience :: Developers",
"Intended Audience :: Financial and Insurance Industry",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
],
python_requires=">=3.7",
python_requires=">=3.8",
)
34 changes: 34 additions & 0 deletions tests/lib/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import pytest

from binance.error import (
ParameterRequiredError,
ParameterTypeError,
Expand All @@ -8,6 +10,7 @@
check_type_parameter,
convert_list_to_json_array,
purge_map,
parse_proxies,
)
from binance.lib.utils import check_required_parameters
from binance.lib.utils import check_enum_parameter
Expand Down Expand Up @@ -117,3 +120,34 @@ def test_remove_empty_parameter():
purge_map({"foo": "bar", "foo2": 0}).should.equal({"foo": "bar"})
purge_map({"foo": "bar", "foo2": []}).should.equal({"foo": "bar", "foo2": []})
purge_map({"foo": "bar", "foo2": {}}).should.equal({"foo": "bar", "foo2": {}})


def test_parse_proxies():
proxies = {"http": "http://1.2.3.4:8080"}
output = {
"http_proxy_host": "1.2.3.4",
"http_proxy_port": 8080,
"http_proxy_auth": None,
}

proxy_data = parse_proxies(proxies)
assert proxy_data == output

proxies_2 = {"https": "http://1.2.3.4:8080"}

proxy_data_2 = parse_proxies(proxies_2)
assert proxy_data_2 == output


def test_parse_proxies_non_supported():
proxies = {"socks5": "http://1.2.3.4:8080"}

proxy_data = parse_proxies(proxies)
assert proxy_data == {}


def test_parse_proxies_invalid():
proxies = {"http": "http://x1.2.3.4.6-8080:x"}

with pytest.raises(ValueError):
parse_proxies(proxies)
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[tox]
envlist = py36,py37,py38,py39
envlist = py38,py39,310,311

[testenv]
deps =
Expand Down

0 comments on commit bcb7bb0

Please sign in to comment.