--- /dev/null
+{
+ "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.\"}]}"
+ }
+ }
+ }
+ ]
+}
import shutil
from time import sleep
import os
+from ast import literal_eval
from nose import SkipTest
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):
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)
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) \
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
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
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):