-
Notifications
You must be signed in to change notification settings - Fork 0
/
utils.py
55 lines (42 loc) · 1.48 KB
/
utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import logging
import time
from datetime import timedelta
from functools import wraps
import anyio
from httpx import AsyncClient, Timeout
from config import USER_AGENT
HTTP = AsyncClient(
headers={'User-Agent': USER_AGENT},
timeout=Timeout(60, connect=15),
follow_redirects=True,
)
def retry_exponential(timeout: timedelta | float | None, *, start: float = 1):
if timeout is None:
timeout_seconds = float('inf')
elif isinstance(timeout, timedelta):
timeout_seconds = timeout.total_seconds()
else:
timeout_seconds = timeout
def decorator(func):
@wraps(func)
async def wrapper(*args, **kwargs):
ts = time.perf_counter()
sleep = start
while True:
try:
return await func(*args, **kwargs)
except Exception:
logging.warning('%s failed', func.__qualname__, exc_info=True)
if (time.perf_counter() + sleep) - ts > timeout_seconds:
raise
await anyio.sleep(sleep)
sleep = min(sleep * 2, 4 * 3600) # max 4 hours
return wrapper
return decorator
def abbreviate(num: int) -> str:
for suffix, divisor in (('m', 1_000_000), ('k', 1_000)):
if num >= divisor:
return f'{num / divisor:.1f}{suffix}'
return str(num)
def get_wikimedia_commons_url(path: str) -> str:
return f'https://commons.wikimedia.org/wiki/{path}'