Skip to content

Commit 21055ad

Browse files
committed
Merge remote-tracking branch 'master' into search-v1.1
Conflicts: tweepy/models.py
2 parents 2098078 + 4d75a02 commit 21055ad

File tree

8 files changed

+130
-47
lines changed

8 files changed

+130
-47
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
script: nosetests -v tests.test_api tests.test_streaming
2+
script: nosetests -v tests.test_api tests.test_streaming tests.test_cursors
33
language: python
44
env:
55
global:

tests/test_api.py

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -312,43 +312,6 @@ def place_name_in_list(place_name, place_list):
312312
self.assertTrue(place_name_in_list('Austin, TX',
313313
self.api.reverse_geocode(lat=30.267370168467806, long= -97.74261474609375))) # Austin, TX, USA
314314

315-
class TweepyCursorTests(unittest.TestCase):
316-
317-
def setUp(self):
318-
auth = OAuthHandler(oauth_consumer_key, oauth_consumer_secret)
319-
auth.set_access_token(oauth_token, oauth_token_secret)
320-
self.api = API(auth)
321-
self.api.retry_count = 2
322-
self.api.retry_delay = 5
323-
324-
def testpagecursoritems(self):
325-
items = list(Cursor(self.api.user_timeline).items())
326-
self.assert_(len(items) > 0)
327-
328-
items = list(Cursor(self.api.user_timeline, 'twitter').items(30))
329-
self.assert_(len(items) == 30)
330-
331-
def testpagecursorpages(self):
332-
pages = list(Cursor(self.api.user_timeline).pages())
333-
self.assert_(len(pages) > 0)
334-
335-
pages = list(Cursor(self.api.user_timeline, 'twitter').pages(5))
336-
self.assert_(len(pages) == 5)
337-
338-
def testcursorcursoritems(self):
339-
items = list(Cursor(self.api.friends_ids).items())
340-
self.assert_(len(items) > 0)
341-
342-
items = list(Cursor(self.api.followers_ids, 'twitter').items(30))
343-
self.assert_(len(items) == 30)
344-
345-
def testcursorcursorpages(self):
346-
pages = list(Cursor(self.api.friends_ids).pages())
347-
self.assert_(len(pages) > 0)
348-
349-
pages = list(Cursor(self.api.followers_ids, 'twitter').pages(5))
350-
self.assert_(len(pages) == 5)
351-
352315
class TweepyCacheTests(unittest.TestCase):
353316

354317
timeout = 2.0

tests/test_cursors.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import unittest
2+
3+
from tweepy import API, Cursor
4+
5+
from config import create_auth
6+
7+
class TweepyCursorTests(unittest.TestCase):
8+
9+
def setUp(self):
10+
self.api = API(create_auth())
11+
self.api.retry_count = 2
12+
self.api.retry_delay = 5
13+
14+
def testidcursoritems(self):
15+
items = list(Cursor(self.api.user_timeline).items(25))
16+
self.assertEqual(len(items), 25)
17+
18+
def testidcursorpages(self):
19+
pages = list(Cursor(self.api.user_timeline).pages(5))
20+
self.assertEqual(len(pages), 5)
21+
22+
def testcursorcursoritems(self):
23+
items = list(Cursor(self.api.friends_ids).items())
24+
self.assert_(len(items) > 0)
25+
26+
items = list(Cursor(self.api.followers_ids, 'twitter').items(30))
27+
self.assert_(len(items) == 30)
28+
29+
def testcursorcursorpages(self):
30+
pages = list(Cursor(self.api.friends_ids).pages())
31+
self.assert_(len(pages) > 0)
32+
33+
pages = list(Cursor(self.api.followers_ids, 'twitter').pages(5))
34+
self.assert_(len(pages) == 5)
35+

tests/test_resultset.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import unittest
2+
3+
from tweepy.models import ResultSet
4+
5+
class NoIdItem(object): pass
6+
7+
class IdItem(object):
8+
def __init__(self, id):
9+
self.id = id
10+
11+
ids_fixture = [1, 10, 8, 50, 2, 100, 5]
12+
13+
class TweepyResultSetTests(unittest.TestCase):
14+
def setUp(self):
15+
self.results = ResultSet()
16+
for i in ids_fixture:
17+
self.results.append(IdItem(i))
18+
self.results.append(NoIdItem())
19+
20+
def testids(self):
21+
ids = self.results.ids()
22+
self.assertListEqual(ids, ids_fixture)
23+
24+
def testmaxid(self):
25+
self.assertEqual(self.results.max_id, 100)
26+
27+
def testsinceid(self):
28+
self.assertEqual(self.results.since_id, 1)
29+

tweepy/api.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def __init__(self, auth_handler=None,
3737
home_timeline = bind_api(
3838
path = '/statuses/home_timeline.json',
3939
payload_type = 'status', payload_list = True,
40-
allowed_param = ['since_id', 'max_id', 'count', 'page'],
40+
allowed_param = ['since_id', 'max_id', 'count'],
4141
require_auth = True
4242
)
4343

@@ -46,7 +46,7 @@ def __init__(self, auth_handler=None,
4646
path = '/statuses/user_timeline.json',
4747
payload_type = 'status', payload_list = True,
4848
allowed_param = ['id', 'user_id', 'screen_name', 'since_id',
49-
'max_id', 'count', 'page', 'include_rts']
49+
'max_id', 'count', 'include_rts']
5050
)
5151

