From b162d945c17444d36e3fef1f50ef8b14f461467c Mon Sep 17 00:00:00 2001 From: davidMkCb <159589283+davidMkCb@users.noreply.github.com> Date: Fri, 15 Nov 2024 12:24:47 -0800 Subject: [PATCH] Release v1.8.1 (#81) --- CHANGELOG.md | 8 ++++++++ README.md | 15 +++++++++++---- coinbase/__version__.py | 2 +- coinbase/rest/types/base_response.py | 14 ++++++++++++++ coinbase/websocket/types/__init__.py | 0 coinbase/websocket/types/base_response.py | 14 ++++++++++++++ tests/rest/test_accounts.py | 9 ++------- tests/rest/test_orders.py | 7 ++----- 8 files changed, 52 insertions(+), 17 deletions(-) create mode 100644 coinbase/websocket/types/__init__.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 3654b39..2eb2b01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [1.8.1] - 2024-NOV-15 + +### Added +- Support for converting custom REST and WebSocket response type objects to dict via new `to_dict()` function + +### Fixed +- Importing from `coinbase.websocket.types` + ## [1.8.0] - 2024-NOV-12 ### Added diff --git a/README.md b/README.md index 5d12a8c..621e133 100644 --- a/README.md +++ b/README.md @@ -69,10 +69,10 @@ You are able to use any of the API hooks to make calls to the Coinbase API. For from json import dumps accounts = client.get_accounts() -print(dumps(accounts, indent=2)) +print(dumps(accounts.to_dict(), indent=2)) order = client.market_order_buy(client_order_id="clientOrderId", product_id="BTC-USD", quote_size="1") -print(dumps(order, indent=2)) +print(dumps(order.to_dict(), indent=2)) ``` This code calls the `get_accounts` and `market_order_buy` endpoints. @@ -270,7 +270,12 @@ The functions described above handle the asynchronous nature of WebSocket connec We similarly provide async channel specific methods for subscribing and unsubscribing such as `ticker_async`, `ticker_unsubscribe_async`, etc. ### WebSocket Response Types -For your convenience, we have provided a custom, built-in WebSocket response type object to help interact with our WebSocket feeds more easily. +For your convenience, we have provided a custom, built-in WebSocket response type object to help interact with our WebSocket feeds more easily. +Simply import it from the same module as you do the `WSClient`: + +```python +from coinbase.websocket import WSClient, WebsocketResponse +``` Assume we simply want the price feed for BTC-USD and ETH-USD. Like we did in previous steps, we subscribe to the `ticker` channel and include 'BTC-USD' and 'ETH-USD' in the `product_ids` list. @@ -278,6 +283,8 @@ As the data comes through, it is passed into the `on_message` function. From the Using said object, we can now extract only the desired parts. In our case, we retrieve and print only the `product_id` and `price` fields, resulting in a cleaner feed. ```python +from coinbase.websocket import WSClient, WebsocketResponse + def on_message(msg): ws_object = WebsocketResponse(json.loads(msg)) if ws_object.channel == "ticker" : @@ -366,7 +373,7 @@ from coinbase.rest import RESTClient client = RESTClient() public_products = client.get_public_products() -print(public_products) +print(json.dumps(public_products.to_dict(), indent=2)) ``` _Full list of all public REST endpoints [here](https://docs.cdp.coinbase.com/advanced-trade/docs/rest-api-overview#public-endpoints)_ diff --git a/coinbase/__version__.py b/coinbase/__version__.py index 29654ee..2d986fc 100644 --- a/coinbase/__version__.py +++ b/coinbase/__version__.py @@ -1 +1 @@ -__version__ = "1.8.0" +__version__ = "1.8.1" diff --git a/coinbase/rest/types/base_response.py b/coinbase/rest/types/base_response.py index d62fac1..90d7047 100644 --- a/coinbase/rest/types/base_response.py +++ b/coinbase/rest/types/base_response.py @@ -21,3 +21,17 @@ def __getitem__(self, key: str) -> Any: def __repr__(self): return str(self.__dict__) + + def to_dict(self) -> dict: + dict_response = {} + for key, value in self.__dict__.items(): + if isinstance(value, BaseResponse): + dict_response[key] = value.to_dict() + elif isinstance(value, list): + dict_response[key] = [ + item.to_dict() if isinstance(item, BaseResponse) else item + for item in value + ] + else: + dict_response[key] = value + return dict_response diff --git a/coinbase/websocket/types/__init__.py b/coinbase/websocket/types/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/coinbase/websocket/types/base_response.py b/coinbase/websocket/types/base_response.py index 2feb28b..8e1944a 100644 --- a/coinbase/websocket/types/base_response.py +++ b/coinbase/websocket/types/base_response.py @@ -11,3 +11,17 @@ def __getitem__(self, key: str) -> Any: def __repr__(self): return str(self.__dict__) + + def to_dict(self) -> dict: + dict_response = {} + for key, value in self.__dict__.items(): + if isinstance(value, BaseResponse): + dict_response[key] = value.to_dict() + elif isinstance(value, list): + dict_response[key] = [ + item.to_dict() if isinstance(item, BaseResponse) else item + for item in value + ] + else: + dict_response[key] = value + return dict_response diff --git a/tests/rest/test_accounts.py b/tests/rest/test_accounts.py index 0ebe317..3483511 100644 --- a/tests/rest/test_accounts.py +++ b/tests/rest/test_accounts.py @@ -4,7 +4,6 @@ from coinbase.rest import RESTClient from coinbase.rest.types.accounts_types import ListAccountsResponse -from tests.rest.serialize import object_to_dict from ..constants import TEST_API_KEY, TEST_API_SECRET @@ -32,12 +31,8 @@ def test_get_accounts(self): captured_request.query, "limit=2&cursor=abcd&retail_portfolio_id=portfolio1", ) - actual_response_dict = object_to_dict(accounts) - expected_response_dict = object_to_dict( - ListAccountsResponse(expected_response) - ) - print(actual_response_dict) - print(expected_response_dict) + actual_response_dict = accounts.to_dict() + expected_response_dict = ListAccountsResponse(expected_response).to_dict() self.assertEqual(actual_response_dict, expected_response_dict) def test_get_account(self): diff --git a/tests/rest/test_orders.py b/tests/rest/test_orders.py index 0a09096..e70afd0 100644 --- a/tests/rest/test_orders.py +++ b/tests/rest/test_orders.py @@ -6,7 +6,6 @@ from coinbase.rest.types.orders_types import ListOrdersResponse from ..constants import TEST_API_KEY, TEST_API_SECRET -from .serialize import object_to_dict class OrdersTest(unittest.TestCase): @@ -1112,10 +1111,8 @@ def test_list_orders(self): captured_request.query, "product_ids=product_id_1&product_ids=product_id_2&order_status=open&limit=2&product_type=spot", ) - actual_response_dict = object_to_dict(orders) - expected_response_dict = object_to_dict( - ListOrdersResponse(expected_response) - ) + actual_response_dict = orders.to_dict() + expected_response_dict = ListOrdersResponse(expected_response).to_dict() self.assertEqual(actual_response_dict, expected_response_dict) def test_get_fills(self):