From 4d8752715bfba8270647ea9dd321f9b304286594 Mon Sep 17 00:00:00 2001 From: "Luiz Otavio V. B. Oliveira" Date: Fri, 14 Sep 2018 12:51:06 -0300 Subject: [PATCH] Adds code to deal with rate limits imposed by Product Hunt API Each API response includes 3 rate limit headers. You can use these headers to check your current rate limit status. - X-Rate-Limit-Limit: Your applications limit for the 15 minute period - X-Rate-Limit-Remaining: Remaining allowed requests for the reset period - X-Rate-Limit-Reset: Seconds until the rate limit is reset The methods `_update_time_limits` is called at each request and updates three new atributes of the class responsible for keeping track of these three limits. The method `get_rate_limits` is used to acess these values and `wait_if_no_rate_limit_remaining` wait the limit to be restabilished before making a new request. --- ph_py/product_hunt_client.py | 41 +++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/ph_py/product_hunt_client.py b/ph_py/product_hunt_client.py index 8a1351b..4dfe395 100644 --- a/ph_py/product_hunt_client.py +++ b/ph_py/product_hunt_client.py @@ -8,6 +8,7 @@ from .helpers import parse_notifications from .helpers import parse_related_links from simplejson.scanner import JSONDecodeError +import time class ProductHuntClient: @@ -26,8 +27,34 @@ def __init__(self, client_id, client_secret, redirect_uri, dev_token=None): else: self.user_auth = None + # Rate limit information recovered from the header + self._rate_lim_limit = 900 + self._rate_lim_remaining = 900 + self._rate_lim_reset = 15*60 + self.client_auth = self.oauth_client_token() + def get_rate_limits(self): + """Return the three rate limit headers from the last request made + + Returns + ------- + rate_lim_limit: float + The application limit for the 15 minute period + rate_lim_remaining: float + Remaining allowed requests for the reset period + rate_lim_reset: float + Seconds until the rate limit is reset + """ + return self._rate_lim_limit, self._rate_lim_remaining, \ + self._rate_lim_reset + + def wait_if_no_rate_limit_remaining(self): + if self._rate_lim_remaining <= 0: + print("Waiting {:d} seconds until next request".format( + self._rate_lim_reset)) + time.sleep(self._rate_lim_reset) + def build_header(self, context): if context == "client": if self.client_auth is None: @@ -40,6 +67,16 @@ def build_header(self, context): return {"Authorization": "Bearer %s" % self.user_auth["access_token"]} + def _update_time_limits(self, headers): + if headers: + if 'X-Rate-Limit-Limit' in headers: + self._rate_lim_limit = int(headers['X-Rate-Limit-Limit']) + if 'X-Rate-Limit-Remaining' in headers: + self._rate_lim_remaining = int( + headers['X-Rate-Limit-Remaining']) + if 'X-Rate-Limit-Reset' in headers: + self._rate_lim_reset = int(headers['X-Rate-Limit-Reset']) + def make_request(self, method, route, data, context="", retry=False): url = self.API_BASE + route @@ -56,6 +93,8 @@ def make_request(self, method, route, data, context="", retry=False): elif method == "DELETE": response = r.delete(url, headers=headers, params=data) + self._update_time_limits(response.headers) + try: json_data = response.json() if response.status_code in self.ERROR_CODES: @@ -114,9 +153,9 @@ def get_previous_days_posts(self, days_ago, context="client"): def get_specific_days_posts(self, day, context="client"): responses = self.make_request("GET", "posts", {"day": day}, context) responses = responses["posts"] - return parse_posts(responses) + def get_details_of_post(self, post_id, context="client"): post = self.make_request("GET", "posts/%d" % post_id, None, context) return parse_posts(post["post"])