Skip to content

Commit 8f6d058

Browse files
committed
Hotfixes:
- Events response object uses .get() method now to accomodate different EventType HTTP responses. - Various typehint error fixes. - Fixed some duplicate tests coming from git merge/rebase mistakes.
1 parent e725636 commit 8f6d058

File tree

12 files changed

+75
-135
lines changed

12 files changed

+75
-135
lines changed

open_sea_v1/endpoints/assets.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def url(self):
7676

7777
@property
7878
def parsed_http_response(self) -> list[AssetResponse]:
79-
return self.parse_http_response(AssetResponse, 'assets')
79+
return self.parse_http_response(AssetResponse, 'assets') # type: ignore
8080

8181
def _get_request(self, **kwargs):
8282
params = dict(

open_sea_v1/endpoints/client.py

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,29 @@ class ClientParams:
1818
"""Common OpenSea Endpoint parameters to pass in."""
1919
offset: int = 0
2020
page_size: int = 50
21-
limit: Optional[int] = None
21+
limit: int = 50
2222
max_pages: Optional[int] = None
2323
api_key: Optional[str] = None
2424

2525
def __post_init__(self):
26+
if self.max_pages:
27+
self.max_pages += 1 # prevent paginator from ending one page early
2628
self._validate_attrs()
27-
self._decrement_max_pages_attr()
2829
self._set_max_rate_limit()
2930

3031
def _validate_attrs(self) -> None:
31-
if self.limit is not None and not 0 < self.limit <= 300:
32+
if not 0 < self.limit <= 300:
3233
raise ValueError(f'{self.limit=} must be over 0 and lesser or equal to 300.')
33-
if self.page_size is not None and not 0 <= self.page_size <= 50:
34+
35+
if self.max_pages is not None and self.max_pages <= 0:
36+
raise ValueError(f'{self.max_pages=} must be greater than 0.')
37+
38+
if not 0 <= self.page_size <= 50:
3439
raise ValueError(f'{self.page_size=} must be between 0 and 50.')
40+
41+
if self.limit < self.page_size:
42+
raise ValueError(f'{self.limit=} cannot be lesser than {self.page_size=}.')
43+
3544
if self.max_pages is not None and self.max_pages < 0:
3645
raise ValueError(f'{self.max_pages=} must be greater than or equal to 0.')
3746

@@ -49,6 +58,7 @@ def _set_max_rate_limit(self) -> None:
4958
if self.api_key:
5059
raise NotImplementedError("I don't know what the rate limit is for calls with an API key is yet.")
5160

61+
5262
@dataclass
5363
class BaseClient(ABC):
5464
"""
@@ -61,20 +71,11 @@ class BaseClient(ABC):
6171
If True, will throttle the amount of requests per second to the OpenSea API.
6272
If you pass an API key into the client_params instance, the rate limiting will change accordingly.
6373
If False, will not throttle.
64-
65-
retry: bool
66-
OpenSea will occasionally return an empty response object, although the same query would yield
67-
a full response object afterwards.
68-
If True, will pause the request for one second before trying again once.
69-
If it fails again, the empty response is returned.
70-
If set to False, the client always returns the first response object, even if it is empty.
71-
7274
"""
7375

7476
client_params: ClientParams
7577
url = None
7678
rate_limiting: bool = True
77-
retry: bool = True
7879

7980
def __post_init__(self):
8081
self.processed_pages: int = 0
@@ -115,17 +116,20 @@ def get_pages(self) -> Generator[list[list[BaseResponse]], None, None]:
115116
if self.parsed_http_response is not None: # edge case
116117
self.processed_pages += 1
117118
self.client_params.offset += self.client_params.page_size
119+
self.client_params._decrement_max_pages_attr()
120+
if not self.parsed_http_response:
121+
break # prevents returning last empty page
118122
yield self.parsed_http_response
119123

120124
def remaining_pages(self) -> bool:
121125
if self._http_response is None:
122126
return True
123127

124-
if (max_pages_was_set := self.client_params.max_pages is not None) and \
125-
(previous_page_was_not_empty := len(self.parsed_http_response) > 0) and \
126-
(remaining_pages_until_max_pages := self.processed_pages <= self.client_params.max_pages):
127-
return True
128+
if is_the_last_page := len(self.parsed_http_response) < self.client_params.page_size:
129+
return False
128130

129-
if is_not_the_last_page := len(self.parsed_http_response) >= self.client_params.offset:
130-
return True
131-
return False
131+
max_pages_reached: bool = self.client_params.max_pages is not None and self.client_params.max_pages <= 0
132+
if max_pages_reached:
133+
return False
134+
135+
return True

open_sea_v1/endpoints/collections.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def url(self):
5050

5151
@property
5252
def parsed_http_response(self) -> list[CollectionResponse]:
53-
return self.parse_http_response(CollectionResponse, 'collections')
53+
return self.parse_http_response(CollectionResponse, 'collections') # type: ignore
5454

5555
def _get_request(self, **kwargs):
5656
params = dict(

open_sea_v1/endpoints/events.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ def _get_request(self, **kwargs) -> Response:
106106

107107
@property
108108
def parsed_http_response(self) -> list[EventResponse]:
109-
return self.parse_http_response(EventResponse, 'asset_events')
109+
return self.parse_http_response(EventResponse, 'asset_events') # type: ignore
110110

111111
def _validate_request_params(self) -> None:
112112
self._validate_param_auction_type()

open_sea_v1/endpoints/orders.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from open_sea_v1.endpoints.abc import BaseEndpoint
77
from open_sea_v1.endpoints.client import BaseClient, ClientParams, MAX_CALLS_PER_SECOND, RATE_LIMIT
88
from open_sea_v1.endpoints.urls import EndpointURLS
9+
from open_sea_v1.responses.collection import CollectionResponse
910
from open_sea_v1.responses.order import OrderResponse
1011

1112

@@ -107,15 +108,13 @@ class OrdersEndpoint(BaseClient, BaseEndpoint):
107108

108109
def __post_init__(self):
109110
self._validate_request_params()
110-
if not self.client_params:
111-
raise AttributeError('Attribute client_params is missing.')
112111

113112
@property
114113
def url(self):
115114
return EndpointURLS.ORDERS.value
116115

117116
@property
118-
def parsed_http_response(self) -> list[CollectionResponse]:
117+
def parsed_http_response(self) -> list[OrderResponse]:
119118
orders_jsons = self._http_response.json()['orders']
120119
orders = [OrderResponse(order_json) for order_json in orders_jsons]
121120
return orders

open_sea_v1/endpoints/tests/test_assets.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def test_param_token_ids_raise_exception_if_missing_contract_address_and_address
3535
self.assertRaises(ValueError, AssetsEndpoint, token_ids=[1, 2, 3])
3636

3737
def test_params_cannot_be_simultaneously_be_passed_asset_contract_address_and_contract_addresses(self):
38-
params = self.default_asset_params | dict(asset_contract_address=True, asset_contract_addresses=True)
38+
params = self.default_asset_params | dict(asset_contract_address=True, asset_contract_addresses=True) # type: ignore
3939
self.assertRaises(ValueError, AssetsEndpoint, **params)
4040

4141
def test_param_token_ids_returns_assets_corresponding_to_single_contract(self):

open_sea_v1/endpoints/tests/test_client.py

Lines changed: 1 addition & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -6,63 +6,6 @@
66
from open_sea_v1.responses.event import EventResponse
77

88

9-
10-
class TestClientParams(TestCase):
11-
12-
def test_max_pages_attr_is_automatically_decremented_by_1(self):
13-
params = ClientParams(max_pages=1)
14-
self.assertEqual(params.max_pages, 0)
15-
16-
def test_max_pages_attr_raises_value_error_if_below_or_equal_to_zero(self):
17-
self.assertRaises(ValueError, ClientParams, max_pages=-1)
18-
19-
def test_limit_attr_raises_value_error_if_not_between_0_and_300(self):
20-
self.assertRaises(ValueError, ClientParams, limit=-1)
21-
self.assertRaises(ValueError, ClientParams, limit=301)
22-
23-
def test_page_size_attr_raises_value_error_if_not_between_0_and_50(self):
24-
self.assertRaises(ValueError, ClientParams, page_size=-1)
25-
self.assertRaises(ValueError, ClientParams, page_size=51)
26-
27-
28-
class TestClientParams(TestCase):
29-
30-
def test_max_pages_attr_is_automatically_decremented_by_1(self):
31-
params = ClientParams(max_pages=1)
32-
self.assertEqual(params.max_pages, 0)
33-
34-
def test_max_pages_attr_raises_value_error_if_below_or_equal_to_zero(self):
35-
self.assertRaises(ValueError, ClientParams, max_pages=-1)
36-
37-
def test_limit_attr_raises_value_error_if_not_between_0_and_300(self):
38-
self.assertRaises(ValueError, ClientParams, limit=-1)
39-
self.assertRaises(ValueError, ClientParams, limit=301)
40-
41-
def test_page_size_attr_raises_value_error_if_not_between_0_and_50(self):
42-
self.assertRaises(ValueError, ClientParams, page_size=-1)
43-
self.assertRaises(ValueError, ClientParams, page_size=51)
44-
45-
46-
47-
48-
class TestClientParams(TestCase):
49-
50-
def test_max_pages_attr_is_automatically_decremented_by_1(self):
51-
params = ClientParams(max_pages=1)
52-
self.assertEqual(params.max_pages, 0)
53-
54-
def test_max_pages_attr_raises_value_error_if_below_or_equal_to_zero(self):
55-
self.assertRaises(ValueError, ClientParams, max_pages=-1)
56-
57-
def test_limit_attr_raises_value_error_if_not_between_0_and_300(self):
58-
self.assertRaises(ValueError, ClientParams, limit=-1)
59-
self.assertRaises(ValueError, ClientParams, limit=301)
60-
61-
def test_page_size_attr_raises_value_error_if_not_between_0_and_50(self):
62-
self.assertRaises(ValueError, ClientParams, page_size=-1)
63-
self.assertRaises(ValueError, ClientParams, page_size=51)
64-
65-
669
class TestClientParams(TestCase):
6710

6811
def test_max_pages_attr_is_automatically_decremented_by_1(self):
@@ -158,7 +101,7 @@ def test_pagination_does_not_return_duplicates_between_different_pages(self):
158101
limit = 2
159102
max_pages = 2
160103
self.sample_client.client_params = ClientParams(limit=limit, page_size=limit, max_pages=max_pages)
161-
events_resps: list[EventResponse] = list(chain.from_iterable(self.sample_client.get_pages()))
104+
events_resps: list[EventResponse] = list(chain.from_iterable(self.sample_client.get_pages())) # type: ignore
162105
unique_ids = set(e.id for e in events_resps)
163106
self.assertEqual(len(unique_ids), (limit+1) * max_pages)
164107

open_sea_v1/endpoints/tests/test_events.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@ def test_param_auction_type_raises_if_not_from_auction_type_enum_values(self):
3838
self.assertRaises((ValueError, TypeError), self.create_and_get, **updated_kwargs)
3939

4040
def test_param_auction_type_raises_if_not_isinstance_of_str(self):
41-
updated_kwargs = self.events_default_kwargs | dict(auction_type=0.0)
41+
updated_kwargs = self.events_default_kwargs | dict(auction_type=0.0) # type: ignore
4242
self.assertRaises((ValueError, TypeError), self.create_and_get, **updated_kwargs)
4343

4444
def test_param_auction_type_does_not_raise_if_is_none(self):
45-
updated_kwargs = self.events_default_kwargs | dict(auction_type=None)
45+
updated_kwargs = self.events_default_kwargs | dict(auction_type=None) # type: ignore
4646
self.create_and_get(**updated_kwargs)
4747

4848
def test_param_only_opensea_true_filters_properly(self):
@@ -55,30 +55,30 @@ def test_param_only_opensea_false_does_not_filter(self):
5555
"""
5656
Have no idea how to test this param.
5757
"""
58-
# updated_kwargs = self.events_default_kwargs | dict(only_opensea=False, offset=1, limit=100)
58+
# updated_kwargs = self.events_kwargs | dict(only_opensea=False, offset=1, limit=100)
5959
# events = self.create_and_get(**updated_kwargs)
6060
# self.assertTrue(any('opensea.io' not in event.asset.permalink for event in events))
6161
pass
6262

6363
def test_param_occurred_before_raises_exception_if_not_datetime_instances(self):
64-
updated_kwargs = self.events_default_kwargs | dict(occurred_before=True)
64+
updated_kwargs = self.events_default_kwargs | dict(occurred_before=True) # type: ignore
6565
self.assertRaises(TypeError, self.create_and_get, **updated_kwargs)
6666

6767
def test_param_occurred_before_and_after_raises_exception_if_are_equal_values(self):
6868
dt_now = datetime.now()
6969
occurred_params = dict(occurred_before=dt_now, occurred_after=dt_now)
70-
updated_kwargs = self.events_default_kwargs | occurred_params
70+
updated_kwargs = self.events_default_kwargs | occurred_params # type: ignore
7171
self.assertRaises(ValueError, self.create_and_get, **updated_kwargs)
7272

7373
def test_param_occurred_before_and_after_does_not_raise_if_both_are_none(self):
74-
updated_kwargs = self.events_default_kwargs | dict(occurred_before=None, occurred_after=None)
74+
updated_kwargs = self.events_default_kwargs | dict(occurred_before=None, occurred_after=None) # type: ignore
7575
self.create_and_get(**updated_kwargs)
7676

7777
def test_param_occurred_after_cannot_be_higher_than_occurred_before(self):
7878
occurred_before = datetime.now()
7979
occurred_after = occurred_before + timedelta(microseconds=1)
8080
occurred_params = dict(occurred_before=occurred_before, occurred_after=occurred_after)
81-
updated_kwargs = self.events_default_kwargs | occurred_params
81+
updated_kwargs = self.events_default_kwargs | occurred_params # type: ignore
8282
self.assertRaises(ValueError, self.create_and_get, **updated_kwargs)
8383

8484
def test_param_occurred_after_filters_properly(self):

open_sea_v1/endpoints/tests/test_orders.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,6 @@ def test_attr_token_id_raises_if_not_defined_together_with_asset_contract_addres
3232
self.endpoint_kwargs |= dict(asset_contract_address='str')
3333
OrdersEndpoint(**self.endpoint_kwargs)
3434

35-
def test_attr_token_ids_raises_if_len_is_above_30(self):
36-
self.endpoint_kwargs |= dict(asset_contract_address=self.asset_contract_address, token_ids=list(range(1, 32)))
37-
self.assertRaises(ValueError, OrdersEndpoint, **self.endpoint_kwargs)
38-
39-
def test_attr_token_ids_raises_if_len_is_above_30(self):
40-
self.endpoint_kwargs |= dict(asset_contract_address=self.asset_contract_address, token_ids=list(range(1, 32)))
41-
self.assertRaises(ValueError, OrdersEndpoint, **self.endpoint_kwargs)
42-
4335
def test_attr_token_ids_raises_if_not_defined_together_with_asset_contract_address(self):
4436
self.endpoint_kwargs |= dict(token_ids=[1, 2])
4537
self.assertRaises(AttributeError, OrdersEndpoint, **self.endpoint_kwargs)

open_sea_v1/responses/asset.py

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -85,31 +85,28 @@ def __str__(self):
8585

8686
def __post_init__(self):
8787
self._set_common_attrs()
88-
self._set_optional_attrs()
8988

9089
def _set_common_attrs(self):
91-
self.token_id = str(self._json["token_id"])
92-
self.num_sales = self._json["num_sales"]
93-
self.background_color = self._json["background_color"]
94-
self.image_url = self._json["image_url"]
95-
self.image_preview_url = self._json["image_preview_url"]
96-
self.image_thumbnail_url = self._json["image_thumbnail_url"]
97-
self.image_original_url = self._json["image_original_url"]
98-
self.animation_url = self._json["animation_url"]
99-
self.animation_original_url = self._json["animation_original_url"]
100-
self.name = self._json["name"]
101-
self.description = self._json["description"]
102-
self.external_link = self._json["external_link"]
103-
self.permalink = self._json["permalink"]
104-
self.decimals = self._json["decimals"]
105-
self.token_metadata = self._json["token_metadata"]
106-
self.id = str(self._json["id"])
107-
108-
def _set_optional_attrs(self):
10990
"""
110-
Most asset responses are alike, but some are returned with less information.
111-
To avoid raising KeyErrors, we will use the .get method when setting these attributes.
91+
Depending on the EventType you request, some elements of the json response will be missing.
92+
For that reason we use .get() on every element.
11293
"""
94+
self.token_id = str(self._json.get("token_id") or '')
95+
self.num_sales = self._json.get("num_sales")
96+
self.background_color = self._json.get("background_color")
97+
self.image_url = self._json.get("image_url")
98+
self.image_preview_url = self._json.get("image_preview_url")
99+
self.image_thumbnail_url = self._json.get("image_thumbnail_url")
100+
self.image_original_url = self._json.get("image_original_url")
101+
self.animation_url = self._json.get("animation_url")
102+
self.animation_original_url = self._json.get("animation_original_url")
103+
self.name = self._json.get("name")
104+
self.description = self._json.get("description")
105+
self.external_link = self._json.get("external_link")
106+
self.permalink = self._json.get("permalink")
107+
self.decimals = self._json.get("decimals")
108+
self.token_metadata = self._json.get("token_metadata")
109+
self.id = str(self._json.get("id") or '')
113110
self.transfer_fee = self._json.get("transfer_fee")
114111
self.transfer_fee_payment_token = self._json.get("transfer_fee_payment_token")
115112
self.is_presale = self._json.get("is_presale")

0 commit comments

Comments
 (0)