Skip to content

Commit

Permalink
Add pagination for AsyncClient
Browse files Browse the repository at this point in the history
  • Loading branch information
Harmon758 committed Oct 10, 2022
1 parent 48ebdb7 commit 33e444a
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 0 deletions.
3 changes: 3 additions & 0 deletions docs/v2_pagination.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@ Pagination
for tweet in tweepy.Paginator(client.search_recent_tweets, "Tweepy",
max_results=100).flatten(limit=250):
print(tweet.id)

.. autoclass:: tweepy.asynchronous.AsyncPaginator
:members:
1 change: 1 addition & 0 deletions tweepy/asynchronous/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@

from tweepy.asynchronous.streaming import AsyncStream, AsyncStreamingClient
from tweepy.asynchronous.client import AsyncClient
from tweepy.asynchronous.pagination import AsyncPaginator
108 changes: 108 additions & 0 deletions tweepy/asynchronous/pagination.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Tweepy
# Copyright 2009-2022 Joshua Roesslein
# See LICENSE for details.

from math import inf


class AsyncPaginator:
""":class:`AsyncPaginator` can be used to paginate for any
:class:`AsyncClient` methods that support pagination
.. versionadded:: 4.11
Parameters
----------
method
:class:`AsyncClient` method to paginate for
args
Positional arguments to pass to ``method``
kwargs
Keyword arguments to pass to ``method``
"""

def __init__(self, method, *args, **kwargs):
self.method = method
self.args = args
self.kwargs = kwargs

def __aiter__(self):
return AsyncPaginationIterator(self.method, *self.args, **self.kwargs)

def __reversed__(self):
return AsyncPaginationIterator(
self.method, *self.args, reverse=True, **self.kwargs
)

async def flatten(self, limit=inf):
"""Flatten paginated data
Parameters
----------
limit
Maximum number of results to yield
"""
if limit <= 0:
return

count = 0
async for response in AsyncPaginationIterator(
self.method, *self.args, **self.kwargs
):
if response.data is not None:
for data in response.data:
yield data
count += 1
if count == limit:
return


class AsyncPaginationIterator:

def __init__(
self, method, *args, limit=inf, pagination_token=None, reverse=False,
**kwargs
):
self.method = method
self.args = args
self.limit = limit
self.kwargs = kwargs
self.reverse = reverse

if reverse:
self.previous_token = pagination_token
self.next_token = None
else:
self.previous_token = None
self.next_token = pagination_token

self.count = 0

def __aiter__(self):
return self

async def __anext__(self):
if self.reverse:
pagination_token = self.previous_token
else:
pagination_token = self.next_token

if self.count >= self.limit or self.count and pagination_token is None:
raise StopAsyncIteration

# https://twittercommunity.com/t/why-does-timeline-use-pagination-token-while-search-uses-next-token/150963
if self.method.__name__ in (
"search_all_tweets", "search_recent_tweets",
"get_all_tweets_count"
):
self.kwargs["next_token"] = pagination_token
else:
self.kwargs["pagination_token"] = pagination_token

response = await self.method(*self.args, **self.kwargs)

self.previous_token = response.meta.get("previous_token")
self.next_token = response.meta.get("next_token")
self.count += 1

return response

0 comments on commit 33e444a

Please sign in to comment.