Skip to content
This repository was archived by the owner on Jul 3, 2020. It is now read-only.

Commit 7c46b02

Browse files
authored
Merge pull request #123 from puzzlement/master
Add support for re-trying on Slack's 429 rate limited response after their suggested wait time. Default is no retries.
2 parents 7a712b9 + 5075974 commit 7c46b02

File tree

1 file changed

+36
-8
lines changed

1 file changed

+36
-8
lines changed

slacker/__init__.py

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,18 @@
1616

1717
import requests
1818

19+
import time
20+
1921
from slacker.utils import get_item_id_by_name
2022

2123

2224
__version__ = '0.9.60'
2325

2426
API_BASE_URL = 'https://slack.com/api/{api}'
2527
DEFAULT_TIMEOUT = 10
26-
28+
DEFAULT_RETRIES = 0
29+
# seconds to wait after a 429 error if Slack's API doesn't provide one
30+
DEFAULT_WAIT = 20
2731

2832
__all__ = ['Error', 'Response', 'BaseAPI', 'API', 'Auth', 'Users', 'Groups',
2933
'Channels', 'Chat', 'IM', 'IncomingWebhook', 'Search', 'Files',
@@ -50,22 +54,45 @@ def __str__(self):
5054

5155
class BaseAPI(object):
5256
def __init__(self, token=None, timeout=DEFAULT_TIMEOUT, proxies=None,
53-
session=None):
57+
session=None, rate_limit_retries=DEFAULT_RETRIES):
5458
self.token = token
5559
self.timeout = timeout
5660
self.proxies = proxies
5761
self.session = session
62+
self.rate_limit_retries = rate_limit_retries
5863

5964
def _request(self, method, api, **kwargs):
6065
if self.token:
6166
kwargs.setdefault('params', {})['token'] = self.token
6267

63-
response = method(API_BASE_URL.format(api=api),
64-
timeout=self.timeout,
65-
proxies=self.proxies,
66-
**kwargs)
68+
# while we have rate limit retries left, fetch the resource and back
69+
# off as Slack's HTTP response suggests
70+
for retry_num in range(self.rate_limit_retries):
71+
response = method(API_BASE_URL.format(api=api),
72+
timeout=self.timeout,
73+
proxies=self.proxies,
74+
**kwargs)
75+
76+
if response.status_code == requests.codes.ok:
77+
break
6778

68-
response.raise_for_status()
79+
# handle HTTP 429 as documented at
80+
# https://api.slack.com/docs/rate-limits
81+
elif response.status_code == requests.codes.too_many: # HTTP 429
82+
time.sleep(int(response.headers.get('retry-after', DEFAULT_WAIT)))
83+
continue
84+
85+
else:
86+
response.raise_for_status()
87+
88+
else:
89+
# with no retries left, make one final attempt to fetch the resource,
90+
# but do not handle too_many status differently
91+
response = method(API_BASE_URL.format(api=api),
92+
timeout=self.timeout,
93+
proxies=self.proxies,
94+
**kwargs)
95+
response.raise_for_status()
6996

7097
response = Response(response.text)
7198
if not response.successful:
@@ -977,14 +1004,15 @@ class Slacker(object):
9771004

9781005
def __init__(self, token, incoming_webhook_url=None,
9791006
timeout=DEFAULT_TIMEOUT, http_proxy=None, https_proxy=None,
980-
session=None):
1007+
session=None, rate_limit_retries=DEFAULT_RETRIES):
9811008

9821009
proxies = self.__create_proxies(http_proxy, https_proxy)
9831010
api_args = {
9841011
'token': token,
9851012
'timeout': timeout,
9861013
'proxies': proxies,
9871014
'session': session,
1015+
'rate_limit_retries': rate_limit_retries,
9881016
}
9891017
self.im = IM(**api_args)
9901018
self.api = API(**api_args)

0 commit comments

Comments
 (0)