diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 1a7be1879..008292495 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,3 +1,8 @@ +- [ ] I have marked all applicable categories: + + [ ] exception-raising bug + + [ ] visual output bug + + [ ] documentation request (i.e. "X is missing from the documentation." If instead I want to ask "how to use X?" I understand [StackOverflow#tqdm] is more appropriate) + + [ ] new feature request - [ ] I have visited the [source website], and in particular read the [known issues] - [ ] I have searched through the [issue tracker] for duplicates @@ -11,3 +16,4 @@ [source website]: https://github.com/tqdm/tqdm/ [known issues]: https://github.com/tqdm/tqdm/#faq-and-known-issues [issue tracker]: https://github.com/tqdm/tqdm/issues?q= + [StackOverflow#tqdm]: https://stackoverflow.com/questions/tagged/tqdm diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 9ab758cbf..4c7471d8b 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,3 +1,12 @@ +- [ ] I have marked all applicable categories: + + [ ] exception-raising fix + + [ ] visual output fix + + [ ] documentation modification + + [ ] new feature +- [ ] If applicable, I have mentioned the relevant/related issue(s) + +Less important but also useful: + - [ ] I have visited the [source website], and in particular read the [known issues] - [ ] I have searched through the [issue tracker] for duplicates @@ -7,7 +16,6 @@ import tqdm, sys print(tqdm.__version__, sys.version, sys.platform) ``` -- [ ] If applicable, I have mentioned the relevant/related issue(s) [source website]: https://github.com/tqdm/tqdm/ [known issues]: https://github.com/tqdm/tqdm/#faq-and-known-issues diff --git a/.meta/.readme.rst b/.meta/.readme.rst index a6768b71c..65690c75d 100644 --- a/.meta/.readme.rst +++ b/.meta/.readme.rst @@ -116,6 +116,16 @@ Latest Docker release docker pull tqdm/tqdm docker run -i --rm tqdm/tqdm --help +Other +~~~~~ + +There are other (unofficial) places where ``tqdm`` may be downloaded, particularly for CLI use: + +|Repology| + +.. |Repology| image:: https://repology.org/badge/tiny-repos/python:tqdm.svg + :target: https://repology.org/project/python:tqdm/versions + Changelog --------- @@ -274,7 +284,7 @@ of a neat one-line progress bar. ``tqdm(zip(a, b))`` should be replaced with ``zip(tqdm(a), b)`` or even ``zip(tqdm(a), tqdm(b))``. - `Hanging pipes in python2 `__: - when using ``tqdm`` on the CLI, you may need to use python 3.5+ for correct + when using ``tqdm`` on the CLI, you may need to use Python 3.5+ for correct buffering. If you come across any other difficulties, browse and file |GitHub-Issues|. @@ -478,7 +488,8 @@ available to keep nested bars on their respective lines. For manual control over positioning (e.g. for multi-processing use), you may specify ``position=n`` where ``n=0`` for the outermost bar, -``n=1`` for the next, and so on: +``n=1`` for the next, and so on. However, it's best to check if `tqdm` can work +without manual `position` first. .. code:: python @@ -497,11 +508,10 @@ you may specify ``position=n`` where ``n=0`` for the outermost bar, if __name__ == '__main__': freeze_support() # for Windows support - p = Pool(len(L), initializer=tqdm.set_lock, initargs=(tqdm.get_lock(),)) + p = Pool(initializer=tqdm.set_lock, initargs=(tqdm.get_lock(),)) p.map(progresser, L) -Note that in python 3, threads do not require manual positioning, -and ``tqdm.write`` is safe to use: +Note that in Python 3, ``tqdm.write`` is thread-safe: .. code:: python @@ -837,7 +847,7 @@ The monitor thread may be disabled application-wide by setting Contributions ------------- -|GitHub-Commits| |GitHub-Issues| |GitHub-PRs| |OpenHub-Status| |GitHub-Contributions| +|GitHub-Commits| |GitHub-Issues| |GitHub-PRs| |OpenHub-Status| |GitHub-Contributions| |CII Best Practices| All source code is hosted on `GitHub `__. Contributions are welcome. @@ -896,6 +906,8 @@ Citation information: |DOI| (publication), |DOI-code| (code) :target: https://codecov.io/gh/tqdm/tqdm .. |Codacy-Grade| image:: https://api.codacy.com/project/badge/Grade/3f965571598f44549c7818f29cdcf177 :target: https://www.codacy.com/app/tqdm/tqdm/dashboard +.. |CII Best Practices| image:: https://bestpractices.coreinfrastructure.org/projects/3264/badge + :target: https://bestpractices.coreinfrastructure.org/projects/3264 .. |GitHub-Status| image:: https://img.shields.io/github/tag/tqdm/tqdm.svg?maxAge=86400&logo=github&logoColor=white :target: https://github.com/tqdm/tqdm/releases .. |GitHub-Forks| image:: https://img.shields.io/github/forks/tqdm/tqdm.svg?logo=github&logoColor=white diff --git a/README.rst b/README.rst index 534475943..22fbcb2df 100644 --- a/README.rst +++ b/README.rst @@ -116,6 +116,16 @@ Latest Docker release docker pull tqdm/tqdm docker run -i --rm tqdm/tqdm --help +Other +~~~~~ + +There are other (unofficial) places where ``tqdm`` may be downloaded, particularly for CLI use: + +|Repology| + +.. |Repology| image:: https://repology.org/badge/tiny-repos/python:tqdm.svg + :target: https://repology.org/project/python:tqdm/versions + Changelog --------- @@ -274,7 +284,7 @@ of a neat one-line progress bar. ``tqdm(zip(a, b))`` should be replaced with ``zip(tqdm(a), b)`` or even ``zip(tqdm(a), tqdm(b))``. - `Hanging pipes in python2 `__: - when using ``tqdm`` on the CLI, you may need to use python 3.5+ for correct + when using ``tqdm`` on the CLI, you may need to use Python 3.5+ for correct buffering. If you come across any other difficulties, browse and file |GitHub-Issues|. @@ -644,7 +654,8 @@ available to keep nested bars on their respective lines. For manual control over positioning (e.g. for multi-processing use), you may specify ``position=n`` where ``n=0`` for the outermost bar, -``n=1`` for the next, and so on: +``n=1`` for the next, and so on. However, it's best to check if `tqdm` can work +without manual `position` first. .. code:: python @@ -663,11 +674,10 @@ you may specify ``position=n`` where ``n=0`` for the outermost bar, if __name__ == '__main__': freeze_support() # for Windows support - p = Pool(len(L), initializer=tqdm.set_lock, initargs=(tqdm.get_lock(),)) + p = Pool(initializer=tqdm.set_lock, initargs=(tqdm.get_lock(),)) p.map(progresser, L) -Note that in python 3, threads do not require manual positioning, -and ``tqdm.write`` is safe to use: +Note that in Python 3, ``tqdm.write`` is thread-safe: .. code:: python @@ -1003,7 +1013,7 @@ The monitor thread may be disabled application-wide by setting Contributions ------------- -|GitHub-Commits| |GitHub-Issues| |GitHub-PRs| |OpenHub-Status| |GitHub-Contributions| +|GitHub-Commits| |GitHub-Issues| |GitHub-PRs| |OpenHub-Status| |GitHub-Contributions| |CII Best Practices| All source code is hosted on `GitHub `__. Contributions are welcome. @@ -1062,6 +1072,8 @@ Citation information: |DOI| (publication), |DOI-code| (code) :target: https://codecov.io/gh/tqdm/tqdm .. |Codacy-Grade| image:: https://api.codacy.com/project/badge/Grade/3f965571598f44549c7818f29cdcf177 :target: https://www.codacy.com/app/tqdm/tqdm/dashboard +.. |CII Best Practices| image:: https://bestpractices.coreinfrastructure.org/projects/3264/badge + :target: https://bestpractices.coreinfrastructure.org/projects/3264 .. |GitHub-Status| image:: https://img.shields.io/github/tag/tqdm/tqdm.svg?maxAge=86400&logo=github&logoColor=white :target: https://github.com/tqdm/tqdm/releases .. |GitHub-Forks| image:: https://img.shields.io/github/forks/tqdm/tqdm.svg?logo=github&logoColor=white diff --git a/examples/parallel_bars.py b/examples/parallel_bars.py index b62020a16..e49a2775f 100644 --- a/examples/parallel_bars.py +++ b/examples/parallel_bars.py @@ -1,38 +1,41 @@ from __future__ import print_function from time import sleep from tqdm import tqdm, trange +from random import random from multiprocessing import Pool, freeze_support from concurrent.futures import ThreadPoolExecutor from functools import partial import sys +NUM_SUBITERS = 9 +PY2 = sys.version_info[:1] <= (2,) -L = list(range(9))[::-1] - -def progresser(n, auto_position=False): - interval = 0.001 / (len(L) - n + 2) +def progresser(n, auto_position=True, write_safe=False): + interval = random() * 0.002 / (NUM_SUBITERS - n + 2) total = 5000 text = "#{}, est. {:<04.2}s".format(n, interval * total) for _ in tqdm(range(total), desc=text, position=None if auto_position else n): sleep(interval) # NB: may not clear instances with higher `position` upon completion # since this worker may not know about other bars #796 - if auto_position: + if write_safe: # we think we know about other bars (currently only py3 threading) if n == 6: tqdm.write("n == 6 completed") -if sys.version_info[:1] > (2,): - progresser_thread = partial(progresser, auto_position=True) -else: - progresser_thread = progresser - if __name__ == '__main__': freeze_support() # for Windows support + L = list(range(NUM_SUBITERS))[::-1] + + print("Manual nesting") + for i in trange(16, desc="1"): + for _ in trange(16, desc="2 @ %d" % i, leave=i % 2): + sleep(0.01) + print("Multi-processing") - p = Pool(len(L), initializer=tqdm.set_lock, initargs=(tqdm.get_lock(),)) + p = Pool(initializer=tqdm.set_lock, initargs=(tqdm.get_lock(),)) p.map(progresser, L) # unfortunately need ncols @@ -41,10 +44,6 @@ def progresser(n, auto_position=False): ncols = t.ncols or 80 print(("{msg:<{ncols}}").format(msg="Multi-threading", ncols=ncols)) - with ThreadPoolExecutor(4) as p: + with ThreadPoolExecutor() as p: + progresser_thread = partial(progresser, write_safe=not PY2) p.map(progresser_thread, L) - - print("Manual nesting") - for i in trange(16, desc="1"): - for _ in trange(16, desc="2 @ %d" % i, leave=i % 2): - sleep(0.01) diff --git a/examples/tqdm_wget.py b/examples/tqdm_wget.py index e49039b3c..7e9f6c53a 100644 --- a/examples/tqdm_wget.py +++ b/examples/tqdm_wget.py @@ -48,9 +48,10 @@ def update_to(b=1, bsize=1, tsize=None): bsize : int, optional Size of each block (in tqdm units) [default: 1]. tsize : int, optional - Total size (in tqdm units). If [default: None] remains unchanged. + Total size (in tqdm units). If [default: None] or -1, + remains unchanged. """ - if tsize is not None: + if tsize not in (None, -1): t.total = tsize t.update((b - last_b[0]) * bsize) last_b[0] = b diff --git a/tqdm/__init__.py b/tqdm/__init__.py index 9e682f02d..670d6457e 100644 --- a/tqdm/__init__.py +++ b/tqdm/__init__.py @@ -24,7 +24,7 @@ def tqdm_notebook(*args, **kwargs): # pragma: no cover from warnings import warn warn("This function will be removed in tqdm==5.0.0\n" "Please use `tqdm.notebook.tqdm` instead of `tqdm.tqdm_notebook`", - TqdmDeprecationWarning) + TqdmDeprecationWarning, stacklevel=2) return _tqdm_notebook(*args, **kwargs) @@ -36,5 +36,5 @@ def tnrange(*args, **kwargs): # pragma: no cover from .notebook import trange as _tnrange from warnings import warn warn("Please use `tqdm.notebook.trange` instead of `tqdm.tnrange`", - TqdmDeprecationWarning) + TqdmDeprecationWarning, stacklevel=2) return _tnrange(*args, **kwargs) diff --git a/tqdm/_main.py b/tqdm/_main.py index ec5cd7a0e..07b6730b1 100644 --- a/tqdm/_main.py +++ b/tqdm/_main.py @@ -4,4 +4,4 @@ from warnings import warn warn("This function will be removed in tqdm==5.0.0\n" "Please use `tqdm.cli.*` instead of `tqdm._main.*`", - TqdmDeprecationWarning) + TqdmDeprecationWarning, stacklevel=2) diff --git a/tqdm/_monitor.py b/tqdm/_monitor.py index 17c5a1abd..e1e257069 100644 --- a/tqdm/_monitor.py +++ b/tqdm/_monitor.py @@ -93,7 +93,7 @@ def run(self): if instances != self.get_instances(): # pragma: nocover warn("Set changed size during iteration" + " (see https://github.com/tqdm/tqdm/issues/481)", - TqdmSynchronisationWarning) + TqdmSynchronisationWarning, stacklevel=2) def report(self): return not self.was_killed.is_set() diff --git a/tqdm/_tqdm.py b/tqdm/_tqdm.py index 57c3d75bf..694318ee7 100644 --- a/tqdm/_tqdm.py +++ b/tqdm/_tqdm.py @@ -4,4 +4,4 @@ from warnings import warn warn("This function will be removed in tqdm==5.0.0\n" "Please use `tqdm.std.*` instead of `tqdm._tqdm.*`", - TqdmDeprecationWarning) + TqdmDeprecationWarning, stacklevel=2) diff --git a/tqdm/_tqdm_gui.py b/tqdm/_tqdm_gui.py index f31a2483b..541f104fb 100644 --- a/tqdm/_tqdm_gui.py +++ b/tqdm/_tqdm_gui.py @@ -4,4 +4,4 @@ from warnings import warn warn("This function will be removed in tqdm==5.0.0\n" "Please use `tqdm.gui.*` instead of `tqdm._tqdm_gui.*`", - TqdmDeprecationWarning) + TqdmDeprecationWarning, stacklevel=2) diff --git a/tqdm/_tqdm_notebook.py b/tqdm/_tqdm_notebook.py index d424112fb..dde999817 100644 --- a/tqdm/_tqdm_notebook.py +++ b/tqdm/_tqdm_notebook.py @@ -4,4 +4,4 @@ from warnings import warn warn("This function will be removed in tqdm==5.0.0\n" "Please use `tqdm.notebook.*` instead of `tqdm._tqdm_notebook.*`", - TqdmDeprecationWarning) + TqdmDeprecationWarning, stacklevel=2) diff --git a/tqdm/_utils.py b/tqdm/_utils.py index 1c01ad055..31f78ed70 100644 --- a/tqdm/_utils.py +++ b/tqdm/_utils.py @@ -3,4 +3,4 @@ from warnings import warn warn("This function will be removed in tqdm==5.0.0\n" "Please use `tqdm.utils.*` instead of `tqdm._utils.*`", - TqdmDeprecationWarning) + TqdmDeprecationWarning, stacklevel=2) diff --git a/tqdm/_version.py b/tqdm/_version.py index 7cdb170f2..9d0030a48 100644 --- a/tqdm/_version.py +++ b/tqdm/_version.py @@ -5,7 +5,7 @@ __all__ = ["__version__"] # major, minor, patch, -extra -version_info = 4, 36, 1 +version_info = 4, 37, 0 # Nice string for the version __version__ = '.'.join(map(str, version_info)) diff --git a/tqdm/autonotebook.py b/tqdm/autonotebook.py index bafe0e801..0bcd42a13 100644 --- a/tqdm/autonotebook.py +++ b/tqdm/autonotebook.py @@ -14,5 +14,5 @@ from warnings import warn warn("Using `tqdm.autonotebook.tqdm` in notebook mode." " Use `tqdm.tqdm` instead to force console mode" - " (e.g. in jupyter console)", TqdmExperimentalWarning) + " (e.g. in jupyter console)", TqdmExperimentalWarning, stacklevel=2) __all__ = ["tqdm", "trange"] diff --git a/tqdm/gui.py b/tqdm/gui.py index dbac9d676..35f5c5e55 100644 --- a/tqdm/gui.py +++ b/tqdm/gui.py @@ -41,7 +41,7 @@ def __init__(self, *args, **kwargs): if self.disable or not kwargs['gui']: return - warn('GUI is experimental/alpha', TqdmExperimentalWarning) + warn('GUI is experimental/alpha', TqdmExperimentalWarning, stacklevel=2) self.mpl = mpl self.plt = plt self.sp = None diff --git a/tqdm/std.py b/tqdm/std.py index 8c73c8f0f..bc49b02d2 100644 --- a/tqdm/std.py +++ b/tqdm/std.py @@ -12,7 +12,7 @@ from __future__ import division # compatibility functions and utilities from .utils import _supports_unicode, _environ_cols_wrapper, _range, _unich, \ - _term_move_up, _unicode, WeakSet, _basestring, _OrderedDict, \ + _term_move_up, _unicode, WeakSet, _basestring, _OrderedDict, _text_width, \ Comparable, RE_ANSI, _is_ascii, SimpleTextIOWrapper, FormatReplace from ._monitor import TMonitor # native libraries @@ -474,7 +474,8 @@ def format_meter(n, total, elapsed, ncols=None, prefix='', ascii=False, # Formatting progress bar space available for bar's display full_bar = Bar( frac, - max(1, ncols - len(RE_ANSI.sub('', nobar))) if ncols else 10, + max(1, ncols - _text_width(RE_ANSI.sub('', nobar))) + if ncols else 10, charset=Bar.ASCII if ascii is True else ascii or Bar.UTF) if not _is_ascii(full_bar.charset) and _is_ascii(bar_format): bar_format = _unicode(bar_format) @@ -490,7 +491,8 @@ def format_meter(n, total, elapsed, ncols=None, prefix='', ascii=False, return nobar full_bar = Bar( 0, - max(1, ncols - len(RE_ANSI.sub('', nobar))) if ncols else 10, + max(1, ncols - _text_width(RE_ANSI.sub('', nobar))) + if ncols else 10, charset=Bar.BLANK) return bar_format.format(bar=full_bar, **format_dict) else: @@ -516,7 +518,7 @@ def __new__(cls, *args, **kwargs): except Exception as e: # pragma: nocover warn("tqdm:disabling monitor support" " (monitor_interval = 0) due to:\n" + str(e), - TqdmMonitorWarning) + TqdmMonitorWarning, stacklevel=2) cls.monitor_interval = 0 # Return the instance return instance @@ -645,7 +647,10 @@ def pandas(tclass, *targs, **tkwargs): """ from pandas.core.frame import DataFrame from pandas.core.series import Series - from pandas import Panel + try: + from pandas import Panel + except ImportError: # TODO: pandas>0.25.2 + Panel = None try: # pandas>=0.18.0 from pandas.core.window import _Rolling_and_Expanding except ImportError: # pragma: no cover @@ -752,7 +757,8 @@ def wrapper(*args, **kwargs): DataFrameGroupBy.progress_apply = inner_generator() DataFrame.progress_applymap = inner_generator('applymap') - Panel.progress_apply = inner_generator() + if Panel is not None: + Panel.progress_apply = inner_generator() if PanelGroupBy is not None: PanelGroupBy.progress_apply = inner_generator() @@ -1033,7 +1039,7 @@ def __exit__(self, exc_type, exc_value, traceback): # maybe eager thread cleanup upon external error if (exc_type, exc_value, traceback) == (None, None, None): raise - warn("AttributeError ignored", TqdmWarning) + warn("AttributeError ignored", TqdmWarning, stacklevel=2) def __del__(self): self.close() diff --git a/tqdm/tests/tests_tqdm.py b/tqdm/tests/tests_tqdm.py index dcd21b84e..2ce4620a9 100644 --- a/tqdm/tests/tests_tqdm.py +++ b/tqdm/tests/tests_tqdm.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # Advice: use repr(our_file.read()) to print the full output of tqdm # (else '\r' will replace the previous lines and you'll see only the latest. @@ -277,6 +278,12 @@ def test_format_meter(): assert format_meter(20, 100, 12, ncols=14, rate=8.1, bar_format=r'{l_bar}{bar}|{n_fmt}/{total_fmt}') == \ " 20%|" + unich(0x258d) + " |20/100" + # Check wide characters + if sys.version_info >= (3,): + assert format_meter(0, 1000, 13, ncols=68, prefix='fullwidth: ') == \ + "fullwidth: 0%| | 0/1000 [00:13