Post-Local sync at 2025-06-23T22:46:07Z
This commit is contained in:
parent
9d33b42020
commit
9f97801b0d
1387 changed files with 250216 additions and 117 deletions
92
.venv/lib/python3.12/site-packages/tqdm/contrib/__init__.py
Normal file
92
.venv/lib/python3.12/site-packages/tqdm/contrib/__init__.py
Normal file
|
@ -0,0 +1,92 @@
|
|||
"""
|
||||
Thin wrappers around common functions.
|
||||
|
||||
Subpackages contain potentially unstable extensions.
|
||||
"""
|
||||
from warnings import warn
|
||||
|
||||
from ..auto import tqdm as tqdm_auto
|
||||
from ..std import TqdmDeprecationWarning, tqdm
|
||||
from ..utils import ObjectWrapper
|
||||
|
||||
__author__ = {"github.com/": ["casperdcl"]}
|
||||
__all__ = ['tenumerate', 'tzip', 'tmap']
|
||||
|
||||
|
||||
class DummyTqdmFile(ObjectWrapper):
|
||||
"""Dummy file-like that will write to tqdm"""
|
||||
|
||||
def __init__(self, wrapped):
|
||||
super().__init__(wrapped)
|
||||
self._buf = []
|
||||
|
||||
def write(self, x, nolock=False):
|
||||
nl = b"\n" if isinstance(x, bytes) else "\n"
|
||||
pre, sep, post = x.rpartition(nl)
|
||||
if sep:
|
||||
blank = type(nl)()
|
||||
tqdm.write(blank.join(self._buf + [pre, sep]),
|
||||
end=blank, file=self._wrapped, nolock=nolock)
|
||||
self._buf = [post]
|
||||
else:
|
||||
self._buf.append(x)
|
||||
|
||||
def __del__(self):
|
||||
if self._buf:
|
||||
blank = type(self._buf[0])()
|
||||
try:
|
||||
tqdm.write(blank.join(self._buf), end=blank, file=self._wrapped)
|
||||
except (OSError, ValueError):
|
||||
pass
|
||||
|
||||
|
||||
def builtin_iterable(func):
|
||||
"""Returns `func`"""
|
||||
warn("This function has no effect, and will be removed in tqdm==5.0.0",
|
||||
TqdmDeprecationWarning, stacklevel=2)
|
||||
return func
|
||||
|
||||
|
||||
def tenumerate(iterable, start=0, total=None, tqdm_class=tqdm_auto, **tqdm_kwargs):
|
||||
"""
|
||||
Equivalent of `numpy.ndenumerate` or builtin `enumerate`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
tqdm_class : [default: tqdm.auto.tqdm].
|
||||
"""
|
||||
try:
|
||||
import numpy as np
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
if isinstance(iterable, np.ndarray):
|
||||
return tqdm_class(np.ndenumerate(iterable), total=total or iterable.size,
|
||||
**tqdm_kwargs)
|
||||
return enumerate(tqdm_class(iterable, total=total, **tqdm_kwargs), start)
|
||||
|
||||
|
||||
def tzip(iter1, *iter2plus, **tqdm_kwargs):
|
||||
"""
|
||||
Equivalent of builtin `zip`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
tqdm_class : [default: tqdm.auto.tqdm].
|
||||
"""
|
||||
kwargs = tqdm_kwargs.copy()
|
||||
tqdm_class = kwargs.pop("tqdm_class", tqdm_auto)
|
||||
for i in zip(tqdm_class(iter1, **kwargs), *iter2plus):
|
||||
yield i
|
||||
|
||||
|
||||
def tmap(function, *sequences, **tqdm_kwargs):
|
||||
"""
|
||||
Equivalent of builtin `map`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
tqdm_class : [default: tqdm.auto.tqdm].
|
||||
"""
|
||||
for i in tzip(*sequences, **tqdm_kwargs):
|
||||
yield function(*i)
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
26
.venv/lib/python3.12/site-packages/tqdm/contrib/bells.py
Normal file
26
.venv/lib/python3.12/site-packages/tqdm/contrib/bells.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
"""
|
||||
Even more features than `tqdm.auto` (all the bells & whistles):
|
||||
|
||||
- `tqdm.auto`
|
||||
- `tqdm.tqdm.pandas`
|
||||
- `tqdm.contrib.telegram`
|
||||
+ uses `${TQDM_TELEGRAM_TOKEN}` and `${TQDM_TELEGRAM_CHAT_ID}`
|
||||
- `tqdm.contrib.discord`
|
||||
+ uses `${TQDM_DISCORD_TOKEN}` and `${TQDM_DISCORD_CHANNEL_ID}`
|
||||
"""
|
||||
__all__ = ['tqdm', 'trange']
|
||||
import warnings
|
||||
from os import getenv
|
||||
|
||||
if getenv("TQDM_SLACK_TOKEN") and getenv("TQDM_SLACK_CHANNEL"):
|
||||
from .slack import tqdm, trange
|
||||
elif getenv("TQDM_TELEGRAM_TOKEN") and getenv("TQDM_TELEGRAM_CHAT_ID"):
|
||||
from .telegram import tqdm, trange
|
||||
elif getenv("TQDM_DISCORD_TOKEN") and getenv("TQDM_DISCORD_CHANNEL_ID"):
|
||||
from .discord import tqdm, trange
|
||||
else:
|
||||
from ..auto import tqdm, trange
|
||||
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore", category=FutureWarning)
|
||||
tqdm.pandas()
|
105
.venv/lib/python3.12/site-packages/tqdm/contrib/concurrent.py
Normal file
105
.venv/lib/python3.12/site-packages/tqdm/contrib/concurrent.py
Normal file
|
@ -0,0 +1,105 @@
|
|||
"""
|
||||
Thin wrappers around `concurrent.futures`.
|
||||
"""
|
||||
from contextlib import contextmanager
|
||||
from operator import length_hint
|
||||
from os import cpu_count
|
||||
|
||||
from ..auto import tqdm as tqdm_auto
|
||||
from ..std import TqdmWarning
|
||||
|
||||
__author__ = {"github.com/": ["casperdcl"]}
|
||||
__all__ = ['thread_map', 'process_map']
|
||||
|
||||
|
||||
@contextmanager
|
||||
def ensure_lock(tqdm_class, lock_name=""):
|
||||
"""get (create if necessary) and then restore `tqdm_class`'s lock"""
|
||||
old_lock = getattr(tqdm_class, '_lock', None) # don't create a new lock
|
||||
lock = old_lock or tqdm_class.get_lock() # maybe create a new lock
|
||||
lock = getattr(lock, lock_name, lock) # maybe subtype
|
||||
tqdm_class.set_lock(lock)
|
||||
yield lock
|
||||
if old_lock is None:
|
||||
del tqdm_class._lock
|
||||
else:
|
||||
tqdm_class.set_lock(old_lock)
|
||||
|
||||
|
||||
def _executor_map(PoolExecutor, fn, *iterables, **tqdm_kwargs):
|
||||
"""
|
||||
Implementation of `thread_map` and `process_map`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
tqdm_class : [default: tqdm.auto.tqdm].
|
||||
max_workers : [default: min(32, cpu_count() + 4)].
|
||||
chunksize : [default: 1].
|
||||
lock_name : [default: "":str].
|
||||
"""
|
||||
kwargs = tqdm_kwargs.copy()
|
||||
if "total" not in kwargs:
|
||||
kwargs["total"] = length_hint(iterables[0])
|
||||
tqdm_class = kwargs.pop("tqdm_class", tqdm_auto)
|
||||
max_workers = kwargs.pop("max_workers", min(32, cpu_count() + 4))
|
||||
chunksize = kwargs.pop("chunksize", 1)
|
||||
lock_name = kwargs.pop("lock_name", "")
|
||||
with ensure_lock(tqdm_class, lock_name=lock_name) as lk:
|
||||
# share lock in case workers are already using `tqdm`
|
||||
with PoolExecutor(max_workers=max_workers, initializer=tqdm_class.set_lock,
|
||||
initargs=(lk,)) as ex:
|
||||
return list(tqdm_class(ex.map(fn, *iterables, chunksize=chunksize), **kwargs))
|
||||
|
||||
|
||||
def thread_map(fn, *iterables, **tqdm_kwargs):
|
||||
"""
|
||||
Equivalent of `list(map(fn, *iterables))`
|
||||
driven by `concurrent.futures.ThreadPoolExecutor`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
tqdm_class : optional
|
||||
`tqdm` class to use for bars [default: tqdm.auto.tqdm].
|
||||
max_workers : int, optional
|
||||
Maximum number of workers to spawn; passed to
|
||||
`concurrent.futures.ThreadPoolExecutor.__init__`.
|
||||
[default: max(32, cpu_count() + 4)].
|
||||
"""
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
return _executor_map(ThreadPoolExecutor, fn, *iterables, **tqdm_kwargs)
|
||||
|
||||
|
||||
def process_map(fn, *iterables, **tqdm_kwargs):
|
||||
"""
|
||||
Equivalent of `list(map(fn, *iterables))`
|
||||
driven by `concurrent.futures.ProcessPoolExecutor`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
tqdm_class : optional
|
||||
`tqdm` class to use for bars [default: tqdm.auto.tqdm].
|
||||
max_workers : int, optional
|
||||
Maximum number of workers to spawn; passed to
|
||||
`concurrent.futures.ProcessPoolExecutor.__init__`.
|
||||
[default: min(32, cpu_count() + 4)].
|
||||
chunksize : int, optional
|
||||
Size of chunks sent to worker processes; passed to
|
||||
`concurrent.futures.ProcessPoolExecutor.map`. [default: 1].
|
||||
lock_name : str, optional
|
||||
Member of `tqdm_class.get_lock()` to use [default: mp_lock].
|
||||
"""
|
||||
from concurrent.futures import ProcessPoolExecutor
|
||||
if iterables and "chunksize" not in tqdm_kwargs:
|
||||
# default `chunksize=1` has poor performance for large iterables
|
||||
# (most time spent dispatching items to workers).
|
||||
longest_iterable_len = max(map(length_hint, iterables))
|
||||
if longest_iterable_len > 1000:
|
||||
from warnings import warn
|
||||
warn("Iterable length %d > 1000 but `chunksize` is not set."
|
||||
" This may seriously degrade multiprocess performance."
|
||||
" Set `chunksize=1` or more." % longest_iterable_len,
|
||||
TqdmWarning, stacklevel=2)
|
||||
if "lock_name" not in tqdm_kwargs:
|
||||
tqdm_kwargs = tqdm_kwargs.copy()
|
||||
tqdm_kwargs["lock_name"] = "mp_lock"
|
||||
return _executor_map(ProcessPoolExecutor, fn, *iterables, **tqdm_kwargs)
|
156
.venv/lib/python3.12/site-packages/tqdm/contrib/discord.py
Normal file
156
.venv/lib/python3.12/site-packages/tqdm/contrib/discord.py
Normal file
|
@ -0,0 +1,156 @@
|
|||
"""
|
||||
Sends updates to a Discord bot.
|
||||
|
||||
Usage:
|
||||
>>> from tqdm.contrib.discord import tqdm, trange
|
||||
>>> for i in trange(10, token='{token}', channel_id='{channel_id}'):
|
||||
... ...
|
||||
|
||||

|
||||
"""
|
||||
from os import getenv
|
||||
from warnings import warn
|
||||
|
||||
from requests import Session
|
||||
from requests.utils import default_user_agent
|
||||
|
||||
from ..auto import tqdm as tqdm_auto
|
||||
from ..std import TqdmWarning
|
||||
from ..version import __version__
|
||||
from .utils_worker import MonoWorker
|
||||
|
||||
__author__ = {"github.com/": ["casperdcl", "guigoruiz1"]}
|
||||
__all__ = ['DiscordIO', 'tqdm_discord', 'tdrange', 'tqdm', 'trange']
|
||||
|
||||
|
||||
class DiscordIO(MonoWorker):
|
||||
"""Non-blocking file-like IO using a Discord Bot."""
|
||||
API = "https://discord.com/api/v10"
|
||||
UA = f"tqdm (https://tqdm.github.io, {__version__}) {default_user_agent()}"
|
||||
|
||||
def __init__(self, token, channel_id):
|
||||
"""Creates a new message in the given `channel_id`."""
|
||||
super().__init__()
|
||||
self.token = token
|
||||
self.channel_id = channel_id
|
||||
self.session = Session()
|
||||
self.text = self.__class__.__name__
|
||||
self.message_id
|
||||
|
||||
@property
|
||||
def message_id(self):
|
||||
if hasattr(self, '_message_id'):
|
||||
return self._message_id
|
||||
try:
|
||||
res = self.session.post(
|
||||
f'{self.API}/channels/{self.channel_id}/messages',
|
||||
headers={'Authorization': f'Bot {self.token}', 'User-Agent': self.UA},
|
||||
json={'content': f"`{self.text}`"}).json()
|
||||
except Exception as e:
|
||||
tqdm_auto.write(str(e))
|
||||
else:
|
||||
if res.get('error_code') == 429:
|
||||
warn("Creation rate limit: try increasing `mininterval`.",
|
||||
TqdmWarning, stacklevel=2)
|
||||
else:
|
||||
self._message_id = res['id']
|
||||
return self._message_id
|
||||
|
||||
def write(self, s):
|
||||
"""Replaces internal `message_id`'s text with `s`."""
|
||||
if not s:
|
||||
s = "..."
|
||||
s = s.replace('\r', '').strip()
|
||||
if s == self.text:
|
||||
return # avoid duplicate message Bot error
|
||||
message_id = self.message_id
|
||||
if message_id is None:
|
||||
return
|
||||
self.text = s
|
||||
try:
|
||||
future = self.submit(
|
||||
self.session.patch,
|
||||
f'{self.API}/channels/{self.channel_id}/messages/{message_id}',
|
||||
headers={'Authorization': f'Bot {self.token}', 'User-Agent': self.UA},
|
||||
json={'content': f"`{self.text}`"})
|
||||
except Exception as e:
|
||||
tqdm_auto.write(str(e))
|
||||
else:
|
||||
return future
|
||||
|
||||
def delete(self):
|
||||
"""Deletes internal `message_id`."""
|
||||
try:
|
||||
future = self.submit(
|
||||
self.session.delete,
|
||||
f'{self.API}/channels/{self.channel_id}/messages/{self.message_id}',
|
||||
headers={'Authorization': f'Bot {self.token}', 'User-Agent': self.UA})
|
||||
except Exception as e:
|
||||
tqdm_auto.write(str(e))
|
||||
else:
|
||||
return future
|
||||
|
||||
|
||||
class tqdm_discord(tqdm_auto):
|
||||
"""
|
||||
Standard `tqdm.auto.tqdm` but also sends updates to a Discord Bot.
|
||||
May take a few seconds to create (`__init__`).
|
||||
|
||||
- create a discord bot (not public, no requirement of OAuth2 code
|
||||
grant, only send message permissions) & invite it to a channel:
|
||||
<https://discordpy.readthedocs.io/en/latest/discord.html>
|
||||
- copy the bot `{token}` & `{channel_id}` and paste below
|
||||
|
||||
>>> from tqdm.contrib.discord import tqdm, trange
|
||||
>>> for i in tqdm(iterable, token='{token}', channel_id='{channel_id}'):
|
||||
... ...
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
token : str, required. Discord bot token
|
||||
[default: ${TQDM_DISCORD_TOKEN}].
|
||||
channel_id : int, required. Discord channel ID
|
||||
[default: ${TQDM_DISCORD_CHANNEL_ID}].
|
||||
|
||||
See `tqdm.auto.tqdm.__init__` for other parameters.
|
||||
"""
|
||||
if not kwargs.get('disable'):
|
||||
kwargs = kwargs.copy()
|
||||
self.dio = DiscordIO(
|
||||
kwargs.pop('token', getenv('TQDM_DISCORD_TOKEN')),
|
||||
kwargs.pop('channel_id', getenv('TQDM_DISCORD_CHANNEL_ID')))
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def display(self, **kwargs):
|
||||
super().display(**kwargs)
|
||||
fmt = self.format_dict
|
||||
if fmt.get('bar_format', None):
|
||||
fmt['bar_format'] = fmt['bar_format'].replace(
|
||||
'<bar/>', '{bar:10u}').replace('{bar}', '{bar:10u}')
|
||||
else:
|
||||
fmt['bar_format'] = '{l_bar}{bar:10u}{r_bar}'
|
||||
self.dio.write(self.format_meter(**fmt))
|
||||
|
||||
def clear(self, *args, **kwargs):
|
||||
super().clear(*args, **kwargs)
|
||||
if not self.disable:
|
||||
self.dio.write("")
|
||||
|
||||
def close(self):
|
||||
if self.disable:
|
||||
return
|
||||
super().close()
|
||||
if not (self.leave or (self.leave is None and self.pos == 0)):
|
||||
self.dio.delete()
|
||||
|
||||
|
||||
def tdrange(*args, **kwargs):
|
||||
"""Shortcut for `tqdm.contrib.discord.tqdm(range(*args), **kwargs)`."""
|
||||
return tqdm_discord(range(*args), **kwargs)
|
||||
|
||||
|
||||
# Aliases
|
||||
tqdm = tqdm_discord
|
||||
trange = tdrange
|
35
.venv/lib/python3.12/site-packages/tqdm/contrib/itertools.py
Normal file
35
.venv/lib/python3.12/site-packages/tqdm/contrib/itertools.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
"""
|
||||
Thin wrappers around `itertools`.
|
||||
"""
|
||||
import itertools
|
||||
|
||||
from ..auto import tqdm as tqdm_auto
|
||||
|
||||
__author__ = {"github.com/": ["casperdcl"]}
|
||||
__all__ = ['product']
|
||||
|
||||
|
||||
def product(*iterables, **tqdm_kwargs):
|
||||
"""
|
||||
Equivalent of `itertools.product`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
tqdm_class : [default: tqdm.auto.tqdm].
|
||||
"""
|
||||
kwargs = tqdm_kwargs.copy()
|
||||
tqdm_class = kwargs.pop("tqdm_class", tqdm_auto)
|
||||
try:
|
||||
lens = list(map(len, iterables))
|
||||
except TypeError:
|
||||
total = None
|
||||
else:
|
||||
total = 1
|
||||
for i in lens:
|
||||
total *= i
|
||||
kwargs.setdefault("total", total)
|
||||
with tqdm_class(**kwargs) as t:
|
||||
it = itertools.product(*iterables)
|
||||
for i in it:
|
||||
yield i
|
||||
t.update()
|
126
.venv/lib/python3.12/site-packages/tqdm/contrib/logging.py
Normal file
126
.venv/lib/python3.12/site-packages/tqdm/contrib/logging.py
Normal file
|
@ -0,0 +1,126 @@
|
|||
"""
|
||||
Helper functionality for interoperability with stdlib `logging`.
|
||||
"""
|
||||
import logging
|
||||
import sys
|
||||
from contextlib import contextmanager
|
||||
|
||||
try:
|
||||
from typing import Iterator, List, Optional, Type # noqa: F401
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from ..std import tqdm as std_tqdm
|
||||
|
||||
|
||||
class _TqdmLoggingHandler(logging.StreamHandler):
|
||||
def __init__(
|
||||
self,
|
||||
tqdm_class=std_tqdm # type: Type[std_tqdm]
|
||||
):
|
||||
super().__init__()
|
||||
self.tqdm_class = tqdm_class
|
||||
|
||||
def emit(self, record):
|
||||
try:
|
||||
msg = self.format(record)
|
||||
self.tqdm_class.write(msg, file=self.stream)
|
||||
self.flush()
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except: # noqa pylint: disable=bare-except
|
||||
self.handleError(record)
|
||||
|
||||
|
||||
def _is_console_logging_handler(handler):
|
||||
return (isinstance(handler, logging.StreamHandler)
|
||||
and handler.stream in {sys.stdout, sys.stderr})
|
||||
|
||||
|
||||
def _get_first_found_console_logging_handler(handlers):
|
||||
for handler in handlers:
|
||||
if _is_console_logging_handler(handler):
|
||||
return handler
|
||||
|
||||
|
||||
@contextmanager
|
||||
def logging_redirect_tqdm(
|
||||
loggers=None, # type: Optional[List[logging.Logger]],
|
||||
tqdm_class=std_tqdm # type: Type[std_tqdm]
|
||||
):
|
||||
# type: (...) -> Iterator[None]
|
||||
"""
|
||||
Context manager redirecting console logging to `tqdm.write()`, leaving
|
||||
other logging handlers (e.g. log files) unaffected.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
loggers : list, optional
|
||||
Which handlers to redirect (default: [logging.root]).
|
||||
tqdm_class : optional
|
||||
|
||||
Example
|
||||
-------
|
||||
```python
|
||||
import logging
|
||||
from tqdm import trange
|
||||
from tqdm.contrib.logging import logging_redirect_tqdm
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
if __name__ == '__main__':
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
with logging_redirect_tqdm():
|
||||
for i in trange(9):
|
||||
if i == 4:
|
||||
LOG.info("console logging redirected to `tqdm.write()`")
|
||||
# logging restored
|
||||
```
|
||||
"""
|
||||
if loggers is None:
|
||||
loggers = [logging.root]
|
||||
original_handlers_list = [logger.handlers for logger in loggers]
|
||||
try:
|
||||
for logger in loggers:
|
||||
tqdm_handler = _TqdmLoggingHandler(tqdm_class)
|
||||
orig_handler = _get_first_found_console_logging_handler(logger.handlers)
|
||||
if orig_handler is not None:
|
||||
tqdm_handler.setFormatter(orig_handler.formatter)
|
||||
tqdm_handler.stream = orig_handler.stream
|
||||
logger.handlers = [
|
||||
handler for handler in logger.handlers
|
||||
if not _is_console_logging_handler(handler)] + [tqdm_handler]
|
||||
yield
|
||||
finally:
|
||||
for logger, original_handlers in zip(loggers, original_handlers_list):
|
||||
logger.handlers = original_handlers
|
||||
|
||||
|
||||
@contextmanager
|
||||
def tqdm_logging_redirect(
|
||||
*args,
|
||||
# loggers=None, # type: Optional[List[logging.Logger]]
|
||||
# tqdm=None, # type: Optional[Type[tqdm.tqdm]]
|
||||
**kwargs
|
||||
):
|
||||
# type: (...) -> Iterator[None]
|
||||
"""
|
||||
Convenience shortcut for:
|
||||
```python
|
||||
with tqdm_class(*args, **tqdm_kwargs) as pbar:
|
||||
with logging_redirect_tqdm(loggers=loggers, tqdm_class=tqdm_class):
|
||||
yield pbar
|
||||
```
|
||||
|
||||
Parameters
|
||||
----------
|
||||
tqdm_class : optional, (default: tqdm.std.tqdm).
|
||||
loggers : optional, list.
|
||||
**tqdm_kwargs : passed to `tqdm_class`.
|
||||
"""
|
||||
tqdm_kwargs = kwargs.copy()
|
||||
loggers = tqdm_kwargs.pop('loggers', None)
|
||||
tqdm_class = tqdm_kwargs.pop('tqdm_class', std_tqdm)
|
||||
with tqdm_class(*args, **tqdm_kwargs) as pbar:
|
||||
with logging_redirect_tqdm(loggers=loggers, tqdm_class=tqdm_class):
|
||||
yield pbar
|
120
.venv/lib/python3.12/site-packages/tqdm/contrib/slack.py
Normal file
120
.venv/lib/python3.12/site-packages/tqdm/contrib/slack.py
Normal file
|
@ -0,0 +1,120 @@
|
|||
"""
|
||||
Sends updates to a Slack app.
|
||||
|
||||
Usage:
|
||||
>>> from tqdm.contrib.slack import tqdm, trange
|
||||
>>> for i in trange(10, token='{token}', channel='{channel}'):
|
||||
... ...
|
||||
|
||||

|
||||
"""
|
||||
import logging
|
||||
from os import getenv
|
||||
|
||||
try:
|
||||
from slack_sdk import WebClient
|
||||
except ImportError:
|
||||
raise ImportError("Please `pip install slack-sdk`")
|
||||
|
||||
from ..auto import tqdm as tqdm_auto
|
||||
from .utils_worker import MonoWorker
|
||||
|
||||
__author__ = {"github.com/": ["0x2b3bfa0", "casperdcl"]}
|
||||
__all__ = ['SlackIO', 'tqdm_slack', 'tsrange', 'tqdm', 'trange']
|
||||
|
||||
|
||||
class SlackIO(MonoWorker):
|
||||
"""Non-blocking file-like IO using a Slack app."""
|
||||
def __init__(self, token, channel):
|
||||
"""Creates a new message in the given `channel`."""
|
||||
super().__init__()
|
||||
self.client = WebClient(token=token)
|
||||
self.text = self.__class__.__name__
|
||||
try:
|
||||
self.message = self.client.chat_postMessage(channel=channel, text=self.text)
|
||||
except Exception as e:
|
||||
tqdm_auto.write(str(e))
|
||||
self.message = None
|
||||
|
||||
def write(self, s):
|
||||
"""Replaces internal `message`'s text with `s`."""
|
||||
if not s:
|
||||
s = "..."
|
||||
s = s.replace('\r', '').strip()
|
||||
if s == self.text:
|
||||
return # skip duplicate message
|
||||
message = self.message
|
||||
if message is None:
|
||||
return
|
||||
self.text = s
|
||||
try:
|
||||
future = self.submit(self.client.chat_update, channel=message['channel'],
|
||||
ts=message['ts'], text='`' + s + '`')
|
||||
except Exception as e:
|
||||
tqdm_auto.write(str(e))
|
||||
else:
|
||||
return future
|
||||
|
||||
|
||||
class tqdm_slack(tqdm_auto):
|
||||
"""
|
||||
Standard `tqdm.auto.tqdm` but also sends updates to a Slack app.
|
||||
May take a few seconds to create (`__init__`).
|
||||
|
||||
- create a Slack app with the `chat:write` scope & invite it to a
|
||||
channel: <https://api.slack.com/authentication/basics>
|
||||
- copy the bot `{token}` & `{channel}` and paste below
|
||||
>>> from tqdm.contrib.slack import tqdm, trange
|
||||
>>> for i in tqdm(iterable, token='{token}', channel='{channel}'):
|
||||
... ...
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
token : str, required. Slack token
|
||||
[default: ${TQDM_SLACK_TOKEN}].
|
||||
channel : int, required. Slack channel
|
||||
[default: ${TQDM_SLACK_CHANNEL}].
|
||||
mininterval : float, optional.
|
||||
Minimum of [default: 1.5] to avoid rate limit.
|
||||
|
||||
See `tqdm.auto.tqdm.__init__` for other parameters.
|
||||
"""
|
||||
if not kwargs.get('disable'):
|
||||
kwargs = kwargs.copy()
|
||||
logging.getLogger("HTTPClient").setLevel(logging.WARNING)
|
||||
self.sio = SlackIO(
|
||||
kwargs.pop('token', getenv("TQDM_SLACK_TOKEN")),
|
||||
kwargs.pop('channel', getenv("TQDM_SLACK_CHANNEL")))
|
||||
kwargs['mininterval'] = max(1.5, kwargs.get('mininterval', 1.5))
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def display(self, **kwargs):
|
||||
super().display(**kwargs)
|
||||
fmt = self.format_dict
|
||||
if fmt.get('bar_format', None):
|
||||
fmt['bar_format'] = fmt['bar_format'].replace(
|
||||
'<bar/>', '`{bar:10}`').replace('{bar}', '`{bar:10u}`')
|
||||
else:
|
||||
fmt['bar_format'] = '{l_bar}`{bar:10}`{r_bar}'
|
||||
if fmt['ascii'] is False:
|
||||
fmt['ascii'] = [":black_square:", ":small_blue_diamond:", ":large_blue_diamond:",
|
||||
":large_blue_square:"]
|
||||
fmt['ncols'] = 336
|
||||
self.sio.write(self.format_meter(**fmt))
|
||||
|
||||
def clear(self, *args, **kwargs):
|
||||
super().clear(*args, **kwargs)
|
||||
if not self.disable:
|
||||
self.sio.write("")
|
||||
|
||||
|
||||
def tsrange(*args, **kwargs):
|
||||
"""Shortcut for `tqdm.contrib.slack.tqdm(range(*args), **kwargs)`."""
|
||||
return tqdm_slack(range(*args), **kwargs)
|
||||
|
||||
|
||||
# Aliases
|
||||
tqdm = tqdm_slack
|
||||
trange = tsrange
|
153
.venv/lib/python3.12/site-packages/tqdm/contrib/telegram.py
Normal file
153
.venv/lib/python3.12/site-packages/tqdm/contrib/telegram.py
Normal file
|
@ -0,0 +1,153 @@
|
|||
"""
|
||||
Sends updates to a Telegram bot.
|
||||
|
||||
Usage:
|
||||
>>> from tqdm.contrib.telegram import tqdm, trange
|
||||
>>> for i in trange(10, token='{token}', chat_id='{chat_id}'):
|
||||
... ...
|
||||
|
||||

|
||||
"""
|
||||
from os import getenv
|
||||
from warnings import warn
|
||||
|
||||
from requests import Session
|
||||
|
||||
from ..auto import tqdm as tqdm_auto
|
||||
from ..std import TqdmWarning
|
||||
from .utils_worker import MonoWorker
|
||||
|
||||
__author__ = {"github.com/": ["casperdcl"]}
|
||||
__all__ = ['TelegramIO', 'tqdm_telegram', 'ttgrange', 'tqdm', 'trange']
|
||||
|
||||
|
||||
class TelegramIO(MonoWorker):
|
||||
"""Non-blocking file-like IO using a Telegram Bot."""
|
||||
API = 'https://api.telegram.org/bot'
|
||||
|
||||
def __init__(self, token, chat_id):
|
||||
"""Creates a new message in the given `chat_id`."""
|
||||
super().__init__()
|
||||
self.token = token
|
||||
self.chat_id = chat_id
|
||||
self.session = Session()
|
||||
self.text = self.__class__.__name__
|
||||
self.message_id
|
||||
|
||||
@property
|
||||
def message_id(self):
|
||||
if hasattr(self, '_message_id'):
|
||||
return self._message_id
|
||||
try:
|
||||
res = self.session.post(
|
||||
self.API + '%s/sendMessage' % self.token,
|
||||
data={'text': '`' + self.text + '`', 'chat_id': self.chat_id,
|
||||
'parse_mode': 'MarkdownV2'}).json()
|
||||
except Exception as e:
|
||||
tqdm_auto.write(str(e))
|
||||
else:
|
||||
if res.get('error_code') == 429:
|
||||
warn("Creation rate limit: try increasing `mininterval`.",
|
||||
TqdmWarning, stacklevel=2)
|
||||
else:
|
||||
self._message_id = res['result']['message_id']
|
||||
return self._message_id
|
||||
|
||||
def write(self, s):
|
||||
"""Replaces internal `message_id`'s text with `s`."""
|
||||
if not s:
|
||||
s = "..."
|
||||
s = s.replace('\r', '').strip()
|
||||
if s == self.text:
|
||||
return # avoid duplicate message Bot error
|
||||
message_id = self.message_id
|
||||
if message_id is None:
|
||||
return
|
||||
self.text = s
|
||||
try:
|
||||
future = self.submit(
|
||||
self.session.post, self.API + '%s/editMessageText' % self.token,
|
||||
data={'text': '`' + s + '`', 'chat_id': self.chat_id,
|
||||
'message_id': message_id, 'parse_mode': 'MarkdownV2'})
|
||||
except Exception as e:
|
||||
tqdm_auto.write(str(e))
|
||||
else:
|
||||
return future
|
||||
|
||||
def delete(self):
|
||||
"""Deletes internal `message_id`."""
|
||||
try:
|
||||
future = self.submit(
|
||||
self.session.post, self.API + '%s/deleteMessage' % self.token,
|
||||
data={'chat_id': self.chat_id, 'message_id': self.message_id})
|
||||
except Exception as e:
|
||||
tqdm_auto.write(str(e))
|
||||
else:
|
||||
return future
|
||||
|
||||
|
||||
class tqdm_telegram(tqdm_auto):
|
||||
"""
|
||||
Standard `tqdm.auto.tqdm` but also sends updates to a Telegram Bot.
|
||||
May take a few seconds to create (`__init__`).
|
||||
|
||||
- create a bot <https://core.telegram.org/bots#6-botfather>
|
||||
- copy its `{token}`
|
||||
- add the bot to a chat and send it a message such as `/start`
|
||||
- go to <https://api.telegram.org/bot`{token}`/getUpdates> to find out
|
||||
the `{chat_id}`
|
||||
- paste the `{token}` & `{chat_id}` below
|
||||
|
||||
>>> from tqdm.contrib.telegram import tqdm, trange
|
||||
>>> for i in tqdm(iterable, token='{token}', chat_id='{chat_id}'):
|
||||
... ...
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
token : str, required. Telegram token
|
||||
[default: ${TQDM_TELEGRAM_TOKEN}].
|
||||
chat_id : str, required. Telegram chat ID
|
||||
[default: ${TQDM_TELEGRAM_CHAT_ID}].
|
||||
|
||||
See `tqdm.auto.tqdm.__init__` for other parameters.
|
||||
"""
|
||||
if not kwargs.get('disable'):
|
||||
kwargs = kwargs.copy()
|
||||
self.tgio = TelegramIO(
|
||||
kwargs.pop('token', getenv('TQDM_TELEGRAM_TOKEN')),
|
||||
kwargs.pop('chat_id', getenv('TQDM_TELEGRAM_CHAT_ID')))
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def display(self, **kwargs):
|
||||
super().display(**kwargs)
|
||||
fmt = self.format_dict
|
||||
if fmt.get('bar_format', None):
|
||||
fmt['bar_format'] = fmt['bar_format'].replace(
|
||||
'<bar/>', '{bar:10u}').replace('{bar}', '{bar:10u}')
|
||||
else:
|
||||
fmt['bar_format'] = '{l_bar}{bar:10u}{r_bar}'
|
||||
self.tgio.write(self.format_meter(**fmt))
|
||||
|
||||
def clear(self, *args, **kwargs):
|
||||
super().clear(*args, **kwargs)
|
||||
if not self.disable:
|
||||
self.tgio.write("")
|
||||
|
||||
def close(self):
|
||||
if self.disable:
|
||||
return
|
||||
super().close()
|
||||
if not (self.leave or (self.leave is None and self.pos == 0)):
|
||||
self.tgio.delete()
|
||||
|
||||
|
||||
def ttgrange(*args, **kwargs):
|
||||
"""Shortcut for `tqdm.contrib.telegram.tqdm(range(*args), **kwargs)`."""
|
||||
return tqdm_telegram(range(*args), **kwargs)
|
||||
|
||||
|
||||
# Aliases
|
||||
tqdm = tqdm_telegram
|
||||
trange = ttgrange
|
|
@ -0,0 +1,38 @@
|
|||
"""
|
||||
IO/concurrency helpers for `tqdm.contrib`.
|
||||
"""
|
||||
from collections import deque
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
from ..auto import tqdm as tqdm_auto
|
||||
|
||||
__author__ = {"github.com/": ["casperdcl"]}
|
||||
__all__ = ['MonoWorker']
|
||||
|
||||
|
||||
class MonoWorker(object):
|
||||
"""
|
||||
Supports one running task and one waiting task.
|
||||
The waiting task is the most recent submitted (others are discarded).
|
||||
"""
|
||||
def __init__(self):
|
||||
self.pool = ThreadPoolExecutor(max_workers=1)
|
||||
self.futures = deque([], 2)
|
||||
|
||||
def submit(self, func, *args, **kwargs):
|
||||
"""`func(*args, **kwargs)` may replace currently waiting task."""
|
||||
futures = self.futures
|
||||
if len(futures) == futures.maxlen:
|
||||
running = futures.popleft()
|
||||
if not running.done():
|
||||
if len(futures): # clear waiting
|
||||
waiting = futures.pop()
|
||||
waiting.cancel()
|
||||
futures.appendleft(running) # re-insert running
|
||||
try:
|
||||
waiting = self.pool.submit(func, *args, **kwargs)
|
||||
except Exception as e:
|
||||
tqdm_auto.write(str(e))
|
||||
else:
|
||||
futures.append(waiting)
|
||||
return waiting
|
Loading…
Add table
Add a link
Reference in a new issue