From: Benjamin Mako Hill Date: Fri, 23 Oct 2015 02:40:37 +0000 (-0700) Subject: new version of tweepy (3.4.0) X-Git-Url: https://projects.mako.cc/source/twitter-api-cdsw/commitdiff_plain/a5afcef4759dfb5886e60791cf9b0b8fc7d67b8f?ds=sidebyside new version of tweepy (3.4.0) --- diff --git a/tweepy/__init__.py b/tweepy/__init__.py index b691e34..10f157a 100644 --- a/tweepy/__init__.py +++ b/tweepy/__init__.py @@ -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 diff --git a/tweepy/api.py b/tweepy/api.py index 4744275..45c35ee 100644 --- a/tweepy/api.py +++ b/tweepy/api.py @@ -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 ) diff --git a/tweepy/binder.py b/tweepy/binder.py index 2ac6146..ba55570 100644 --- a/tweepy/binder.py +++ b/tweepy/binder.py @@ -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) diff --git a/tweepy/error.py b/tweepy/error.py index 1c47a5a..7827029 100644 --- a/tweepy/error.py +++ b/tweepy/error.py @@ -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 diff --git a/tweepy/streaming.py b/tweepy/streaming.py index 9b246bd..c637264 100644 --- a/tweepy/streaming.py +++ b/tweepy/streaming.py @@ -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)