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
 """
-__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
-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
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)
-        
+
         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
-            :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,
-            allowed_param=['id', 'user_id', 'screen_name', 'cursor']
+            allowed_param=['id', 'user_id', 'screen_name', 'cursor', 'skip_status', 'include_user_entities']
         )
 
     @property
@@ -586,9 +586,39 @@ class API(object):
                            '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
-            :allowed_param:'include_entities', 'skip_status'
+            :allowed_param:'include_entities', 'skip_status', 'include_email'
         """
         try:
             return bind_api(
@@ -596,7 +626,7 @@ class API(object):
                 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:
@@ -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
-            :allowed_param:'screen_name', 'user_id', 'slug', 'lit_id',
+            :allowed_param:'screen_name', 'user_id', 'slug', 'list_id',
             'owner_id', 'owner_screen_name'
 
         """
@@ -1021,7 +1051,7 @@ class API(object):
             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
         )
@@ -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
-            :allowed_param:'screen_name', 'user_id', 'slug', 'lit_id',
+            :allowed_param:'screen_name', 'user_id', 'slug', 'list_id',
             'owner_id', 'owner_screen_name'
 
         """
@@ -1046,7 +1076,7 @@ class API(object):
             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
         )
index 2ac614647d2d9bba28c187ce97a825cf6588416b..ba5557083b26c41778ba83b335f7302213e213f4 100644 (file)
@@ -12,7 +12,7 @@ import requests
 
 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
 
@@ -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
-                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)
index 1c47a5a2c3724fb74c6a56157c990c41856f9b53..7827029a55236e7becbc4e445994934c645248c5 100644 (file)
@@ -6,7 +6,6 @@ from __future__ import print_function
 
 import six
 
-
 class TweepError(Exception):
     """Tweepy exception"""
 
@@ -17,3 +16,16 @@ class TweepError(Exception):
 
     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
-        self._buffer = u""
+        self._buffer = ''
         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))
-            self._buffer += self._stream.read(read_len).decode("ascii")
+            self._buffer += self._stream.read(read_len)
 
     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)
-            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]
@@ -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)
-            raise
+            raise exception
 
     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,
-               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:
@@ -423,6 +423,8 @@ class Stream(object):
             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)

Benjamin Mako Hill || Want to submit a patch?