5252
""" statuses/mentions """
@@ -85,7 +85,7 @@ def __init__(self, auth_handler=None,
8585
retweets_of_me = bind_api(
8686
path = '/statuses/retweets_of_me.json',
8787
payload_type = 'status', payload_list = True,
88-
allowed_param = ['since_id', 'max_id', 'count', 'page'],
88+
allowed_param = ['since_id', 'max_id', 'count'],
8989
require_auth = True
9090
)
9191

@@ -195,7 +195,7 @@ def me(self):
195195
direct_messages = bind_api(
196196
path = '/direct_messages.json',
197197
payload_type = 'direct_message', payload_list = True,
198-
allowed_param = ['since_id', 'max_id', 'count', 'page'],
198+
allowed_param = ['since_id', 'max_id', 'count'],
199199
require_auth = True
200200
)
201201

@@ -282,7 +282,7 @@ def lookup_friendships(self, user_ids=None, screen_names=None):
282282
friends = bind_api(
283283
path = '/friends/list.json',
284284
payload_type = 'user', payload_list = True,
285-
allowed_param = ['id', 'user_id', 'screen_name', 'page', 'cursor']
285+
allowed_param = ['id', 'user_id', 'screen_name', 'cursor']
286286
)
287287

288288
""" friendships/incoming """
@@ -310,7 +310,7 @@ def lookup_friendships(self, user_ids=None, screen_names=None):
310310
followers = bind_api(
311311
path = '/followers/list.json',
312312
payload_type = 'user', payload_list = True,
313-
allowed_param = ['id', 'user_id', 'screen_name', 'page', 'cursor']
313+
allowed_param = ['id', 'user_id', 'screen_name', 'cursor']
314314
)
315315

316316
""" account/verify_credentials """
@@ -432,7 +432,7 @@ def update_profile_background_image(self, filename, *args, **kargs):
432432
blocks = bind_api(
433433
path = '/blocks/list.json',
434434
payload_type = 'user', payload_list = True,
435-
allowed_param = ['page'],
435+
allowed_param = ['cursor'],
436436
require_auth = True
437437
)
438438

tweepy/binder.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,9 @@ def _call(api, *args, **kargs):
200200
# Set pagination mode
201201
if 'cursor' in APIMethod.allowed_param:
202202
_call.pagination_mode = 'cursor'
203+
elif 'max_id' in APIMethod.allowed_param and \
204+
'since_id' in APIMethod.allowed_param:
205+
_call.pagination_mode = 'id'
203206
elif 'page' in APIMethod.allowed_param:
204207
_call.pagination_mode = 'page'
205208

tweepy/cursor.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@ def __init__(self, method, *args, **kargs):
1111
if hasattr(method, 'pagination_mode'):
1212
if method.pagination_mode == 'cursor':
1313
self.iterator = CursorIterator(method, args, kargs)
14-
else:
14+
elif method.pagination_mode == 'id':
15+
self.iterator = IdIterator(method, args, kargs)
16+
elif method.pagination_mode == 'page':
1517
self.iterator = PageIterator(method, args, kargs)
18+
else:
19+
raise TweepError('Invalid pagination mode.')
1620
else:
1721
raise TweepError('This method does not perform pagination')
1822

@@ -74,6 +78,35 @@ def prev(self):
7478
self.count -= 1
7579
return data
7680

81+
class IdIterator(BaseIterator):
82+
83+
def __init__(self, method, args, kargs):
84+
BaseIterator.__init__(self, method, args, kargs)
85+
self.max_id = kargs.get('max_id')
86+
self.since_id = kargs.get('since_id')
87+
88+
def next(self):
89+
"""Fetch a set of items with IDs less than current set."""
90+
# max_id is inclusive so decrement by one
91+
# to avoid requesting duplicate items.
92+
max_id = self.since_id - 1 if self.max_id else None
93+
data = self.method(max_id = max_id, *self.args, **self.kargs)
94+
if len(data) == 0:
95+
raise StopIteration
96+
self.max_id = data.max_id
97+
self.since_id = data.since_id
98+
return data
99+
100+
def prev(self):
101+
"""Fetch a set of items with IDs greater than current set."""
102+
since_id = self.max_id
103+
data = self.method(since_id = since_id, *self.args, **self.kargs)
104+
if len(data) == 0:
105+
raise StopIteration
106+
self.max_id = data.max_id
107+
self.since_id = data.since_id
108+
return data
109+
77110
class PageIterator(BaseIterator):
78111

79112
def __init__(self, method, args, kargs):

tweepy/models.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,27 @@
99

1010
class ResultSet(list):
1111
"""A list like object that holds results from a Twitter API query."""
12-
12+
def __init__(self, max_id=None, since_id=None):
13+
super(ResultSet, self).__init__()
14+
self._max_id = max_id
15+
self._since_id = since_id
16+
17+
@property
18+
def max_id(self):
19+
if self._max_id:
20+
return self._max_id
21+
ids = self.ids()
22+
return max(ids) if ids else None
23+
24+
@property
25+
def since_id(self):
26+
if self._since_id:
27+
return self._since_id
28+
ids = self.ids()
29+
return min(ids) if ids else None
30+
31+
def ids(self):
32+
return [item.id for item in self if hasattr(item, 'id')]
1333

1434
class Model(object):
1535

0 commit comments

Comments
 (0)