Replace TweepError with HTTPException
authorHarmon <Harmon758@gmail.com>
Sun, 4 Apr 2021 13:34:03 +0000 (08:34 -0500)
committerHarmon <Harmon758@gmail.com>
Sun, 4 Apr 2021 13:43:57 +0000 (08:43 -0500)
Replace RateLimitError with TooManyRequests
Remove Parser.parse_error

tests/test_api.py
tests/test_rate_limit.py
tweepy/__init__.py
tweepy/api.py
tweepy/error.py [deleted file]
tweepy/errors.py
tweepy/parsers.py

index fbbb3da203180f7c242e3fd6565e223d3611cf3b..fab705c91ee016212d3e8174745d292bf93ee515 100644 (file)
@@ -15,20 +15,6 @@ tweet_text = 'testing 1000'
 """Unit tests"""
 
 
-class TweepyErrorTests(unittest.TestCase):
-
-    def testpickle(self):
-        """Verify exceptions can be pickled and unpickled."""
-        import pickle
-        from tweepy.error import TweepError
-
-        e = TweepError('no reason', {'status': 200})
-        e2 = pickle.loads(pickle.dumps(e))
-
-        self.assertEqual(e.reason, e2.reason)
-        self.assertEqual(e.response, e2.response)
-
-
 class TweepyAPITests(TweepyTestCase):
 
     #@tape.use_cassette('testfailure.json')
index f97336eaaa2ed189ccfe103cc54a93aa414de952..9d386d834085d38d2093d3e2f03cf857cf36a1f6 100644 (file)
@@ -3,7 +3,7 @@ import unittest
 
 from .config import create_auth
 from tweepy import API
-from tweepy.error import TweepError
+from tweepy.errors import HTTPException
 
 testratelimit = 'TEST_RATE_LIMIT' in os.environ
 
@@ -24,9 +24,9 @@ class TweepyRateLimitTests(unittest.TestCase):
         for user_id in test_user_ids:
             try:
                 self.api.user_timeline(user_id=user_id, count=1, include_rts=True)
-            except TweepError as e:
-                # continue if we're not autherized to access the user's timeline or she doesn't exist anymore
-                if e.response is not None and e.response.status in {401, 404}:
+            except HTTPException as e:
+                # continue if we're not autherized to access the user's timeline or user doesn't exist anymore
+                if e.response.status_code in (401, 404):
                     continue
                 raise e
 
index 557baebcbeaec00c82c587497b578c7601c9cef2..4eb0ad3128db3e5de71903f231a50f5c14051a6d 100644 (file)
@@ -13,8 +13,7 @@ from tweepy.api import API
 from tweepy.auth import AppAuthHandler, OAuthHandler
 from tweepy.cache import Cache, FileCache, MemoryCache
 from tweepy.cursor import Cursor
-from tweepy.error import RateLimitError, TweepError
-from tweepy.errors import TweepyException
+from tweepy.errors import HTTPException, TooManyRequests, TweepyException
 from tweepy.models import DirectMessage, Friendship, ModelFactory, SavedSearch, SearchResults, Status, User
 from tweepy.streaming import Stream
 
index 3c253975bcc40e7ead4665100f7e51508a0f1dab..f6f31861abfd3ccd1cca300b983adeb85c8e02a7 100644 (file)
@@ -12,8 +12,7 @@ from urllib.parse import urlencode
 
 import requests
 
-from tweepy.error import is_rate_limit_error_message, RateLimitError, TweepError
-from tweepy.errors import TweepyException
+from tweepy.errors import HTTPException, TooManyRequests, TweepyException
 from tweepy.models import Model
 from tweepy.parsers import ModelParser, Parser
 from tweepy.utils import list_to_csv
@@ -209,17 +208,10 @@ class API:
 
             # If an error was returned, throw an exception
             self.last_response = resp
+            if resp.status_code == 429:
+                raise TooManyRequests(resp)
             if resp.status_code and not 200 <= resp.status_code < 300:
-                try:
-                    error_msg, api_error_code = parser.parse_error(resp.text)
-                except Exception:
-                    error_msg = f"Twitter error response: status code = {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, api_code=api_error_code)
+                raise HTTPException(resp)
 
             # Parse the response payload
             return_cursors = return_cursors or 'cursor' in params or 'next' in params
diff --git a/tweepy/error.py b/tweepy/error.py
deleted file mode 100644 (file)
index 8dbc434..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-# Tweepy
-# Copyright 2009-2021 Joshua Roesslein
-# See LICENSE for details.
-
-
-class TweepError(Exception):
-    """Tweepy exception"""
-
-    def __init__(self, reason, response=None, api_code=None):
-        self.reason = str(reason)
-        self.response = response
-        self.api_code = api_code
-        super().__init__(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 len(message) > 0 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
-    # as TweepError for backwards compatibility reasons.
-    pass
index 89898662e8a13d106aabf224e64bbe8754413c60..e1571478352c35805975e1d5d754af03c0b5ee51 100644 (file)
@@ -2,7 +2,47 @@
 # Copyright 2009-2021 Joshua Roesslein
 # See LICENSE for details.
 
+import json
+
 
 class TweepyException(Exception):
     """Base exception for Tweepy"""
     pass
+
+
+class HTTPException(TweepyException):
+    """Exception raised when an HTTP request fails"""
+
+    def __init__(self, response):
+        self.response = response
+
+        self.api_errors = []
+        self.api_codes = []
+        self.api_messages = []
+
+        try:
+            response_json = response.json()
+        except json.JSONDecodeError:
+            super().__init__(f"{response.status_code} {response.reason}")
+        else:
+            error_text = []
+            for error in response_json.get("errors"):
+                self.api_errors.append(error)
+                # Use := when support for Python 3.7 is dropped
+                if "code" in error:
+                    self.api_codes.append(error["code"])
+                if "message" in error:
+                    self.api_messages.append(error["message"])
+                if "code" in error and "message" in error:
+                    error_text.append(f"{error['code']} - {error['message']}")
+                elif "message" in error:
+                    error_text.append(error["message"])
+            error_text = '\n'.join(error_text)
+            super().__init__(
+                f"{response.status_code} {response.reason}\n{error_text}"
+            )
+
+
+class TooManyRequests(HTTPException):
+    """Exception raised for a 429 HTTP status code"""
+    pass
index 5963afbbd863b8553c9327e86aa738e93ffb212b..a0ca907383ee3e999997cacb28553da0e93f6245 100644 (file)
@@ -18,14 +18,6 @@ class Parser:
         """
         raise NotImplementedError
 
-    def parse_error(self, payload):
-        """
-        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
-
 
 class RawParser(Parser):
 
@@ -35,9 +27,6 @@ class RawParser(Parser):
     def parse(self, payload, *args, **kwargs):
         return payload
 
-    def parse_error(self, payload):
-        return payload
-
 
 class JSONParser(Parser):
 
@@ -60,20 +49,6 @@ class JSONParser(Parser):
                     return json, json['next_cursor']
         return json
 
-    def parse_error(self, payload):
-        error_object = json_lib.loads(payload)
-
-        if 'error' in error_object:
-            reason = error_object['error']
-            api_code = error_object.get('code')
-        else:
-            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):