Skip to content

Commit 89d7cb1

Browse files
authored
Merge pull request #130 from david-lev/result-all
[types] adding `all` method to `Result` and fix `next` and `previous` to return empty result
2 parents 888ff61 + ac8aa96 commit 89d7cb1

File tree

4 files changed

+335
-37
lines changed

4 files changed

+335
-37
lines changed

docs/source/content/types/others.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Others
1313
.. currentmodule:: pywa.types.others
1414

1515
.. autoclass:: Result()
16-
:members: has_next, next, has_previous, previous, before, after
16+
:members: has_next, next, has_previous, previous, all, before, after, empty
1717

1818
.. autoclass:: Pagination()
1919

pywa/types/others.py

Lines changed: 84 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from __future__ import annotations
22

3+
import time
4+
35
from ..errors import WhatsAppError
46

57
"""Types for other objects."""
@@ -1123,15 +1125,26 @@ class Result(Generic[_T]):
11231125
11241126
>>> from pywa import WhatsApp, types
11251127
>>> wa = WhatsApp(...)
1126-
>>> all_blocked_users = []
11271128
>>> res = wa.get_blocked_users(pagination=types.Pagination(limit=100))
1128-
>>> while True:
1129-
... all_blocked_users.extend(res)
1130-
... if not res.has_next:
1131-
... break
1132-
... res = res.next()
1133-
>>> all_blocked_users
1134-
[User(...), User(...), User(...), ...]
1129+
>>> for user in res:
1130+
... print(user.name, user.wa_id)
1131+
...
1132+
>>> if res.has_next:
1133+
... next_res = res.next()
1134+
...
1135+
>>> print(res.all())
1136+
1137+
Methods:
1138+
next: Get the next page of results. if there is no next page, it returns empty Result.
1139+
previous: Get the previous page of results. if there is no previous page, it returns empty Result.
1140+
all: Get all results from the current page, previous pages, and next pages.
1141+
empty: Returns an empty Result instance.
1142+
1143+
Properties:
1144+
has_next: Check if there is a next page of results.
1145+
has_previous: Check if there is a previous page of results.
1146+
before: Cursor that points to the start of the page of data that has been returned.
1147+
after: Cursor that points to the end of the page of data that has been returned.
11351148
"""
11361149

11371150
def __init__(
@@ -1169,18 +1182,38 @@ def after(self) -> str | None:
11691182
"""Cursor that points to the end of the page of data that has been returned."""
11701183
return self._cursors.get("after")
11711184

1172-
def next(self) -> Result[_T] | None:
1173-
"""Get the next page of results."""
1185+
@property
1186+
def empty(self) -> Result[_T]:
1187+
"""Returns an empty Result instance."""
1188+
return Result(
1189+
wa=self._wa,
1190+
response={
1191+
"data": [],
1192+
"paging": {"next": self._next_url, "cursors": self._cursors},
1193+
},
1194+
item_factory=self._item_factory,
1195+
)
1196+
1197+
def next(self) -> Result[_T]:
1198+
"""
1199+
Get the next page of results. if there is no next page, it returns empty Result.
1200+
1201+
- Check if there is a next page using the :attr:`~pywa.types.others.Result.has_next` property before calling this method.
1202+
"""
11741203
if self.has_next:
11751204
# noinspection PyProtectedMember
11761205
response = self._wa.api._make_request(method="GET", endpoint=self._next_url)
11771206
return Result(
11781207
wa=self._wa, response=response, item_factory=self._item_factory
11791208
)
1180-
return None
1209+
return self.empty
1210+
1211+
def previous(self) -> Result[_T]:
1212+
"""
1213+
Get the previous page of results. if there is no previous page, it returns empty Result.
11811214
1182-
def previous(self) -> Result[_T] | None:
1183-
"""Get the previous page of results."""
1215+
- Check if there is a previous page using the :attr:`~pywa.types.others.Result.has_previous` property before calling this method.
1216+
"""
11841217
if self.has_previous:
11851218
# noinspection PyProtectedMember
11861219
response = self._wa.api._make_request(
@@ -1189,22 +1222,53 @@ def previous(self) -> Result[_T] | None:
11891222
return Result(
11901223
wa=self._wa, response=response, item_factory=self._item_factory
11911224
)
1192-
return None
1225+
return self.empty
1226+
1227+
def all(
1228+
self,
1229+
*,
1230+
sleep: float = 0.0,
1231+
) -> list[_T]:
1232+
"""
1233+
Get all results from the current page, previous pages, and next pages.
1234+
1235+
- Make sure to provide higher limit in the ``Pagination`` parameter to avoid hitting rate limits.
1236+
- Also consider using the ``sleep`` parameter to avoid hitting rate limits.
1237+
1238+
Args:
1239+
sleep: The number of seconds to sleep between requests to avoid hitting rate limits. Default is 0.0 (no sleep).
1240+
1241+
Returns:
1242+
A list of all results from the current page, previous pages, and next pages.
1243+
"""
1244+
before_data = []
1245+
after_data = []
1246+
1247+
prev = self
1248+
while prev.has_previous:
1249+
if sleep > 0:
1250+
time.sleep(sleep)
1251+
prev = prev.previous()
1252+
before_data = prev._data + before_data
1253+
1254+
next_page = self
1255+
while next_page.has_next:
1256+
if sleep > 0:
1257+
time.sleep(sleep)
1258+
next_page = next_page.next()
1259+
after_data += next_page._data
1260+
1261+
return before_data + self._data + after_data
11931262

11941263
def __iter__(self) -> Iterator[_T]:
1195-
yield from self._data
1264+
return iter(self._data)
11961265

11971266
def __len__(self) -> int:
11981267
return len(self._data)
11991268

12001269
def __getitem__(self, index: int) -> _T:
12011270
return self._data[index]
12021271

1203-
def __next__(self) -> _T:
1204-
if self._data:
1205-
return self._data.pop(0)
1206-
raise StopIteration
1207-
12081272
def __bool__(self) -> bool:
12091273
return bool(self._data)
12101274

pywa_async/types/others.py

Lines changed: 85 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import asyncio
34
from typing import TYPE_CHECKING
45

56
from pywa.types.others import * # noqa MUST BE IMPORTED FIRST
@@ -96,23 +97,50 @@ class Result(_Result[_T]):
9697
9798
Example:
9899
99-
>>> from pywa_async import WhatsApp, types
100+
>>> from pywa import WhatsApp, types
100101
>>> wa = WhatsApp(...)
101-
>>> all_blocked_users = []
102-
>>> res = await wa.get_blocked_users(pagination=types.Pagination(limit=100))
103-
>>> while True:
104-
... all_blocked_users.extend(res)
105-
... if not res.has_next:
106-
... break
107-
... res = await res.next()
108-
>>> all_blocked_users
109-
[User(...), User(...), User(...), ...]
102+
>>> res = wa.get_blocked_users(pagination=types.Pagination(limit=100))
103+
>>> for user in res:
104+
... print(user.name, user.wa_id)
105+
...
106+
>>> if res.has_next:
107+
... next_res = res.next()
108+
...
109+
>>> print(res.all())
110+
111+
Methods:
112+
next: Get the next page of results. if there is no next page, it returns empty Result.
113+
previous: Get the previous page of results. if there is no previous page, it returns empty Result.
114+
all: Get all results from the current page, previous pages, and next pages.
115+
empty: Returns an empty Result instance.
116+
117+
Properties:
118+
has_next: Check if there is a next page of results.
119+
has_previous: Check if there is a previous page of results.
120+
before: Cursor that points to the start of the page of data that has been returned.
121+
after: Cursor that points to the end of the page of data that has been returned.
110122
"""
111123

112124
_wa: WhatsAppAsync
113125

114-
async def next(self) -> Result[_T] | None:
115-
"""Get the next page of results."""
126+
@property
127+
def empty(self) -> Result[_T]:
128+
"""Returns an empty Result instance."""
129+
return Result(
130+
wa=self._wa,
131+
response={
132+
"data": [],
133+
"paging": {"next": self._next_url, "cursors": self._cursors},
134+
},
135+
item_factory=self._item_factory,
136+
)
137+
138+
async def next(self) -> Result[_T]:
139+
"""
140+
Get the next page of results. if there is no next page, it returns empty Result.
141+
142+
- Check if there is a next page using the :attr:`~pywa.types.others.Result.has_next` property before calling this method.
143+
"""
116144
if self.has_next:
117145
# noinspection PyProtectedMember
118146
response = await self._wa.api._make_request(
@@ -121,10 +149,14 @@ async def next(self) -> Result[_T] | None:
121149
return Result(
122150
wa=self._wa, response=response, item_factory=self._item_factory
123151
)
124-
return None
152+
return self.empty
153+
154+
async def previous(self) -> Result[_T]:
155+
"""
156+
Get the previous page of results. if there is no previous page, it returns empty Result.
125157
126-
async def previous(self) -> Result[_T] | None:
127-
"""Get the previous page of results."""
158+
- Check if there is a previous page using the :attr:`~pywa.types.others.Result.has_previous` property before calling this method.
159+
"""
128160
if self.has_previous:
129161
# noinspection PyProtectedMember
130162
response = await self._wa.api._make_request(
@@ -133,4 +165,41 @@ async def previous(self) -> Result[_T] | None:
133165
return Result(
134166
wa=self._wa, response=response, item_factory=self._item_factory
135167
)
136-
return None
168+
return self.empty
169+
170+
async def all(
171+
self,
172+
*,
173+
sleep: float = 0.0,
174+
) -> list[_T]:
175+
"""
176+
Get all results from the current page, previous pages, and next pages.
177+
178+
- Make sure to provide higher limit in the ``Pagination`` parameter to avoid hitting rate limits.
179+
- Also consider using the ``sleep`` parameter to avoid hitting rate limits.
180+
181+
Args:
182+
sleep: The number of seconds to sleep between requests to avoid hitting rate limits. Default is 0.0 (no sleep).
183+
184+
Returns:
185+
A list of all results from the current page, previous pages, and next pages.
186+
"""
187+
before_data = []
188+
after_data = []
189+
190+
prev = self
191+
while prev.has_previous:
192+
if sleep > 0:
193+
await asyncio.sleep(sleep)
194+
prev = await prev.previous()
195+
# noinspection PyProtectedMember
196+
before_data = prev._data + before_data
197+
198+
next_page = self
199+
while next_page.has_next:
200+
if sleep > 0:
201+
await asyncio.sleep(sleep)
202+
next_page = await next_page.next()
203+
after_data += next_page._data
204+
205+
return before_data + self._data + after_data

0 commit comments

Comments
 (0)