Merge pull request #118 from codl/subclass-api-errors

Subclass api errors
This commit is contained in:
Lorenz Diener 2018-01-29 11:18:37 +01:00 cometido por GitHub
commit 42b1d8fa58
No se encontró ninguna clave conocida en la base de datos para esta firma
ID de clave GPG: 4AEE18F83AFDEB23
S'han modificat 4 arxius amb 137 adicions i 33 eliminacions

Veure arxiu

@ -1550,33 +1550,45 @@ class Mastodon:
print('response headers: ' + str(response_object.headers))
print('Response text content: ' + str(response_object.text))
if response_object.status_code == 404:
if not response_object.ok:
try:
response = response_object.json()
except:
raise MastodonAPIError('Endpoint not found.')
response = response_object.json(object_hook=self.__json_hooks)
if not isinstance(response, dict) or 'error' not in response:
error_msg = None
error_msg = response['error']
except ValueError:
error_msg = None
if isinstance(response, dict) and 'error' in response:
raise MastodonAPIError("Mastodon API returned error: " + str(response['error']))
# 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
if response_object.status_code == 404:
ex_type = MastodonNotFoundError
if not error_msg:
error_msg = 'Endpoint not found.'
# this is for compatibility with older versions
# which raised MastodonAPIError('Endpoint not found.')
# on any 404
elif response_object.status_code == 401:
ex_type = MastodonUnauthorizedError
else:
raise MastodonAPIError('Endpoint not found.')
ex_type = MastodonAPIError
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
raise ex_type(
'Mastodon API returned error',
response_object.status_code,
response_object.reason,
error_msg)
try:
response = response_object.json(object_hook=self.__json_hooks)
@ -1586,12 +1598,6 @@ class Mastodon:
"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:
if not isinstance(response['error'], six.string_types):
response['error'] = six.text_type(response['error'])
raise MastodonAPIError("Mastodon API returned error: " + response['error'])
# Parse link headers
if isinstance(response, list) and \
'Link' in response_object.headers and \
@ -1801,6 +1807,16 @@ class MastodonAPIError(MastodonError):
"""Raised when the mastodon API generates a response that cannot be handled"""
pass
class MastodonNotFoundError(MastodonAPIError):
"""Raised when the mastodon API returns a 404 Not Found error"""
pass
class MastodonUnauthorizedError(MastodonAPIError):
"""Raised when the mastodon API returns a 401 Unauthorized error
This happens when an OAuth token is invalid or has been revoked."""
pass
class MastodonRatelimitError(MastodonError):
"""Raised when rate limiting is set to manual mode and the rate limit is exceeded"""

Veure arxiu

