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
This commit is contained in:
Mike Helmick 2013-05-24 15:19:19 -04:00
parent 815393cc33
commit c8b1202880
9 changed files with 122 additions and 55 deletions

View file

@ -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

View file

@ -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)
++++++++++++++++++

View file

@ -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

View file

@ -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()

View file

@ -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
)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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