new version of tweepy (3.4.0)
authorBenjamin Mako Hill <mako@atdot.cc>
Fri, 23 Oct 2015 02:40:37 +0000 (19:40 -0700)
committerBenjamin Mako Hill <mako@atdot.cc>
Fri, 23 Oct 2015 02:40:37 +0000 (19:40 -0700)
tweepy/__init__.py
tweepy/api.py
tweepy/binder.py
tweepy/error.py
tweepy/streaming.py

index b691e3491272a128992e8e8a7198016f5bb0ae24..10f157a10e01c74fbbc8102e536e748a48982f1d 100644 (file)
@@ -5,12 +5,12 @@
 """
 Tweepy Twitter API library
 """
 """
 Tweepy Twitter API library
 """
-__version__ = '3.3.0'
+__version__ = '3.4.0'
 __author__ = 'Joshua Roesslein'
 __license__ = 'MIT'
 
 from tweepy.models import Status, User, DirectMessage, Friendship, SavedSearch, SearchResults, ModelFactory, Category
 __author__ = 'Joshua Roesslein'
 __license__ = 'MIT'
 
 from tweepy.models import Status, User, DirectMessage, Friendship, SavedSearch, SearchResults, ModelFactory, Category
-from tweepy.error import TweepError
+from tweepy.error import TweepError, RateLimitError
 from tweepy.api import API
 from tweepy.cache import Cache, MemoryCache, FileCache
 from tweepy.auth import OAuthHandler, AppAuthHandler
 from tweepy.api import API
 from tweepy.cache import Cache, MemoryCache, FileCache
 from tweepy.auth import OAuthHandler, AppAuthHandler
index 4744275829945ed6e0e52f1befe5b3d091d4cc2a..45c35ee9c04f71bcdb64b9e57c0937066898098d 100644 (file)
@@ -182,7 +182,7 @@ class API(object):
         post_data = {}
         if media_ids is not None:
             post_data["media_ids"] = list_to_csv(media_ids)
         post_data = {}
         if media_ids is not None:
             post_data["media_ids"] = list_to_csv(media_ids)
-        
+
         return bind_api(
             api=self,
             path='/statuses/update.json',
         return bind_api(
             api=self,
             path='/statuses/update.json',
@@ -528,13 +528,13 @@ class API(object):
     @property
     def friends(self):
         """ :reference: https://dev.twitter.com/rest/reference/get/friends/list
     @property
     def friends(self):
         """ :reference: https://dev.twitter.com/rest/reference/get/friends/list
-            :allowed_param:'id', 'user_id', 'screen_name', 'cursor'
+            :allowed_param:'id', 'user_id', 'screen_name', 'cursor', 'skip_status', 'include_user_entities'
         """
         return bind_api(
             api=self,
             path='/friends/list.json',
             payload_type='user', payload_list=True,
         """
         return bind_api(
             api=self,
             path='/friends/list.json',
             payload_type='user', payload_list=True,
-            allowed_param=['id', 'user_id', 'screen_name', 'cursor']
+            allowed_param=['id', 'user_id', 'screen_name', 'cursor', 'skip_status', 'include_user_entities']
         )
 
     @property
         )
 
     @property
@@ -586,9 +586,39 @@ class API(object):
                            'skip_status', 'include_user_entities']
         )
 
                            'skip_status', 'include_user_entities']
         )
 
+    @property
+    def get_settings(self):
+        """ :reference: https://dev.twitter.com/rest/reference/get/account/settings
+        """
+        return bind_api(
+            api=self,
+            path='/account/settings.json',
+            payload_type='json',
+            use_cache=False
+        )
+
+    @property
+    def set_settings(self):
+        """ :reference: https://dev.twitter.com/rest/reference/post/account/settings
+            :allowed_param:'sleep_time_enabled', 'start_sleep_time',
+            'end_sleep_time', 'time_zone', 'trend_location_woeid',
+            'allow_contributor_request', 'lang'
+        """
+        return bind_api(
+            api=self,
+            path='/account/settings.json',
+            method='POST',
+            payload_type='json',
+            allowed_param=['sleep_time_enabled', 'start_sleep_time',
+                           'end_sleep_time', 'time_zone',
+                           'trend_location_woeid', 'allow_contributor_request',
+                           'lang'],
+            use_cache=False
+        )
+
     def verify_credentials(self, **kargs):
         """ :reference: https://dev.twitter.com/rest/reference/get/account/verify_credentials
     def verify_credentials(self, **kargs):
         """ :reference: https://dev.twitter.com/rest/reference/get/account/verify_credentials
