Commit 7f23b992 authored by Joshua Roesslein's avatar Joshua Roesslein
Browse files

Merge pull request #666 from tjphopkins/new_api_codes

Explicitly return api code when parsing error
parents 89d81972 7f322d76
{
"version": 1,
"interactions": [
{
"request": {
"method": "GET",
"uri": "https://api.twitter.com:443/1.1/direct_messages.json",
"headers": {
"Host": [
"api.twitter.com"
]
},
"body": null
},
"response": {
"status": {
"message": "Bad Request",
"code": 400
},
"headers": {
"x-response-time": [
"6"
],
"content-type": [
"application/json; charset=utf-8"
],
"server": [
"tsa_b"
],
"strict-transport-security": [
"max-age=631138519"
],
"set-cookie": [
"guest_id=v1%3A144655290597218733; Domain=.twitter.com; Path=/; Expires=Thu, 02-Nov-2017 12:15:05 UTC"
],
"date": [
"Tue, 03 Nov 2015 12:15:05 GMT"
],
"x-connection-hash": [
"bbad9c628533f023920a78c282b82a2e"
],
"content-length": [
"62"
]
},
"body": {
"string": "{\"errors\":[{\"code\":215,\"message\":\"Bad Authentication data.\"}]}"
}
}
},
{
"request": {
"method": "GET",
"uri": "https://api.twitter.com:443/1.1/direct_messages.json",
"headers": {
"Cookie": [
"guest_id=v1%3A144655290597218733"
],
"Host": [
"api.twitter.com"
]
},
"body": null
},
"response": {
"status": {
"message": "Bad Request",
"code": 400
},
"headers": {
"x-response-time": [
"5"
],
"content-type": [
"application/json; charset=utf-8"
],
"server": [
"tsa_b"
],
"strict-transport-security": [
"max-age=631138519"
],
"date": [
"Tue, 03 Nov 2015 12:15:11 GMT"
],
"x-connection-hash": [
"bbad9c628533f023920a78c282b82a2e"
],
"content-length": [
"62"
]
},
"body": {
"string": "{\"errors\":[{\"code\":215,\"message\":\"Bad Authentication data.\"}]}"
}
}
},
{
"request": {
"method": "GET",
"uri": "https://api.twitter.com:443/1.1/direct_messages.json",
"headers": {
"Cookie": [
"guest_id=v1%3A144655290597218733"
],
"Host": [
"api.twitter.com"
]
},
"body": null
},
"response": {
"status": {
"message": "Bad Request",
"code": 400
},
"headers": {
"x-response-time": [
"3"
],
"content-type": [
"application/json; charset=utf-8"
],
"server": [
"tsa_b"
],
"strict-transport-security": [
"max-age=631138519"
],
"date": [
"Tue, 03 Nov 2015 12:15:16 GMT"
],
"x-connection-hash": [
"bbad9c628533f023920a78c282b82a2e"
],
"content-length": [
"62"
]
},
"body": {
"string": "{\"errors\":[{\"code\":215,\"message\":\"Bad Authentication data.\"}]}"
}
}
},
{
"request": {
"method": "GET",
"uri": "https://api.twitter.com:443/1.1/direct_messages.json",
"headers": {
"Host": [
"api.twitter.com"
]
},
"body": null
},
"response": {
"status": {
"message": "Bad Request",
"code": 400
},
"headers": {
"x-response-time": [
"4"
],
"content-type": [
"application/json; charset=utf-8"
],
"server": [
"tsa_b"
],
"strict-transport-security": [
"max-age=631138519"
],
"set-cookie": [
"guest_id=v1%3A144655293331152269; Domain=.twitter.com; Path=/; Expires=Thu, 02-Nov-2017 12:15:33 UTC"
],
"date": [
"Tue, 03 Nov 2015 12:15:33 GMT"
],
"x-connection-hash": [
"f3384743aac99980194e77031a6b9d66"
],
"content-length": [
"62"
]
},
"body": {
"string": "{\"errors\":[{\"code\":215,\"message\":\"Bad Authentication data.\"}]}"
}
}
},
{
"request": {
"method": "GET",
"uri": "https://api.twitter.com:443/1.1/direct_messages.json",
"headers": {
"Cookie": [
"guest_id=v1%3A144655293331152269"
],
"Host": [
"api.twitter.com"
]
},
"body": null
},
"response": {
"status": {
"message": "Bad Request",
"code": 400
},
"headers": {
"x-response-time": [
"4"
],
"content-type": [
"application/json; charset=utf-8"
],
"server": [
"tsa_b"
],
"strict-transport-security": [
"max-age=631138519"
],
"date": [
"Tue, 03 Nov 2015 12:15:38 GMT"
],
"x-connection-hash": [
"f3384743aac99980194e77031a6b9d66"
],
"content-length": [
"62"
]
},
"body": {
"string": "{\"errors\":[{\"code\":215,\"message\":\"Bad Authentication data.\"}]}"
}
}
},
{
"request": {
"method": "GET",
"uri": "https://api.twitter.com:443/1.1/direct_messages.json",
"headers": {
"Cookie": [
"guest_id=v1%3A144655293331152269"
],
"Host": [
"api.twitter.com"
]
},
"body": null
},
"response": {
"status": {
"message": "Bad Request",
"code": 400
},
"headers": {
"x-response-time": [
"6"
],
"content-type": [
"application/json; charset=utf-8"
],
"server": [
"tsa_b"
],
"strict-transport-security": [
"max-age=631138519"
],
"date": [
"Tue, 03 Nov 2015 12:15:43 GMT"
],
"x-connection-hash": [
"f3384743aac99980194e77031a6b9d66"
],
"content-length": [
"62"
]
},
"body": {
"string": "{\"errors\":[{\"code\":215,\"message\":\"Bad Authentication data.\"}]}"
}
}
}
]
}
......@@ -3,6 +3,7 @@ import random
import shutil
from time import sleep
import os
from ast import literal_eval
from nose import SkipTest
......@@ -32,6 +33,18 @@ class TweepyErrorTests(unittest.TestCase):
class TweepyAPITests(TweepyTestCase):
@tape.use_cassette('testfailure.json')
def testapierror(self):
from tweepy.error import TweepError
with self.assertRaises(TweepError) as cm:
self.api.direct_messages()
reason, = literal_eval(cm.exception.reason)
self.assertEqual(reason['message'], 'Bad Authentication data.')
self.assertEqual(reason['code'], 215)
self.assertEqual(cm.exception.api_code, 215)
# TODO: Actually have some sort of better assertion
@tape.use_cassette('testgetoembed.json')
def testgetoembed(self):
......
......@@ -217,14 +217,16 @@ def bind_api(**config):
self.api.last_response = resp
if resp.status_code and not 200 <= resp.status_code < 300:
try:
error_msg = self.parser.parse_error(resp.text)
error_msg, api_error_code = \
self.parser.parse_error(resp.text)
except Exception:
error_msg = "Twitter error response: status code = %s" % resp.status_code
api_error_code = None
if is_rate_limit_error_message(error_msg):
raise RateLimitError(error_msg, resp)
else:
raise TweepError(error_msg, resp)
raise TweepError(error_msg, resp, api_code=api_error_code)
# Parse the response payload
result = self.parser.parse(self, resp.text)
......
......@@ -9,14 +9,16 @@ import six
class TweepError(Exception):
"""Tweepy exception"""
def __init__(self, reason, response=None):
def __init__(self, reason, response=None, api_code=None):
self.reason = six.text_type(reason)
self.response = response
self.api_code = api_code
Exception.__init__(self, reason)
def __str__(self):
return self.reason
def is_rate_limit_error_message(message):
"""Check if the supplied error message belongs to a rate limit error."""
return isinstance(message, list) \
......@@ -24,6 +26,7 @@ def is_rate_limit_error_message(message):
and 'code' in message[0] \
and message[0]['code'] == 88
class RateLimitError(TweepError):
"""Exception for Tweepy hitting the rate limit."""
# RateLimitError has the exact same properties and inner workings
......
......@@ -21,9 +21,9 @@ class Parser(object):
def parse_error(self, payload):
"""
Parse the error message from payload.
If unable to parse the message, throw an exception
and default error message will be used.
Parse the error message and api error code from payload.
Return them as an (error_msg, error_code) tuple. If unable to parse the
message, throw an exception and default error message will be used.
"""
raise NotImplementedError
......@@ -63,11 +63,18 @@ class JSONParser(Parser):
return json
def parse_error(self, payload):
error = self.json_lib.loads(payload)
if 'error' in error:
return error['error']
error_object = self.json_lib.loads(payload)
if 'error' in error_object:
reason = error_object['error']
api_code = error_object.get('code')
else:
return error['errors']
reason = error_object['errors']
api_code = [error.get('code') for error in
reason if error.get('code')]
api_code = api_code[0] if len(api_code) == 1 else api_code
return reason, api_code
class ModelParser(JSONParser):
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment