Skip to content

Commit

Permalink
Merge pull request #1 from FasterSpeeding/task/is-depleted
Browse files Browse the repository at this point in the history
Add is depleted property to Backoff + typing fixes and pagination refactors
  • Loading branch information
FasterSpeeding authored Nov 22, 2020
2 parents 3f360e6 + bae763e commit 59d56b3
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 13 deletions.
2 changes: 1 addition & 1 deletion yuyo/about.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,5 @@
__issue_tracker__ = "https://github.com/FasterSpeeding/Yuyo/issues"
__license__ = "BSD"
__url__ = "https://github.com/FasterSpeeding/Yuyo"
__version__ = "0.0.2"
__version__ = "0.0.3.a0"
__git_sha1__ = "HEAD"
28 changes: 22 additions & 6 deletions yuyo/backoff.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
import types


ErrorManagerT = typing.TypeVar("ErrorManagerT", bound="ErrorManager")


class Backoff:
"""Used to exponentially backoff asynchronously.
Expand All @@ -55,8 +58,8 @@ class Backoff:
this will either back off for the time passed to `Backoff.set_next_backoff`
if applicable or a time calculated exponentially.
Parameters
----------
Other Parameters
----------------
max_retries : typing.Optional[builtins.int]
The maximum amount of times this should iterate for between resets.
If left as `builtins.None` then this iterator will be unlimited.
Expand Down Expand Up @@ -149,7 +152,7 @@ def __aiter__(self) -> Backoff:
return self

async def __anext__(self) -> None:
if self._finished or self._max_retries is not None and self._max_retries == self._retries:
if self._finished or self.is_depleted:
self._finished = False
raise StopAsyncIteration

Expand All @@ -168,6 +171,20 @@ async def __anext__(self) -> None:
self._retries += 1
await asyncio.sleep(backoff_)

@property
def is_depleted(self) -> bool:
"""Whether "max_retries" has been reached.
This can be used to workout whether the loop was explicitly broken out
of using `Backoff.finish`/`break` or if it hit "max_retries".
Returns
-------
bool
If "max_retries" has been hit.
"""
return self._max_retries is not None and self._max_retries == self._retries

async def backoff(self, backoff_: typing.Optional[float], /) -> None:
"""Sleep for the provided backoff or for the next exponent.
Expand All @@ -192,7 +209,6 @@ async def backoff(self, backoff_: typing.Optional[float], /) -> None:

def finish(self) -> None:
"""Mark the iterator as finished to break out of the current loop."""
self.reset()
self._finished = True

def reset(self) -> None:
Expand Down Expand Up @@ -304,10 +320,10 @@ def clear_rules(self) -> None:
self._rules.clear()

def with_rule(
self,
self: ErrorManagerT,
exceptions: typing.Iterable[typing.Type[BaseException]],
result: typing.Callable[[typing.Any], typing.Optional[bool]],
) -> ErrorManager:
) -> ErrorManagerT:
"""Add a rule to this exception context manager.
Parameters
Expand Down
29 changes: 23 additions & 6 deletions yuyo/paginaton.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@
from hikari import users


LEFT_DOUBLE_TRIANGLE: typing.Final[emojis.UnicodeEmoji] = emojis.UnicodeEmoji(
"\N{BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}\N{VARIATION SELECTOR-16}"
)
"""The emoji used to go back to the first entry."""
LEFT_TRIANGLE: typing.Final[emojis.UnicodeEmoji] = emojis.UnicodeEmoji(
"\N{BLACK LEFT-POINTING TRIANGLE}\N{VARIATION SELECTOR-16}"
)
Expand All @@ -70,8 +74,8 @@
"\N{BLACK RIGHT-POINTING TRIANGLE}\N{VARIATION SELECTOR-16}"
)
"""The emoji used to continue to the next entry."""
SKULL_AND_CROSSBONES: typing.Final[emojis.UnicodeEmoji] = emojis.UnicodeEmoji(
"\N{SKULL AND CROSSBONES}\N{VARIATION SELECTOR-16}"
RIGHT_DOUBLE_TRIANGLE: typing.Final[emojis.UnicodeEmoji] = emojis.UnicodeEmoji(
"\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}\N{VARIATION SELECTOR-16}"
)
"""The emoji used for the lesser-enabled skip to last entry button."""
END = "END"
Expand Down Expand Up @@ -399,10 +403,11 @@ def __init__(
typing.Union[emojis.Emoji, snowflakes.Snowflake],
typing.Callable[[], typing.Coroutine[typing.Any, typing.Any, typing.Union[EntryT, None, str]]],
] = {
LEFT_DOUBLE_TRIANGLE: self._on_first,
LEFT_TRIANGLE: self._on_previous,
STOP_SQUARE: self._on_disable,
RIGHT_TRIANGLE: self._on_next,
SKULL_AND_CROSSBONES: self._on_last,
RIGHT_DOUBLE_TRIANGLE: self._on_last,
}
self._index = 0
self._iterator = iterator
Expand Down Expand Up @@ -548,13 +553,15 @@ async def open(
*,
message: typing.Optional[snowflakes.SnowflakeishOr[messages.Message]] = None,
add_reactions: bool = True,
max_retries: int = 5,
max_backoff: float = 2.0,
) -> typing.Optional[messages.Message]:
# <<inherited docstring from AbstractPaginator>>.
created_message: typing.Optional[messages.Message] = None
if self._message_id is not None:
return None

retry = backoff.Backoff()
retry = backoff.Backoff(max_retries=max_retries - 1, maximum=max_backoff)
if message is None:
entry = await self._on_next()

Expand All @@ -569,6 +576,9 @@ async def open(
message = created_message.id

except errors.RateLimitedError as exc:
if exc.retry_after > max_backoff:
raise

retry.set_next_backoff(exc.retry_after)

except errors.InternalServerError:
Expand All @@ -577,12 +587,13 @@ async def open(
else:
break

retry.reset()
assert message is not None # "Mypy doesn't quite handle this scoping properly"
else:
message = await self._rest.rest.create_message(self._channel_id, content=entry[0], embed=entry[1])

message = snowflakes.Snowflake(message)
self._message_id = message
for emoji in self._triggers:
retry.reset()
async for _ in retry:
try:
await self._rest.rest.add_reaction(self._channel_id, message, emoji)
Expand All @@ -592,6 +603,9 @@ async def open(
raise

except errors.RateLimitedError as exc:
if exc.retry_after > max_backoff:
raise

retry.set_next_backoff(exc.retry_after)

except errors.InternalServerError:
Expand All @@ -600,6 +614,9 @@ async def open(
else:
break

else:
await self._rest.rest.add_reaction(self._channel_id, message, emoji)

return created_message

async def on_reaction_event(self, emoji: emojis.Emoji, user_id: snowflakes.Snowflake) -> typing.Optional[str]:
Expand Down

0 comments on commit 59d56b3

Please sign in to comment.