diff options
author | codl <[email protected]> | 2017-10-04 16:08:21 +0200 |
---|---|---|
committer | codl <[email protected]> | 2017-10-04 16:08:21 +0200 |
commit | 0ff6abf2f4ac9ebdc13391fc8e52b3dcc297f1fa (patch) | |
tree | 0a2630de6a135f14de1e2c7fd19f658e6c8d6a23 | |
parent | b18b6f201b75f704655e0784930a74050fb196b9 (diff) | |
download | mastodon.py-0ff6abf2f4ac9ebdc13391fc8e52b3dcc297f1fa.tar.gz |
fix #92, check for throttling by status code, and do it before the catchall error handler
-rw-r--r-- | mastodon/Mastodon.py | 62 |
1 files changed, 32 insertions, 30 deletions
diff --git a/mastodon/Mastodon.py b/mastodon/Mastodon.py index 5b198a5..7a1d701 100644 --- a/mastodon/Mastodon.py +++ b/mastodon/Mastodon.py | |||
@@ -1047,6 +1047,25 @@ class Mastodon: | |||
1047 | if response_object is None: | 1047 | if response_object is None: |
1048 | raise MastodonIllegalArgumentError("Illegal request.") | 1048 | raise MastodonIllegalArgumentError("Illegal request.") |
1049 | 1049 | ||
1050 | # Parse rate limiting headers | ||
1051 | if 'X-RateLimit-Remaining' in response_object.headers and do_ratelimiting: | ||
1052 | self.ratelimit_remaining = int(response_object.headers['X-RateLimit-Remaining']) | ||
1053 | self.ratelimit_limit = int(response_object.headers['X-RateLimit-Limit']) | ||
1054 | |||
1055 | try: | ||
1056 | ratelimit_reset_datetime = dateutil.parser.parse(response_object.headers['X-RateLimit-Reset']) | ||
1057 | self.ratelimit_reset = self.__datetime_to_epoch(ratelimit_reset_datetime) | ||
1058 | |||
1059 | # Adjust server time to local clock | ||
1060 | if 'Date' in response_object.headers: | ||
1061 | server_time_datetime = dateutil.parser.parse(response_object.headers['Date']) | ||
1062 | server_time = self.__datetime_to_epoch(server_time_datetime) | ||
1063 | server_time_diff = time.time() - server_time | ||
1064 | self.ratelimit_reset += server_time_diff | ||
1065 | self.ratelimit_lastcall = time.time() | ||
1066 | except Exception as e: | ||
1067 | raise MastodonRatelimitError("Rate limit time calculations failed: %s" % e) | ||
1068 | |||
1050 | # Handle response | 1069 | # Handle response |
1051 | if self.debug_requests: | 1070 | if self.debug_requests: |
1052 | print('Mastodon: Response received with code ' + str(response_object.status_code) + '.') | 1071 | print('Mastodon: Response received with code ' + str(response_object.status_code) + '.') |
@@ -1068,6 +1087,19 @@ class Mastodon: | |||
1068 | if response_object.status_code == 500: | 1087 | if response_object.status_code == 500: |
1069 | raise MastodonAPIError('General API problem.') | 1088 | raise MastodonAPIError('General API problem.') |
1070 | 1089 | ||
1090 | # Handle rate limiting | ||
1091 | if response_object.status_code == 429: | ||
1092 | if self.ratelimit_method == 'throw' or not do_ratelimiting: | ||
1093 | raise MastodonRatelimitError('Hit rate limit.') | ||
1094 | elif self.ratelimit_method in ('wait', 'pace'): | ||
1095 | to_next = self.ratelimit_reset - time.time() | ||
1096 | if to_next > 0: | ||
1097 | # As a precaution, never sleep longer than 5 minutes | ||
1098 | to_next = min(to_next, 5 * 60) | ||
1099 | time.sleep(to_next) | ||
1100 | request_complete = False | ||
1101 | continue | ||
1102 | |||
1071 | try: | 1103 | try: |
1072 | response = response_object.json(object_hook=self.__json_date_parse) | 1104 | response = response_object.json(object_hook=self.__json_date_parse) |
1073 | except: | 1105 | except: |
@@ -1118,36 +1150,6 @@ class Mastodon: | |||
1118 | del prev_params['max_id'] | 1150 | del prev_params['max_id'] |
1119 | response[0]['_pagination_prev'] = prev_params | 1151 | response[0]['_pagination_prev'] = prev_params |
1120 | 1152 | ||
1121 | # Handle rate limiting | ||
1122 | if 'X-RateLimit-Remaining' in response_object.headers and do_ratelimiting: | ||
1123 | self.ratelimit_remaining = int(response_object.headers['X-RateLimit-Remaining']) | ||
1124 | self.ratelimit_limit = int(response_object.headers['X-RateLimit-Limit']) | ||
1125 | |||
1126 | try: | ||
1127 | ratelimit_reset_datetime = dateutil.parser.parse(response_object.headers['X-RateLimit-Reset']) | ||
1128 | self.ratelimit_reset = self.__datetime_to_epoch(ratelimit_reset_datetime) | ||
1129 | |||
1130 | # Adjust server time to local clock | ||
1131 | if 'Date' in response_object.headers: | ||
1132 | server_time_datetime = dateutil.parser.parse(response_object.headers['Date']) | ||
1133 | server_time = self.__datetime_to_epoch(server_time_datetime) | ||
1134 | server_time_diff = time.time() - server_time | ||
1135 | self.ratelimit_reset += server_time_diff | ||
1136 | self.ratelimit_lastcall = time.time() | ||
1137 | except Exception as e: | ||
1138 | raise MastodonRatelimitError("Rate limit time calculations failed: %s" % e) | ||
1139 | |||
1140 | if "error" in response and response["error"] == "Throttled": | ||
1141 | if self.ratelimit_method == "throw": | ||
1142 | raise MastodonRatelimitError("Hit rate limit.") | ||
1143 | |||
1144 | if self.ratelimit_method == "wait" or self.ratelimit_method == "pace": | ||
1145 | to_next = self.ratelimit_reset - time.time() | ||
1146 | if to_next > 0: | ||
1147 | # As a precaution, never sleep longer than 5 minutes | ||
1148 | to_next = min(to_next, 5 * 60) | ||
1149 | time.sleep(to_next) | ||
1150 | request_complete = False | ||
1151 | 1153 | ||
1152 | return response | 1154 | return response |
1153 | 1155 | ||