from __future__ import annotations
from typing import Union
__all__ = (
"Rate",
"Seconds",
"Minutes",
"Hours",
"Days",
"Weeks",
"Months"
)
[docs]class RateGroup:
__slots__ = ("_data",)
_data: list[Rate]
def __init__(self):
self._data = []
def __or__(self, other) -> RateGroup:
if isinstance(other, Rate):
self._data.append(other)
return self
elif isinstance(other, self.__class__):
self._data = self._data + other._data
return self
return NotImplemented
# __ror__ wouldn't be called
# unless someone is using it wrong or
# is dealing with uprate's internals
__ror__ = __or__
[docs]class Rate:
"""A rate consisting of uses and time period
Used to define rates for ratelimits
with syntatical expressions.
You should refrain from creating your own Rate objects
and instead use the objects provided in the library.
Attributes
----------
uses: int
Number of uses per time period
period: float
The time period of the rate in seconds
"""
__slots__ = ("uses", "period")
uses: int
period: float
def __init__(self, uses: int, period: float) -> None:
self.uses = uses
self.period = period
def __call__(self, magnitude: float) -> Rate:
return self.__class__(self.uses, self.period * magnitude)
def __or__(self, other: Union[Rate, RateGroup]) -> RateGroup:
if isinstance(other, RateGroup):
return other | self
elif isinstance(other, self.__class__):
return RateGroup() | self | other
return NotImplemented
__ror__ = __or__
def __rtruediv__(self, other: int) -> Rate:
return self.__class__(other, self.period)
def __mul__(self, other: float) -> Rate:
# Let multipling a Rate increase the time period
if isinstance(other, (float, int)):
return self.__class__(self.uses, self.period * other)
return NotImplemented
__rmul__ = __mul__
def __truediv__(self, other: float) -> Rate:
# Let dividing a Rate decrease the time period
if isinstance(other, (float, int)):
return self.__class__(self.uses, self.period / other)
return NotImplemented
def __add__(self, other: Rate) -> Rate:
if isinstance(other, self.__class__):
if other.uses != 1 or self.uses != 1:
raise ValueError("Cannot 'add' two rates which have uses other than one")
return self.__class__(1, self.period + other.period)
return NotImplemented
__radd__ = __add__
# object has other as object
def __eq__(self, other) -> bool:
if isinstance(other, self.__class__):
return self.period == other.period and self.uses == other.uses
return NotImplemented
def __ne__(self, other) -> bool:
if isinstance(other, self.__class__):
return not (self == other)
return False
def __hash__(self) -> int:
# This might be a bad idea
return hash((self.uses, self.period))
def __str__(self) -> str:
return f"{self.uses}/{self.period}"
Seconds = Rate(1, 1)
"""A Rate of 1 use / 1 Second
Used in syntatical expression to define rate
Example
-------
.. code-block:: python
assert 2 / Seconds(3) == 2 / (3 * Seconds)
...
# 30 uses per 2 min 30 sec but also 2 uses per 2 seconds
api_rate: uprate.rate.RateGroup = 2/Seconds(2) | 30/(2 * Minutes + 30 * Seconds)
"""
Minutes = 60 * Seconds
"""Rate of 1 use / 1 Minute"""
Hours = 60 * Minutes
"""Rate of 1 use / 1 Hour"""
Days = 24 * Hours
"""Rate of 1 use / 1 Day"""
Weeks = 7 * Days
"""Rate of 1 use / 1 Week"""
Months = 30 * Days
"""Rate of 1 use / 1 Month
Note
----
If you need larger time periods you can create them by
.. code-block:: python
Year = Rate(1, 60 * 60 * 24 * 365)
# Proceed to use like other unitary use rates
rate = 5 / Year(2) # 5 uses per 2 years
"""