-            :allowed_param:'include_entities', 'skip_status'
+            :allowed_param:'include_entities', 'skip_status', 'include_email'
         """
         try:
             return bind_api(
         """
         try:
             return bind_api(
@@ -596,7 +626,7 @@ class API(object):
                 path='/account/verify_credentials.json',
                 payload_type='user',
                 require_auth=True,
                 path='/account/verify_credentials.json',
                 payload_type='user',
                 require_auth=True,
-                allowed_param=['include_entities', 'skip_status'],
+                allowed_param=['include_entities', 'skip_status', 'include_email'],
             )(**kargs)
         except TweepError as e:
             if e.response and e.response.status == 401:
             )(**kargs)
         except TweepError as e:
             if e.response and e.response.status == 401:
@@ -1012,7 +1042,7 @@ class API(object):
     @property
     def _add_list_members(self):
         """ :reference: https://dev.twitter.com/docs/api/1.1/post/lists/members/create_all
     @property
     def _add_list_members(self):
         """ :reference: https://dev.twitter.com/docs/api/1.1/post/lists/members/create_all
-            :allowed_param:'screen_name', 'user_id', 'slug', 'lit_id',
+            :allowed_param:'screen_name', 'user_id', 'slug', 'list_id',
             'owner_id', 'owner_screen_name'
 
         """
             'owner_id', 'owner_screen_name'
 
         """
@@ -1021,7 +1051,7 @@ class API(object):
             path='/lists/members/create_all.json',
             method='POST',
             payload_type='list',
             path='/lists/members/create_all.json',
             method='POST',
             payload_type='list',
-            allowed_param=['screen_name', 'user_id', 'slug', 'lit_id',
+            allowed_param=['screen_name', 'user_id', 'slug', 'list_id',
                            'owner_id', 'owner_screen_name'],
             require_auth=True
         )
                            'owner_id', 'owner_screen_name'],
             require_auth=True
         )
@@ -1037,7 +1067,7 @@ class API(object):
     @property
     def _remove_list_members(self):
         """ :reference: https://dev.twitter.com/docs/api/1.1/post/lists/members/destroy_all
     @property
     def _remove_list_members(self):
         """ :reference: https://dev.twitter.com/docs/api/1.1/post/lists/members/destroy_all
-            :allowed_param:'screen_name', 'user_id', 'slug', 'lit_id',
+            :allowed_param:'screen_name', 'user_id', 'slug', 'list_id',
             'owner_id', 'owner_screen_name'
 
         """
             'owner_id', 'owner_screen_name'
 
         """
@@ -1046,7 +1076,7 @@ class API(object):
             path='/lists/members/destroy_all.json',
             method='POST',
             payload_type='list',
             path='/lists/members/destroy_all.json',
             method='POST',
             payload_type='list',
-            allowed_param=['screen_name', 'user_id', 'slug', 'lit_id',
+            allowed_param=['screen_name', 'user_id', 'slug', 'list_id',
                            'owner_id', 'owner_screen_name'],
             require_auth=True
         )
                            'owner_id', 'owner_screen_name'],
             require_auth=True
         )
index 2ac614647d2d9bba28c187ce97a825cf6588416b..ba5557083b26c41778ba83b335f7302213e213f4 100644 (file)
@@ -12,7 +12,7 @@ import requests
 
 import logging
 
 
 import logging
 
-from tweepy.error import TweepError
+from tweepy.error import TweepError, RateLimitError, is_rate_limit_error_message
 from tweepy.utils import convert_to_utf8_str
 from tweepy.models import Model
 
 from tweepy.utils import convert_to_utf8_str
 from tweepy.models import Model
 
@@ -220,7 +220,11 @@ def bind_api(**config):
                     error_msg = self.parser.parse_error(resp.text)
                 except Exception:
                     error_msg = "Twitter error response: status code = %s" % resp.status_code
                     error_msg = self.parser.parse_error(resp.text)
                 except Exception:
                     error_msg = "Twitter error response: status code = %s" % resp.status_code