@ -0,0 +1,82 @@
interactions:
- request:
body: visibility=&status=Toot%21
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Authorization: [Bearer __MASTODON_PY_TEST_ACCESS_TOKEN]
Connection: [keep-alive]
Content-Length: ['26']
Content-Type: [application/x-www-form-urlencoded]
User-Agent: [python-requests/2.18.4]
method: POST
uri: http://localhost:3000/api/v1/statuses
response:
body: {string: '{"id":"99285482671609362","created_at":"2018-01-03T10:43:57.160Z","in_reply_to_id":null,"in_reply_to_account_id":null,"sensitive":false,"spoiler_text":"","visibility":"private","language":"ja","uri":"http://localhost:3000/users/mastodonpy_test/statuses/99285482671609362","content":"\u003cp\u003eToot!\u003c/p\u003e","url":"http://localhost:3000/@mastodonpy_test/99285482671609362","reblogs_count":0,"favourites_count":0,"favourited":false,"reblogged":false,"muted":false,"reblog":null,"application":{"name":"Mastodon.py
test suite","website":null},"account":{"id":"1234567890123456","username":"mastodonpy_test","acct":"mastodonpy_test","display_name":"","locked":true,"created_at":"2018-01-03T11:24:32.957Z","note":"\u003cp\u003e\u003c/p\u003e","url":"http://localhost:3000/@mastodonpy_test","avatar":"http://localhost:3000/avatars/original/missing.png","avatar_static":"http://localhost:3000/avatars/original/missing.png","header":"http://localhost:3000/headers/original/missing.png","header_static":"http://localhost:3000/headers/original/missing.png","followers_count":0,"following_count":0,"statuses_count":1},"media_attachments":[],"mentions":[],"tags":[],"emojis":[]}'}
headers:
Cache-Control: ['max-age=0, private, must-revalidate']
Content-Type: [application/json; charset=utf-8]
ETag: [W/"d9b57bb0592371b00e98fbc0f44a8fc9"]
Transfer-Encoding: [chunked]
Vary: ['Accept-Encoding, Origin']
X-Content-Type-Options: [nosniff]
X-Frame-Options: [SAMEORIGIN]
X-Request-Id: [d7a9df07-1a3c-4784-adc5-b67bd6347614]
X-Runtime: ['0.301984']
X-XSS-Protection: [1; mode=block]
content-length: ['1175']
status: {code: 200, message: OK}
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
User-Agent: [python-requests/2.18.4]
method: GET
uri: http://localhost:3000/api/v1/timelines/home
response:
body: {string: '{"error":"The access token is invalid"}'}
headers:
Cache-Control: [no-store]
Content-Type: [application/json; charset=utf-8]
Pragma: [no-cache]
Transfer-Encoding: [chunked]
Vary: ['Accept-Encoding, Origin']
WWW-Authenticate: ['Bearer realm="Doorkeeper", error="invalid_token", error_description="The
access token is invalid"']
X-Content-Type-Options: [nosniff]
X-Frame-Options: [SAMEORIGIN]
X-Request-Id: [dc45d4f4-c203-4b28-ad27-f0db32912a16]
X-Runtime: ['0.010224']
X-XSS-Protection: [1; mode=block]
content-length: ['39']
status: {code: 401, message: Unauthorized}
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Authorization: [Bearer __MASTODON_PY_TEST_ACCESS_TOKEN]
Connection: [keep-alive]
Content-Length: ['0']
User-Agent: [python-requests/2.18.4]
method: DELETE
uri: http://localhost:3000/api/v1/statuses/99285482671609362
response:
body: {string: '{}'}
headers:
Cache-Control: ['max-age=0, private, must-revalidate']
Content-Type: [application/json; charset=utf-8]
ETag: [W/"8ca371aea536ee2c56c8d13b43824703"]
Transfer-Encoding: [chunked]
Vary: ['Accept-Encoding, Origin']
X-Content-Type-Options: [nosniff]
X-Frame-Options: [SAMEORIGIN]
X-Request-Id: [ddbd4335-1aeb-42af-8dea-fa78a787609f]
X-Runtime: ['0.017701']
X-XSS-Protection: [1; mode=block]
content-length: ['2']
status: {code: 200, message: OK}
version: 1

Veure arxiu

@ -1,6 +1,5 @@
import pytest
from mastodon.Mastodon import MastodonAPIError
from time import sleep
from mastodon.Mastodon import MastodonAPIError, MastodonNotFoundError
@pytest.mark.vcr()
def test_status(status, api):
@ -14,7 +13,7 @@ def test_status_empty(api):
@pytest.mark.vcr()
def test_status_missing(api):
with pytest.raises(MastodonAPIError):
with pytest.raises(MastodonNotFoundError):
api.status(0)
@pytest.mark.skip(reason="Doesn't look like mastodon will make a card for an url that doesn't have a TLD, and relying on some external website being reachable to make a card of is messy :/")

Veure arxiu

@ -1,5 +1,7 @@
import pytest
from mastodon.Mastodon import MastodonAPIError, MastodonIllegalArgumentError
from mastodon.Mastodon import MastodonAPIError,\
MastodonIllegalArgumentError,\
MastodonUnauthorizedError
@pytest.mark.vcr()
def test_public_tl_anonymous(api_anonymous, status):
@ -17,6 +19,11 @@ def test_public_tl(api, status):
assert status['id'] in map(lambda st: st['id'], public)
assert status['id'] in map(lambda st: st['id'], local)
@pytest.mark.vcr()
def test_unauthed_home_tl_throws(api_anonymous, status):
with pytest.raises(MastodonUnauthorizedError):
api_anonymous.timeline_home()
@pytest.mark.vcr()
def test_home_tl(api, status):
tl = api.timeline_home()