Merge branch 'master' into cleanup-methods
This commit is contained in:
commit
0610b07682
|
@ -16,7 +16,10 @@ import dateutil.parser
|
|||
import re
|
||||
import copy
|
||||
import threading
|
||||
from urllib.parse import urlparse
|
||||
try:
|
||||
from urllib.parse import urlparse
|
||||
except ImportError:
|
||||
from urlparse import urlparse
|
||||
|
||||
|
||||
class Mastodon:
|
||||
|
@ -569,7 +572,7 @@ class Mastodon:
|
|||
def toot(self, status):
|
||||
"""
|
||||
Synonym for status_post that only takes the status text as input.
|
||||
|
||||
|
||||
Usage in production code is not recommended.
|
||||
|
||||
Returns a toot dict with the new status.
|
||||
|
@ -901,7 +904,7 @@ class Mastodon:
|
|||
|
||||
If async is False, this method blocks forever.
|
||||
|
||||
If async is True, 'listener' will listen on another thread and this method
|
||||
If async is True, 'listener' will listen on another thread and this method
|
||||
will return a handle corresponding to the open connection. The
|
||||
connection may be closed at any time by calling its close() method.
|
||||
"""
|
||||
|
@ -914,7 +917,7 @@ class Mastodon:
|
|||
|
||||
If async is False, this method blocks forever.
|
||||
|
||||
If async is True, 'listener' will listen on another thread and this method
|
||||
If async is True, 'listener' will listen on another thread and this method
|
||||
will return a handle corresponding to the open connection. The
|
||||
connection may be closed at any time by calling its close() method.
|
||||
"""
|
||||
|
@ -927,7 +930,7 @@ class Mastodon:
|
|||
|
||||
If async is False, this method blocks forever.
|
||||
|
||||
If async is True, 'listener' will listen on another thread and this method
|
||||
If async is True, 'listener' will listen on another thread and this method
|
||||
will return a handle corresponding to the open connection. The
|
||||
connection may be closed at any time by calling its close() method.
|
||||
"""
|
||||
|
@ -941,12 +944,12 @@ class Mastodon:
|
|||
|
||||
If async is False, this method blocks forever.
|
||||
|
||||
If async is True, 'listener' will listen on another thread and this method
|
||||
If async is True, 'listener' will listen on another thread and this method
|
||||
will return a handle corresponding to the open connection. The
|
||||
connection may be closed at any time by calling its close() method.
|
||||
"""
|
||||
return self.__stream("/api/v1/streaming/hashtag?tag={}".format(tag), listener)
|
||||
|
||||
|
||||
###
|
||||
# Internal helpers, dragons probably
|
||||
###
|
||||
|
@ -982,7 +985,7 @@ class Mastodon:
|
|||
except:
|
||||
raise MastodonAPIError('Encountered invalid date.')
|
||||
return json_object
|
||||
|
||||
|
||||
def __api_request(self, method, endpoint, params={}, files={}, do_ratelimiting=True):
|
||||
"""
|
||||
Internal API request helper.
|
||||
|
@ -1037,6 +1040,28 @@ class Mastodon:
|
|||
except Exception as e:
|
||||
raise MastodonNetworkError("Could not complete request: %s" % e)
|
||||
|
||||
if response_object is None:
|
||||
raise MastodonIllegalArgumentError("Illegal request.")
|
||||
|
||||
# Parse rate limiting headers
|
||||
if 'X-RateLimit-Remaining' in response_object.headers and do_ratelimiting:
|
||||
self.ratelimit_remaining = int(response_object.headers['X-RateLimit-Remaining'])
|
||||
self.ratelimit_limit = int(response_object.headers['X-RateLimit-Limit'])
|
||||
|
||||
try:
|
||||
ratelimit_reset_datetime = dateutil.parser.parse(response_object.headers['X-RateLimit-Reset'])
|
||||
self.ratelimit_reset = self.__datetime_to_epoch(ratelimit_reset_datetime)
|
||||
|
||||
# Adjust server time to local clock
|
||||
if 'Date' in response_object.headers:
|
||||
server_time_datetime = dateutil.parser.parse(response_object.headers['Date'])
|
||||
server_time = self.__datetime_to_epoch(server_time_datetime)
|
||||
server_time_diff = time.time() - server_time
|
||||
self.ratelimit_reset += server_time_diff
|
||||
self.ratelimit_lastcall = time.time()
|
||||
except Exception as e:
|
||||
raise MastodonRatelimitError("Rate limit time calculations failed: %s" % e)
|
||||
|
||||
# Handle response
|
||||
if self.debug_requests:
|
||||
print('Mastodon: Response received with code ' + str(response_object.status_code) + '.')
|
||||
|
@ -1048,16 +1073,29 @@ class Mastodon:
|
|||
response = response_object.json()
|
||||
except:
|
||||
raise MastodonAPIError('Endpoint not found.')
|
||||
|
||||
|
||||
if isinstance(response, dict) and 'error' in response:
|
||||
raise MastodonAPIError("Mastodon API returned error: " + str(response['error']))
|
||||
else:
|
||||
raise MastodonAPIError('Endpoint not found.')
|
||||
|
||||
|
||||
|
||||
|
||||
if response_object.status_code == 500:
|
||||
raise MastodonAPIError('General API problem.')
|
||||
|
||||
# Handle rate limiting
|
||||
if response_object.status_code == 429:
|
||||
if self.ratelimit_method == 'throw' or not do_ratelimiting:
|
||||
raise MastodonRatelimitError('Hit rate limit.')
|
||||
elif self.ratelimit_method in ('wait', 'pace'):
|
||||
to_next = self.ratelimit_reset - time.time()
|
||||
if to_next > 0:
|
||||
# As a precaution, never sleep longer than 5 minutes
|
||||
to_next = min(to_next, 5 * 60)
|
||||
time.sleep(to_next)
|
||||
request_complete = False
|
||||
continue
|
||||
|
||||
try:
|
||||
response = response_object.json(object_hook=self.__json_date_parse)
|
||||
except:
|
||||
|
@ -1065,11 +1103,11 @@ class Mastodon:
|
|||
"Could not parse response as JSON, response code was %s, "
|
||||
"bad json content was '%s'" % (response_object.status_code,
|
||||
response_object.content))
|
||||
|
||||
|
||||
# See if the returned dict is an error dict even though status is 200
|
||||
if isinstance(response, dict) and 'error' in response:
|
||||
raise MastodonAPIError("Mastodon API returned error: " + str(response['error']))
|
||||
|
||||
|
||||
# Parse link headers
|
||||
if isinstance(response, list) and \
|
||||
'Link' in response_object.headers and \
|
||||
|
@ -1108,36 +1146,6 @@ class Mastodon:
|
|||
del prev_params['max_id']
|
||||
response[0]['_pagination_prev'] = prev_params
|
||||
|
||||
# Handle rate limiting
|
||||
if 'X-RateLimit-Remaining' in response_object.headers and do_ratelimiting:
|
||||
self.ratelimit_remaining = int(response_object.headers['X-RateLimit-Remaining'])
|
||||
self.ratelimit_limit = int(response_object.headers['X-RateLimit-Limit'])
|
||||
|
||||
try:
|
||||
ratelimit_reset_datetime = dateutil.parser.parse(response_object.headers['X-RateLimit-Reset'])
|
||||
self.ratelimit_reset = self.__datetime_to_epoch(ratelimit_reset_datetime)
|
||||
|
||||
# Adjust server time to local clock
|
||||
if 'Date' in response_object.headers:
|
||||
server_time_datetime = dateutil.parser.parse(response_object.headers['Date'])
|
||||
server_time = self.__datetime_to_epoch(server_time_datetime)
|
||||
server_time_diff = time.time() - server_time
|
||||
self.ratelimit_reset += server_time_diff
|
||||
self.ratelimit_lastcall = time.time()
|
||||
except Exception as e:
|
||||
raise MastodonRatelimitError("Rate limit time calculations failed: %s" % e)
|
||||
|
||||
if "error" in response and response["error"] == "Throttled":
|
||||
if self.ratelimit_method == "throw":
|
||||
raise MastodonRatelimitError("Hit rate limit.")
|
||||
|
||||
if self.ratelimit_method == "wait" or self.ratelimit_method == "pace":
|
||||
to_next = self.ratelimit_reset - time.time()
|
||||
if to_next > 0:
|
||||
# As a precaution, never sleep longer than 5 minutes
|
||||
to_next = min(to_next, 5 * 60)
|
||||
time.sleep(to_next)
|
||||
request_complete = False
|
||||
|
||||
return response
|
||||
|
||||
|
|
Loading…
Referencia en una nova incidència