-                raise TweepError(error_msg, resp)
+
+                if is_rate_limit_error_message(error_msg):
+                    raise RateLimitError(error_msg, resp)
+                else:
+                    raise TweepError(error_msg, resp)
 
             # Parse the response payload
             result = self.parser.parse(self, resp.text)
 
             # Parse the response payload
             result = self.parser.parse(self, resp.text)
index 1c47a5a2c3724fb74c6a56157c990c41856f9b53..7827029a55236e7becbc4e445994934c645248c5 100644 (file)
@@ -6,7 +6,6 @@ from __future__ import print_function
 
 import six
 
 
 import six
 
-
 class TweepError(Exception):
     """Tweepy exception"""
 
 class TweepError(Exception):
     """Tweepy exception"""
 
@@ -17,3 +16,16 @@ class TweepError(Exception):
 
     def __str__(self):
         return 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 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 9b246bda820c5f2e5b31abcd3bf8b6570a7b2f93..c6372646c86387ccbf228aec3b0c5f7061fbd28c 100644 (file)
@@ -150,7 +150,7 @@ class ReadBuffer(object):
 
     def __init__(self, stream, chunk_size):
         self._stream = stream
 
     def __init__(self, stream, chunk_size):
         self._stream = stream
-        self._buffer = u""
+        self._buffer = ''
         self._chunk_size = chunk_size
 
     def read_len(self, length):
         self._chunk_size = chunk_size
 
     def read_len(self, length):
@@ -158,7 +158,7 @@ class ReadBuffer(object):
             if len(self._buffer) >= length:
                 return self._pop(length)
             read_len = max(self._chunk_size, length - len(self._buffer))
             if len(self._buffer) >= length:
                 return self._pop(length)
             read_len = max(self._chunk_size, length - len(self._buffer))
-            self._buffer += self._stream.read(read_len).decode("ascii")
+            self._buffer += self._stream.read(read_len)
 
     def read_line(self, sep='\n'):
         start = 0
 
     def read_line(self, sep='\n'):
         start = 0
@@ -168,7 +168,7 @@ class ReadBuffer(object):
                 return self._pop(loc + len(sep))
             else:
                 start = len(self._buffer)
                 return self._pop(loc + len(sep))
             else:
                 start = len(self._buffer)
-            self._buffer += self._stream.read(self._chunk_size).decode("ascii")
+            self._buffer += self._stream.read(self._chunk_size)
 
     def _pop(self, length):
         r = self._buffer[:length]
 
     def _pop(self, length):
         r = self._buffer[:length]
@@ -283,7 +283,7 @@ class Stream(object):
         if exception:
             # call a handler first so that the exception can be logged.
             self.listener.on_exception(exception)
         if exception:
             # call a handler first so that the exception can be logged.
             self.listener.on_exception(exception)
-            raise
+            raise exception
 
     def _data(self, data):
         if self.listener.on_data(data) is False:
 
     def _data(self, data):
         if self.listener.on_data(data) is False:
@@ -404,7 +404,7 @@ class Stream(object):
         self._start(async)
 
     def filter(self, follow=None, track=None, async=False, locations=None,
         self._start(async)
 
     def filter(self, follow=None, track=None, async=False, locations=None,
-               stall_warnings=False, languages=None, encoding='utf8'):
+               stall_warnings=False, languages=None, encoding='utf8', filter_level=None):
         self.body = {}
         self.session.headers['Content-type'] = "application/x-www-form-urlencoded"
         if self.running:
         self.body = {}
         self.session.headers['Content-type'] = "application/x-www-form-urlencoded"
         if self.running:
@@ -423,6 +423,8 @@ class Stream(object):
             self.body['stall_warnings'] = stall_warnings
         if languages:
             self.body['language'] = u','.join(map(str, languages))
             self.body['stall_warnings'] = stall_warnings
         if languages:
             self.body['language'] = u','.join(map(str, languages))
+        if filter_level:
+            self.body['filter_level'] = unicode(filter_level, encoding)
         self.session.params = {'delimited': 'length'}
         self.host = 'stream.twitter.com'
         self._start(async)
         self.session.params = {'delimited': 'length'}
         self.host = 'stream.twitter.com'
         self._start(async)

Benjamin Mako Hill || Want to submit a patch?