Skip to content

Commit fd0e121

Browse files
committed
Resolve "Handle rate limit"
1 parent 3b4f69f commit fd0e121

2 files changed

Lines changed: 42 additions & 2 deletions

File tree

hcloud/hcloud.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# -*- coding: utf-8 -*-
22
from __future__ import absolute_import
33

4+
import time
45
import requests
56

67
from hcloud.actions.client import ActionsClient
@@ -26,6 +27,7 @@ def __init__(self, code, message, details):
2627

2728
class HcloudClient(object):
2829
version = VERSION
30+
retry_wait_time = 0.5
2931

3032
def __init__(self, token):
3133
self.token = token
@@ -68,7 +70,7 @@ def _raise_exception_from_json_content(self, json_content):
6870
details=json_content['error']['details']
6971
)
7072

71-
def request(self, method, url, **kwargs):
73+
def request(self, method, url, tries=1, **kwargs):
7274
response = requests.request(
7375
method,
7476
self.api_endpoint + url,
@@ -85,7 +87,12 @@ def request(self, method, url, **kwargs):
8587

8688
if not response.ok:
8789
if json_content:
88-
self._raise_exception_from_json_content(json_content)
90+
if json_content['error']['code'] == "rate_limit_exceeded" and tries < 5:
91+
time.sleep(tries * self.retry_wait_time)
92+
tries = tries + 1
93+
return self.request(method, url, tries, **kwargs)
94+
else:
95+
self._raise_exception_from_json_content(json_content)
8996
else:
9097
self._raise_exception_from_response(response)
9198

tests/unit/test_hcloud.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,19 @@ def fail_response(self, response):
3838
response._content = json.dumps({"error": error}).encode('utf-8')
3939
return response
4040

41+
@pytest.fixture()
42+
def rate_limit_response(self, response):
43+
response.status_code = 422
44+
error = {
45+
"code": "rate_limit_exceeded",
46+
"message": "limit of 10 requests per hour reached",
47+
"details": {
48+
49+
}
50+
}
51+
response._content = json.dumps({"error": error}).encode('utf-8')
52+
return response
53+
4154
def test__get_user_agent(self, client):
4255
user_agent = client._get_user_agent()
4356
assert user_agent == "hcloud-python/0.0.0"
@@ -114,3 +127,23 @@ def test_request_500_empty_content(self, mocked_requests, client, fail_response)
114127
assert error.code == 500
115128
assert error.message == "Internal Server Error"
116129
assert error.details["content"] == ""
130+
131+
def test_request_limit(self, mocked_requests, client, rate_limit_response):
132+
client.retry_wait_time = 0
133+
mocked_requests.request.return_value = rate_limit_response
134+
with pytest.raises(HcloudAPIException) as exception_info:
135+
client.request("POST", "http://url.com", params={"argument": "value"}, timeout=2)
136+
error = exception_info.value
137+
assert mocked_requests.request.call_count == 5
138+
assert error.code == "rate_limit_exceeded"
139+
assert error.message == "limit of 10 requests per hour reached"
140+
141+
def test_request_limit_then_success(self, mocked_requests, client, rate_limit_response):
142+
client.retry_wait_time = 0
143+
response = requests.Response()
144+
response.status_code = 200
145+
response._content = json.dumps({"result": "data"}).encode('utf-8')
146+
mocked_requests.request.side_effect = [rate_limit_response, response]
147+
148+
client.request("POST", "http://url.com", params={"argument": "value"}, timeout=2)
149+
assert mocked_requests.request.call_count == 2

0 commit comments

Comments
 (0)