aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/tqdm/contrib
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/tqdm/contrib')
-rw-r--r--.venv/lib/python3.12/site-packages/tqdm/contrib/__init__.py92
-rw-r--r--.venv/lib/python3.12/site-packages/tqdm/contrib/bells.py26
-rw-r--r--.venv/lib/python3.12/site-packages/tqdm/contrib/concurrent.py105
-rw-r--r--.venv/lib/python3.12/site-packages/tqdm/contrib/discord.py156
-rw-r--r--.venv/lib/python3.12/site-packages/tqdm/contrib/itertools.py35
-rw-r--r--.venv/lib/python3.12/site-packages/tqdm/contrib/logging.py126
-rw-r--r--.venv/lib/python3.12/site-packages/tqdm/contrib/slack.py120
-rw-r--r--.venv/lib/python3.12/site-packages/tqdm/contrib/telegram.py153
-rw-r--r--.venv/lib/python3.12/site-packages/tqdm/contrib/utils_worker.py38
9 files changed, 851 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/tqdm/contrib/__init__.py b/.venv/lib/python3.12/site-packages/tqdm/contrib/__init__.py
new file mode 100644
index 00000000..d059461f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/tqdm/contrib/__init__.py
@@ -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)
diff --git a/.venv/lib/python3.12/site-packages/tqdm/contrib/bells.py b/.venv/lib/python3.12/site-packages/tqdm/contrib/bells.py
new file mode 100644
index 00000000..5b8f4b9e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/tqdm/contrib/bells.py
@@ -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()
diff --git a/.venv/lib/python3.12/site-packages/tqdm/contrib/concurrent.py b/.venv/lib/python3.12/site-packages/tqdm/contrib/concurrent.py
new file mode 100644
index 00000000..cd81d622
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/tqdm/contrib/concurrent.py
@@ -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)
diff --git a/.venv/lib/python3.12/site-packages/tqdm/contrib/discord.py b/.venv/lib/python3.12/site-packages/tqdm/contrib/discord.py
new file mode 100644
index 00000000..574baa84
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/tqdm/contrib/discord.py
@@ -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}'):
+... ...
+
+![screenshot](https://tqdm.github.io/img/screenshot-discord.png)
+"""
+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
diff --git a/.venv/lib/python3.12/site-packages/tqdm/contrib/itertools.py b/.venv/lib/python3.12/site-packages/tqdm/contrib/itertools.py
new file mode 100644
index 00000000..e67651a4
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/tqdm/contrib/itertools.py
@@ -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()
diff --git a/.venv/lib/python3.12/site-packages/tqdm/contrib/logging.py b/.venv/lib/python3.12/site-packages/tqdm/contrib/logging.py
new file mode 100644
index 00000000..e06febe3
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/tqdm/contrib/logging.py
@@ -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
diff --git a/.venv/lib/python3.12/site-packages/tqdm/contrib/slack.py b/.venv/lib/python3.12/site-packages/tqdm/contrib/slack.py
new file mode 100644
index 00000000..9bca8ee9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/tqdm/contrib/slack.py
@@ -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}'):
+... ...
+
+![screenshot](https://tqdm.github.io/img/screenshot-slack.png)
+"""
+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
diff --git a/.venv/lib/python3.12/site-packages/tqdm/contrib/telegram.py b/.venv/lib/python3.12/site-packages/tqdm/contrib/telegram.py
new file mode 100644
index 00000000..01915180
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/tqdm/contrib/telegram.py
@@ -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}'):
+... ...
+
+![screenshot](https://tqdm.github.io/img/screenshot-telegram.gif)
+"""
+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
diff --git a/.venv/lib/python3.12/site-packages/tqdm/contrib/utils_worker.py b/.venv/lib/python3.12/site-packages/tqdm/contrib/utils_worker.py
new file mode 100644
index 00000000..2a03a2a8
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/tqdm/contrib/utils_worker.py
@@ -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