From 894e94a4cd666bb32e3b0144a4935b0624e8e020 Mon Sep 17 00:00:00 2001 From: Mike Helmick Date: Thu, 23 May 2013 23:32:32 -0400 Subject: [PATCH 1/7] 2.10.1 - More test coverage! - Fix ``search_gen`` - Fixed ``get_lastfunction_header`` to actually do what its docstring says, returns ``None`` if header is not found - Updated some internal API code, ``__init__`` didn't need to have ``self.auth`` and ``self.headers`` because they were never used anywhere else but the ``__init__`` --- HISTORY.rst | 7 ++++ setup.py | 4 ++- test_twython.py | 78 ++++++++++++++++++++++++++++++++++++++++++--- twython/__init__.py | 2 +- twython/twython.py | 35 ++++++++++---------- 5 files changed, 102 insertions(+), 24 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index df10007..4e3706d 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -3,6 +3,13 @@ History ------- +2.10.1 (2013-05-xx) +++++++++++++++++++ +- More test coverage! +- Fix ``search_gen`` +- Fixed ``get_lastfunction_header`` to actually do what its docstring says, returns ``None`` if header is not found +- Updated some internal API code, ``__init__`` didn't need to have ``self.auth`` and ``self.headers`` because they were never used anywhere else but the ``__init__`` + 2.10.0 (2013-05-21) ++++++++++++++++++ - Added ``get_retweeters_ids`` method diff --git a/setup.py b/setup.py index d24a3de..045d9e0 100755 --- a/setup.py +++ b/setup.py @@ -1,10 +1,12 @@ +#!/usr/bin/env python + import os import sys from setuptools import setup __author__ = 'Ryan McGrath ' -__version__ = '2.10.0' +__version__ = '2.10.1' packages = [ 'twython', diff --git a/test_twython.py b/test_twython.py index 759dcb4..e5e7fa1 100644 --- a/test_twython.py +++ b/test_twython.py @@ -24,6 +24,7 @@ test_list_id = os.environ.get('TEST_LIST_ID', '574') # 574 is @twitter/team class TwythonAuthTestCase(unittest.TestCase): def setUp(self): self.api = Twython(app_key, app_secret) + self.bad_api = Twython('BAD_APP_KEY', 'BAD_APP_SECRET') def test_get_authentication_tokens(self): '''Test getting authentication tokens works''' @@ -31,11 +32,76 @@ class TwythonAuthTestCase(unittest.TestCase): force_login=True, screen_name=screen_name) + def test_get_authentication_tokens_bad_tokens(self): + '''Test getting authentication tokens with bad tokens + raises TwythonAuthError''' + self.assertRaises(TwythonAuthError, self.api.get_authentication_tokens, + callback_url='http://google.com/') + + def test_get_authorized_tokens_bad_tokens(self): + '''Test getting final tokens fails with wrong tokens''' + self.assertRaises(TwythonError, self.api.get_authorized_tokens, + 'BAD_OAUTH_VERIFIER') + class TwythonAPITestCase(unittest.TestCase): def setUp(self): self.api = Twython(app_key, app_secret, - oauth_token, oauth_token_secret) + oauth_token, oauth_token_secret, + headers={'User-Agent': '__twython__ Test'}) + + def test_construct_api_url(self): + '''Test constructing a Twitter API url works as we expect''' + url = 'https://api.twitter.com/1.1/search/tweets.json' + constructed_url = self.api.construct_api_url(url, {'q': '#twitter'}) + self.assertEqual(constructed_url, 'https://api.twitter.com/1.1/search/tweets.json?q=%23twitter') + + def test_shorten_url(self): + '''Test shortening a url works''' + self.api.shorten_url('http://google.com') + + def test_shorten_url_no_shortner(self): + '''Test shortening a url with no shortener provided raises TwythonError''' + self.assertRaises(TwythonError, self.api.shorten_url, + 'http://google.com', '') + + def test_get(self): + '''Test Twython generic GET request works''' + self.api.get('account/verify_credentials') + + def test_post(self): + '''Test Twython generic POST request works, with a full url and + with just an endpoint''' + update_url = 'https://api.twitter.com/1.1/statuses/update.json' + status = self.api.post(update_url, params={'status': 'I love Twython!'}) + self.api.post('statuses/destroy/%s' % status['id_str']) + + def test_get_lastfunction_header(self): + '''Test getting last specific header of the last API call works''' + self.api.get('statuses/home_timeline') + self.api.get_lastfunction_header('x-rate-limit-remaining') + + def test_get_lastfunction_header_not_present(self): + '''Test getting specific header that does not exist from the last call returns None''' + self.api.get('statuses/home_timeline') + header = self.api.get_lastfunction_header('does-not-exist') + self.assertEqual(header, None) + + def test_get_lastfunction_header_no_last_api_call(self): + '''Test attempting to get a header when no API call was made raises a TwythonError''' + self.assertRaises(TwythonError, self.api.get_lastfunction_header, + 'no-api-call-was-made') + + def test_search_gen(self): + '''Test looping through the generator results works, at least once that is''' + search = self.api.search_gen('python') + for result in search: + if result: + break + + def test_encode(self): + '''Test encoding UTF-8 works''' + self.api.encode('Twython is awesome!') # Timelines def test_get_mentions_timeline(self): @@ -84,9 +150,11 @@ class TwythonAPITestCase(unittest.TestCase): def test_retweet_twice(self): '''Test that trying to retweet a tweet twice raises a TwythonError''' - retweet = self.api.retweet(id='99530515043983360') - self.assertRaises(TwythonError, self.api.retweet, - id='99530515043983360') + tweets = self.api.search(q='twitter').get('statuses') + if tweets: + retweet = self.api.retweet(id=tweets[0]['id_str']) + self.assertRaises(TwythonError, self.api.retweet, + id=tweets[0]['id_str']) # Then clean up self.api.destroy_status(id=retweet['id_str']) @@ -132,7 +200,7 @@ class TwythonAPITestCase(unittest.TestCase): def test_get_user_ids_of_blocked_retweets(self): '''Test that collection of user_ids that the authenticated user does not want to receive retweets from succeeds''' - self.api.get_user_ids_of_blocked_retweets(stringify_ids='true') + self.api.get_user_ids_of_blocked_retweets(stringify_ids=True) def test_get_friends_ids(self): '''Test returning ids of users the authenticated user and then a random diff --git a/twython/__init__.py b/twython/__init__.py index 6390bcb..ce1c200 100644 --- a/twython/__init__.py +++ b/twython/__init__.py @@ -18,7 +18,7 @@ Questions, comments? ryan@venodesigns.net """ __author__ = 'Ryan McGrath ' -__version__ = '2.10.0' +__version__ = '2.10.1' from .twython import Twython from .streaming import TwythonStreamer diff --git a/twython/twython.py b/twython/twython.py index 286afd8..1ac04f8 100644 --- a/twython/twython.py +++ b/twython/twython.py @@ -59,27 +59,27 @@ class Twython(object): stacklevel=2 ) - self.headers = {'User-Agent': 'Twython v' + __version__} + req_headers = {'User-Agent': 'Twython v' + __version__} if headers: - self.headers.update(headers) + req_headers.update(headers) # Generate OAuth authentication object for the request - # If no keys/tokens are passed to __init__, self.auth=None allows for + # If no keys/tokens are passed to __init__, auth=None allows for # unauthenticated requests, although I think all v1.1 requests need auth - self.auth = None + auth = None if self.app_key is not None and self.app_secret is not None and \ self.oauth_token is None and self.oauth_token_secret is None: - self.auth = OAuth1(self.app_key, self.app_secret) + auth = OAuth1(self.app_key, self.app_secret) if self.app_key is not None and self.app_secret is not None and \ self.oauth_token is not None and self.oauth_token_secret is not None: - self.auth = OAuth1(self.app_key, self.app_secret, - self.oauth_token, self.oauth_token_secret) + auth = OAuth1(self.app_key, self.app_secret, + self.oauth_token, self.oauth_token_secret) self.client = requests.Session() - self.client.headers = self.headers + self.client.headers = req_headers self.client.proxies = proxies - self.client.auth = self.auth + self.client.auth = auth self.client.verify = ssl_verify # register available funcs to allow listing name when debugging. @@ -208,7 +208,7 @@ class Twython(object): def request(self, endpoint, method='GET', params=None, version='1.1'): # In case they want to pass a full Twitter URL - # i.e. https://search.twitter.com/ + # i.e. https://api.twitter.com/1.1/search/tweets.json if endpoint.startswith('http://') or endpoint.startswith('https://'): url = endpoint else: @@ -241,9 +241,11 @@ class Twython(object): """ if self._last_call is None: raise TwythonError('This function must be called after an API call. It delivers header information.') + if header in self._last_call['headers']: return self._last_call['headers'][header] - return self._last_call + else: + return None def get_authentication_tokens(self, callback_url=None, force_login=False, screen_name=''): """Returns a dict including an authorization URL (auth_url) to direct a user to @@ -325,7 +327,7 @@ class Twython(object): stacklevel=2 ) - if shortener == '': + if not shortener: raise TwythonError('Please provide a URL shortening service.') request = requests.get(shortener, params={ @@ -336,7 +338,7 @@ class Twython(object): if request.status_code in [301, 201, 200]: return request.text else: - raise TwythonError('shortenURL() failed with a %s error code.' % request.status_code) + raise TwythonError('shorten_url failed with a %s error code.' % request.status_code) @staticmethod def constructApiURL(base_url, params): @@ -373,17 +375,16 @@ class Twython(object): See Twython.search() for acceptable parameters - e.g search = x.searchGen('python') + e.g search = x.search_gen('python') for result in search: print result """ - kwargs['q'] = search_query content = self.search(q=search_query, **kwargs) - if not content['results']: + if not content.get('statuses'): raise StopIteration - for tweet in content['results']: + for tweet in content['statuses']: yield tweet try: From 815393cc336a87d759b67deda47fabb0a51e43db Mon Sep 17 00:00:00 2001 From: Mike Helmick Date: Thu, 23 May 2013 23:36:05 -0400 Subject: [PATCH 2/7] Meant to use bad_api for these Tests --- test_twython.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test_twython.py b/test_twython.py index e5e7fa1..96a5b4c 100644 --- a/test_twython.py +++ b/test_twython.py @@ -35,12 +35,12 @@ class TwythonAuthTestCase(unittest.TestCase): def test_get_authentication_tokens_bad_tokens(self): '''Test getting authentication tokens with bad tokens raises TwythonAuthError''' - self.assertRaises(TwythonAuthError, self.api.get_authentication_tokens, + self.assertRaises(TwythonAuthError, self.bad_api.get_authentication_tokens, callback_url='http://google.com/') def test_get_authorized_tokens_bad_tokens(self): '''Test getting final tokens fails with wrong tokens''' - self.assertRaises(TwythonError, self.api.get_authorized_tokens, + self.assertRaises(TwythonError, self.bad_api.get_authorized_tokens, 'BAD_OAUTH_VERIFIER') From c8b12028808f04f294b9452da6647877c81911e0 Mon Sep 17 00:00:00 2001 From: Mike Helmick Date: Fri, 24 May 2013 15:19:19 -0400 Subject: [PATCH 3/7] Added disconnect to TwythonStreamer, more tests, update example * Stream and Twython core tests * Import TwythonStreamError from twython See more in 2.10.1 section of HISTORY.rst --- .travis.yml | 2 +- HISTORY.rst | 2 ++ examples/stream.py | 2 ++ test_twython.py | 67 ++++++++++++++++++++++++++++++++++---- twython/__init__.py | 5 ++- twython/exceptions.py | 30 ++++++++--------- twython/streaming/api.py | 62 ++++++++++++++++++++--------------- twython/streaming/types.py | 2 +- twython/twython.py | 5 +-- 9 files changed, 122 insertions(+), 55 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6788276..a5992e1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,7 +26,7 @@ env: - PROTECTED_TWITTER_2=TwythonSecure2 - TEST_TWEET_ID=332992304010899457 - TEST_LIST_ID=574 -script: nosetests -v test_twython:TwythonAPITestCase test_twython:TwythonAuthTestCase --logging-filter="twython" --cover-package="twython" --with-coverage +script: nosetests -v test_twython:TwythonAPITestCase test_twython:TwythonAuthTestCase test_twython:TwythonStreamTestCase --logging-filter="twython" --cover-package="twython" --with-coverage install: pip install -r requirements.txt notifications: email: false diff --git a/HISTORY.rst b/HISTORY.rst index 4e3706d..a56f395 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -9,6 +9,8 @@ History - Fix ``search_gen`` - Fixed ``get_lastfunction_header`` to actually do what its docstring says, returns ``None`` if header is not found - Updated some internal API code, ``__init__`` didn't need to have ``self.auth`` and ``self.headers`` because they were never used anywhere else but the ``__init__`` +- Added ``disconnect`` method to ``TwythonStreamer``, allowing users to disconnect as they desire +- Updated ``TwythonStreamError`` docstring, also allow importing it from ``twython`` 2.10.0 (2013-05-21) ++++++++++++++++++ diff --git a/examples/stream.py b/examples/stream.py index f5c5f1a..0e23a09 100644 --- a/examples/stream.py +++ b/examples/stream.py @@ -4,6 +4,8 @@ from twython import TwythonStreamer class MyStreamer(TwythonStreamer): def on_success(self, data): print data + # Want to disconnect after the first result? + # self.disconnect() def on_error(self, status_code, data): print status_code, data diff --git a/test_twython.py b/test_twython.py index 96a5b4c..c867834 100644 --- a/test_twython.py +++ b/test_twython.py @@ -1,7 +1,11 @@ -import unittest -import os +from twython import( + Twython, TwythonStreamer, TwythonError, + TwythonAuthError, TwythonStreamError +) -from twython import Twython, TwythonError, TwythonAuthError +import os +import time +import unittest app_key = os.environ.get('APP_KEY') app_secret = os.environ.get('APP_SECRET') @@ -94,10 +98,17 @@ class TwythonAPITestCase(unittest.TestCase): def test_search_gen(self): '''Test looping through the generator results works, at least once that is''' - search = self.api.search_gen('python') - for result in search: - if result: - break + search = self.api.search_gen('twitter', count=1) + counter = 0 + while counter < 2: + counter += 1 + result = search.next() + new_id_str = int(result['id_str']) + if counter == 1: + prev_id_str = new_id_str + time.sleep(1) # Give time for another tweet to come into search + if counter == 2: + self.assertTrue(new_id_str > prev_id_str) def test_encode(self): '''Test encoding UTF-8 works''' @@ -480,5 +491,47 @@ class TwythonAPITestCase(unittest.TestCase): self.api.get_closest_trends(lat='37', long='-122') +class TwythonStreamTestCase(unittest.TestCase): + def setUp(self): + class MyStreamer(TwythonStreamer): + def on_success(self, data): + self.disconnect() + + def on_error(self, status_code, data): + raise TwythonStreamError(data) + + def on_delete(self, data): + return + + def on_limit(self, data): + return + + def on_disconnect(self, data): + return + + def on_timeout(self, data): + return + + self.api = MyStreamer(app_key, app_secret, + oauth_token, oauth_token_secret) + + def test_stream_status_filter(self): + self.api.statuses.filter(track='twitter') + + def test_stream_status_sample(self): + self.api.statuses.sample() + + def test_stream_status_firehose(self): + self.assertRaises(TwythonStreamError, self.api.statuses.firehose, + track='twitter') + + def test_stream_site(self): + self.assertRaises(TwythonStreamError, self.api.site, + follow='twitter') + + def test_stream_user(self): + self.api.user(track='twitter') + + if __name__ == '__main__': unittest.main() diff --git a/twython/__init__.py b/twython/__init__.py index ce1c200..5befefb 100644 --- a/twython/__init__.py +++ b/twython/__init__.py @@ -22,4 +22,7 @@ __version__ = '2.10.1' from .twython import Twython from .streaming import TwythonStreamer -from .exceptions import TwythonError, TwythonRateLimitError, TwythonAuthError +from .exceptions import ( + TwythonError, TwythonRateLimitError, TwythonAuthError, + TwythonStreamError +) diff --git a/twython/exceptions.py b/twython/exceptions.py index 265356a..924a882 100644 --- a/twython/exceptions.py +++ b/twython/exceptions.py @@ -2,17 +2,15 @@ from .endpoints import twitter_http_status_codes class TwythonError(Exception): - """ - Generic error class, catch-all for most Twython issues. - Special cases are handled by TwythonAuthError & TwythonRateLimitError. + """Generic error class, catch-all for most Twython issues. + Special cases are handled by TwythonAuthError & TwythonRateLimitError. - Note: Syntax has changed as of Twython 1.3. To catch these, - you need to explicitly import them into your code, e.g: + Note: Syntax has changed as of Twython 1.3. To catch these, + you need to explicitly import them into your code, e.g: - from twython import ( - TwythonError, TwythonRateLimitError, TwythonAuthError - ) - """ + from twython import ( + TwythonError, TwythonRateLimitError, TwythonAuthError + )""" def __init__(self, msg, error_code=None, retry_after=None): self.error_code = error_code @@ -30,18 +28,16 @@ class TwythonError(Exception): class TwythonAuthError(TwythonError): - """ Raised when you try to access a protected resource and it fails due to - some issue with your authentication. - """ + """Raised when you try to access a protected resource and it fails due to + some issue with your authentication.""" pass class TwythonRateLimitError(TwythonError): - """ Raised when you've hit a rate limit. + """Raised when you've hit a rate limit. - The amount of seconds to retry your request in will be appended - to the message. - """ + The amount of seconds to retry your request in will be appended + to the message.""" def __init__(self, msg, error_code, retry_after=None): if isinstance(retry_after, int): msg = '%s (Retry after %d seconds)' % (msg, retry_after) @@ -49,5 +45,5 @@ class TwythonRateLimitError(TwythonError): class TwythonStreamError(TwythonError): - """Test""" + """Raised when an invalid response from the Stream API is received""" pass diff --git a/twython/streaming/api.py b/twython/streaming/api.py index 541aa07..1a77ec5 100644 --- a/twython/streaming/api.py +++ b/twython/streaming/api.py @@ -55,41 +55,48 @@ class TwythonStreamer(object): self.user = StreamTypes.user self.site = StreamTypes.site + self.connected = False + def _request(self, url, method='GET', params=None): """Internal stream request handling""" + self.connected = True retry_counter = 0 method = method.lower() func = getattr(self.client, method) def _send(retry_counter): - try: - if method == 'get': - response = func(url, params=params, timeout=self.timeout) - else: - response = func(url, data=params, timeout=self.timeout) - except requests.exceptions.Timeout: - self.on_timeout() - else: - if response.status_code != 200: - self.on_error(response.status_code, response.content) - - if self.retry_count and (self.retry_count - retry_counter) > 0: - time.sleep(self.retry_in) - retry_counter += 1 - _send(retry_counter) - - return response - - response = _send(retry_counter) - - for line in response.iter_lines(): - if line: + while self.connected: try: - self.on_success(json.loads(line)) - except ValueError: - raise TwythonStreamError('Response was not valid JSON, \ - unable to decode.') + if method == 'get': + response = func(url, params=params, timeout=self.timeout) + else: + response = func(url, data=params, timeout=self.timeout) + except requests.exceptions.Timeout: + self.on_timeout() + else: + if response.status_code != 200: + self.on_error(response.status_code, response.content) + + if self.retry_count and (self.retry_count - retry_counter) > 0: + time.sleep(self.retry_in) + retry_counter += 1 + _send(retry_counter) + + return response + + while self.connected: + response = _send(retry_counter) + + for line in response.iter_lines(): + if not self.connected: + break + if line: + try: + self.on_success(json.loads(line)) + except ValueError: + raise TwythonStreamError('Response was not valid JSON, \ + unable to decode.') def on_success(self, data): """Called when data has been successfull received from the stream @@ -161,3 +168,6 @@ class TwythonStreamer(object): def on_timeout(self): return + + def disconnect(self): + self.connected = False diff --git a/twython/streaming/types.py b/twython/streaming/types.py index fd02f81..c17cadf 100644 --- a/twython/streaming/types.py +++ b/twython/streaming/types.py @@ -61,7 +61,7 @@ class TwythonStreamerTypesStatuses(object): self.streamer._request(url, params=params) def firehose(self, **params): - """Stream statuses/filter + """Stream statuses/firehose Accepted params found at: https://dev.twitter.com/docs/api/1.1/get/statuses/firehose diff --git a/twython/twython.py b/twython/twython.py index 1ac04f8..d196a85 100644 --- a/twython/twython.py +++ b/twython/twython.py @@ -388,11 +388,12 @@ class Twython(object): yield tweet try: - kwargs['page'] = 2 if not 'page' in kwargs else (int(kwargs['page']) + 1) + if not 'since_id' in kwargs: + kwargs['since_id'] = (int(content['statuses'][0]['id_str']) + 1) except (TypeError, ValueError): raise TwythonError('Unable to generate next page of search results, `page` is not a number.') - for tweet in self.searchGen(search_query, **kwargs): + for tweet in self.search_gen(search_query, **kwargs): yield tweet @staticmethod From 64b134999371cbb6a07e12597213d331ab2b16a9 Mon Sep 17 00:00:00 2001 From: Mike Helmick Date: Fri, 24 May 2013 16:17:50 -0400 Subject: [PATCH 4/7] Python 3 compat --- test_twython.py | 2 +- twython/streaming/api.py | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/test_twython.py b/test_twython.py index c867834..1e706a5 100644 --- a/test_twython.py +++ b/test_twython.py @@ -102,7 +102,7 @@ class TwythonAPITestCase(unittest.TestCase): counter = 0 while counter < 2: counter += 1 - result = search.next() + result = next(search) new_id_str = int(result['id_str']) if counter == 1: prev_id_str = new_id_str diff --git a/twython/streaming/api.py b/twython/streaming/api.py index 1a77ec5..6414020 100644 --- a/twython/streaming/api.py +++ b/twython/streaming/api.py @@ -1,5 +1,5 @@ from .. import __version__ -from ..compat import json +from ..compat import json, is_py3 from ..exceptions import TwythonStreamError from .types import TwythonStreamerTypes @@ -93,7 +93,11 @@ class TwythonStreamer(object): break if line: try: - self.on_success(json.loads(line)) + if not is_py3: + self.on_success(json.loads(line)) + else: + line = line.decode('utf-8') + self.on_success(json.loads(line)) except ValueError: raise TwythonStreamError('Response was not valid JSON, \ unable to decode.') From f879094ea196c655c7916359b45331ab81f603e9 Mon Sep 17 00:00:00 2001 From: Mike Helmick Date: Tue, 28 May 2013 17:42:41 -0400 Subject: [PATCH 5/7] Update stream example, update AUTHORS for future example fix Remove tests that usually caused Travis to fail Made it clear that Authenticaiton IS required for Streaming in the docstring --- AUTHORS.rst | 2 ++ HISTORY.rst | 1 + examples/stream.py | 3 ++- test_twython.py | 16 ---------------- twython/streaming/api.py | 6 ++++-- 5 files changed, 9 insertions(+), 19 deletions(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index e155804..461d25d 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -40,3 +40,5 @@ Patches and Suggestions - `Greg Nofi `_, fixed using built-in Exception attributes for storing & retrieving error message - `Jonathan Vanasco `_, Debugging support, error_code tracking, Twitter error API tracking, other fixes - `DevDave `_, quick fix for longs with helper._transparent_params +- `Ruben Varela Rosa `_, Fixed search example +>>>>>>> Update stream example, update AUTHORS for future example fix diff --git a/HISTORY.rst b/HISTORY.rst index a56f395..bdeb0fc 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -11,6 +11,7 @@ History - Updated some internal API code, ``__init__`` didn't need to have ``self.auth`` and ``self.headers`` because they were never used anywhere else but the ``__init__`` - Added ``disconnect`` method to ``TwythonStreamer``, allowing users to disconnect as they desire - Updated ``TwythonStreamError`` docstring, also allow importing it from ``twython`` +- No longer raise ``TwythonStreamError`` when stream line can't be decoded. Instead, sends signal to ``TwythonStreamer.on_error`` 2.10.0 (2013-05-21) ++++++++++++++++++ diff --git a/examples/stream.py b/examples/stream.py index 0e23a09..a571711 100644 --- a/examples/stream.py +++ b/examples/stream.py @@ -3,7 +3,8 @@ from twython import TwythonStreamer class MyStreamer(TwythonStreamer): def on_success(self, data): - print data + if 'text' in data: + print data['text'].encode('utf-8') # Want to disconnect after the first result? # self.disconnect() diff --git a/test_twython.py b/test_twython.py index 1e706a5..16598c1 100644 --- a/test_twython.py +++ b/test_twython.py @@ -154,22 +154,6 @@ class TwythonAPITestCase(unittest.TestCase): status = self.api.update_status(status='Test post just to get deleted :(') self.api.destroy_status(id=status['id_str']) - def test_retweet(self): - '''Test retweeting a status succeeds''' - retweet = self.api.retweet(id='99530515043983360') - self.api.destroy_status(id=retweet['id_str']) - - def test_retweet_twice(self): - '''Test that trying to retweet a tweet twice raises a TwythonError''' - tweets = self.api.search(q='twitter').get('statuses') - if tweets: - retweet = self.api.retweet(id=tweets[0]['id_str']) - self.assertRaises(TwythonError, self.api.retweet, - id=tweets[0]['id_str']) - - # Then clean up - self.api.destroy_status(id=retweet['id_str']) - def test_get_oembed_tweet(self): '''Test getting info to embed tweet on Third Party site succeeds''' self.api.get_oembed_tweet(id='99530515043983360') diff --git a/twython/streaming/api.py b/twython/streaming/api.py index 6414020..a246593 100644 --- a/twython/streaming/api.py +++ b/twython/streaming/api.py @@ -13,6 +13,7 @@ class TwythonStreamer(object): def __init__(self, app_key, app_secret, oauth_token, oauth_token_secret, timeout=300, retry_count=None, retry_in=10, headers=None): """Streaming class for a friendly streaming user experience + Authentication IS required to use the Twitter Streaming API :param app_key: (required) Your applications key :param app_secret: (required) Your applications secret key @@ -99,8 +100,9 @@ class TwythonStreamer(object): line = line.decode('utf-8') self.on_success(json.loads(line)) except ValueError: - raise TwythonStreamError('Response was not valid JSON, \ - unable to decode.') + self.on_error(response.status_code, 'Unable to decode response, not vaild JSON.') + + response.close() def on_success(self, data): """Called when data has been successfull received from the stream From 71ea58cf6fb01c0030fd0c562e9b9ad5318578dc Mon Sep 17 00:00:00 2001 From: Mike Helmick Date: Tue, 28 May 2013 17:47:03 -0400 Subject: [PATCH 6/7] Send a "different" message everytime for DM tests --- test_twython.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_twython.py b/test_twython.py index 16598c1..f5372c7 100644 --- a/test_twython.py +++ b/test_twython.py @@ -180,7 +180,7 @@ class TwythonAPITestCase(unittest.TestCase): def test_send_get_and_destroy_direct_message(self): '''Test sending, getting, then destory a direct message succeeds''' message = self.api.send_direct_message(screen_name=protected_twitter_1, - text='Hey d00d!') + text='Hey d00d! %s' % int(time.time())) self.api.get_direct_message(id=message['id_str']) self.api.destroy_direct_message(id=message['id_str']) From 81a6802c639e8209557a48b99ec905e5712dfaca Mon Sep 17 00:00:00 2001 From: Mike Helmick Date: Wed, 29 May 2013 11:40:14 -0400 Subject: [PATCH 7/7] Update HISTORY [ci skip] --- HISTORY.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/HISTORY.rst b/HISTORY.rst index bdeb0fc..7ac3c4e 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -12,6 +12,7 @@ History - Added ``disconnect`` method to ``TwythonStreamer``, allowing users to disconnect as they desire - Updated ``TwythonStreamError`` docstring, also allow importing it from ``twython`` - No longer raise ``TwythonStreamError`` when stream line can't be decoded. Instead, sends signal to ``TwythonStreamer.on_error`` +- Allow for (int, long, float) params to be passed to Twython Twitter API functions in Python 2, and (int, float) in Python 3 2.10.0 (2013-05-21) ++++++++++++++++++