Hello community, here is the log from the commit of package python-tqdm for openSUSE:Factory checked in at 2019-12-11 12:11:04 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-tqdm (Old) and /work/SRC/openSUSE:Factory/.python-tqdm.new.4691 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-tqdm" Wed Dec 11 12:11:04 2019 rev:26 rq:755168 version:4.40.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-tqdm/python-tqdm.changes 2019-12-07 15:23:07.239731034 +0100 +++ /work/SRC/openSUSE:Factory/.python-tqdm.new.4691/python-tqdm.changes 2019-12-11 12:11:51.252570523 +0100 @@ -1,0 +2,19 @@ +Sat Dec 7 17:40:20 UTC 2019 - Arun Persaud <[email protected]> + +- update to version 4.40.1: + * test for floats for total + +- changes from version 4.40.0: + * officially support float for n and total (#802) + + notebook: use FloatProgress <= IntProgress (#471, #456) + + allow imprecision (n <= total + epsilon) (#849) + * fix unicode bar format arguments (#803 -> #851) + * add contrib submodule (#815) + * add wrapattr, utils.CallbackIOWrapper, contrib.DummyTqdmFile (#84 + -> #844) + * update tests + * update documentation + * tidy automatic snap deployments + * minor doc update (#854) + +------------------------------------------------------------------- Old: ---- tqdm-4.39.0.tar.gz New: ---- tqdm-4.40.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-tqdm.spec ++++++ --- /var/tmp/diff_new_pack.LcLYFC/_old 2019-12-11 12:11:53.832569438 +0100 +++ /var/tmp/diff_new_pack.LcLYFC/_new 2019-12-11 12:11:53.832569438 +0100 @@ -28,7 +28,7 @@ %bcond_with test %endif Name: python-tqdm%{pkg_suffix} -Version: 4.39.0 +Version: 4.40.1 Release: 0 Summary: An extensible progress meter License: MPL-2.0 AND MIT ++++++ tqdm-4.39.0.tar.gz -> tqdm-4.40.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.39.0/CONTRIBUTING.md new/tqdm-4.40.1/CONTRIBUTING.md --- old/tqdm-4.39.0/CONTRIBUTING.md 2019-11-22 18:15:18.000000000 +0100 +++ new/tqdm-4.40.1/CONTRIBUTING.md 2019-12-06 17:14:54.000000000 +0100 @@ -44,6 +44,10 @@ + must have negligible impact on performance + should have 100% coverage by unit tests + should be appropriately commented + + should have well-formatted docstrings for functions + * under 76 chars (incl. initial spaces) to avoid linebreaks in terminal pagers + * use two spaces between variable name and colon + * use [default: ...] for default values of keyword arguments + will not break backward compatibility unless there is a very good reason * e.g. breaking py26 compatibility purely in favour of readability (such as converting `dict(a=1)` to `{'a': 1}`) is not a good enough reason + API changes should be discussed carefully @@ -325,8 +329,8 @@ 5. wait for tests to pass a) in case of failure, fix and go back to (2) 6. `git tag vM.m.p && git push --tags` -7. `[python setup.py] make distclean` -8. `[python setup.py] make build` +7. **`[AUTO:TravisCI]`** `[python setup.py] make distclean` +8. **`[AUTO:TravisCI]`** `[python setup.py] make build` 9. **`[AUTO:TravisCI]`** upload to PyPI. either: a) `[python setup.py] make pypi`, or b) `twine upload -s -i $(git config user.signingkey) dist/tqdm-*` @@ -334,7 +338,7 @@ a) `make -B docker` b) `docker push tqdm/tqdm:latest` c) `docker push tqdm/tqdm:$(docker run -i --rm tqdm/tqdm -v)` -11. upload to snapcraft: +11. **`[AUTO:TravisCI]`** upload to snapcraft: a) `make snap`, and b) `snapcraft push tqdm*.snap --release stable` 12. Wait for travis to draft a new release on <https://github.com/tqdm/tqdm/releases> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.39.0/MANIFEST.in new/tqdm-4.40.1/MANIFEST.in --- old/tqdm-4.39.0/MANIFEST.in 2019-11-22 18:15:18.000000000 +0100 +++ new/tqdm-4.40.1/MANIFEST.in 2019-12-06 17:14:54.000000000 +0100 @@ -7,6 +7,9 @@ include Makefile include tox.ini +# Non-std submodules +recursive-include tqdm/contrib *.py + # Test suite recursive-include tqdm/tests *.py include requirements-dev.txt diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.39.0/PKG-INFO new/tqdm-4.40.1/PKG-INFO --- old/tqdm-4.39.0/PKG-INFO 2019-11-22 18:15:36.000000000 +0100 +++ new/tqdm-4.40.1/PKG-INFO 2019-12-06 17:15:11.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: tqdm -Version: 4.39.0 +Version: 4.40.1 Summary: Fast, Extensible Progress Meter Home-page: https://github.com/tqdm/tqdm Maintainer: tqdm developers @@ -110,9 +110,13 @@ |Snapcraft| + There are 3 channels to choose from: + .. code:: sh - snap install tqdm + snap install tqdm # implies --stable, i.e. latest tagged release + snap install tqdm --candidate # master branch + snap install tqdm --edge # devel branch Latest Docker release ~~~~~~~~~~~~~~~~~~~~~ @@ -326,14 +330,14 @@ Leave blank to manually manage the updates. * desc : str, optional Prefix for the progressbar. - * total : int, optional + * total : int or float, optional The number of expected iterations. If unspecified, len(iterable) is used if possible. If float("inf") or as a last resort, only basic progress statistics are displayed (no ETA, no progressbar). If ``gui`` is True and this parameter needs subsequent updating, - specify an initial arbitrary large positive integer, - e.g. int(9e9). + specify an initial arbitrary large positive number, + e.g. 9e9. * leave : bool, optional If [default: True], keeps all traces of the progressbar upon termination of iteration. @@ -355,7 +359,7 @@ Automatically adjusts ``miniters`` to correspond to ``mininterval`` after long display update lag. Only works if ``dynamic_miniters`` or monitor thread is enabled. - * miniters : int, optional + * miniters : int or float, optional Minimum progress display update interval, in iterations. If 0 and ``dynamic_miniters``, will automatically adjust to equal ``mininterval`` (more CPU efficient, good for tight loops). @@ -398,9 +402,10 @@ remaining, remaining_s. Note that a trailing ": " is automatically removed after {desc} if the latter is empty. - * initial : int, optional + * initial : int or float, optional The initial counter value. Useful when restarting a progress - bar [default: 0]. + bar [default: 0]. If using float, consider specifying ``{n:.3f}`` + or similar in ``bar_format``, or specifying ``unit_scale``. * position : int, optional Specify the line offset to print this bar (starting from 0) Automatic if unspecified. @@ -459,9 +464,10 @@ Parameters ---------- - n : int, optional + n : int or float, optional Increment to add to the internal counter of iterations - [default: 1]. + [default: 1]. If using float, consider specifying ``{n:.3f}`` + or similar in ``bar_format``, or specifying ``unit_scale``. """ def close(self): @@ -495,7 +501,7 @@ Parameters ---------- - total : int, optional. Total to use for the new bar. + total : int or float, optional. Total to use for the new bar. """ def set_description(self, desc=None, refresh=True): @@ -586,7 +592,7 @@ .. code:: python - from tqdm import trange + from tqdm import tqdm, trange from random import random, randint from time import sleep @@ -729,7 +735,7 @@ ``tqdm`` can easily support callbacks/hooks and manual updates. Here's an example with ``urllib``: - **urllib.urlretrieve documentation** + **``urllib.urlretrieve`` documentation** | [...] | If present, the hook function will be called once @@ -772,6 +778,41 @@ large differences in iteration speed (e.g. downloading a file over a patchy connection). + **Wrapping read/write methods** + + To measure throughput through a file-like object's ``read`` or ``write`` + methods, use ``CallbackIOWrapper``: + + .. code:: python + + from tqdm import tqdm + from tqdm.utils import CallbackIOWrapper + + with tqdm(total=file_obj.size, + unit='B', unit_scale=True, unit_divisor=1024) as t: + fobj = CallbackIOWrapper(t.update, file_obj, "read") + while True: + chunk = fobj.read(chunk_size) + if not chunk: + break + t.reset() + # ... continue to use `t` for something else + + Alternatively, use the even simpler ``wrapattr`` convenience function, + which would condense both the ``urllib`` and ``CallbackIOWrapper`` examples + down to: + + .. code:: python + + import urllib, os + from tqdm import tqdm + + eg_link = "https://caspersci.uk.to/matryoshka.zip" + with tqdm.wrapattr(open(os.devnull, "wb"), "write", + miniters=1, desc=eg_link.split('/')[-1]) as fout: + for chunk in urllib.urlopen(eg_link): + fout.write(chunk) + Pandas Integration ~~~~~~~~~~~~~~~~~~ @@ -967,23 +1008,8 @@ import contextlib import sys from tqdm import tqdm + from tqdm.contrib import DummyTqdmFile - class DummyTqdmFile(object): - """Dummy file-like that will write to tqdm""" - file = None - def __init__(self, file): - self.file = file - - def write(self, x): - # Avoid print() second call (useless \n) - if len(x.rstrip()) > 0: - tqdm.write(x, file=self.file) - - def flush(self): - return getattr(self.file, "flush", lambda: None)() - - def isatty(self): - return getattr(self.file, "isatty", lambda: False)() @contextlib.contextmanager def std_out_err_redirect_tqdm(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.39.0/README.rst new/tqdm-4.40.1/README.rst --- old/tqdm-4.39.0/README.rst 2019-11-22 18:15:34.000000000 +0100 +++ new/tqdm-4.40.1/README.rst 2019-12-06 17:15:09.000000000 +0100 @@ -102,9 +102,13 @@ |Snapcraft| +There are 3 channels to choose from: + .. code:: sh - snap install tqdm + snap install tqdm # implies --stable, i.e. latest tagged release + snap install tqdm --candidate # master branch + snap install tqdm --edge # devel branch Latest Docker release ~~~~~~~~~~~~~~~~~~~~~ @@ -318,14 +322,14 @@ Leave blank to manually manage the updates. * desc : str, optional Prefix for the progressbar. -* total : int, optional +* total : int or float, optional The number of expected iterations. If unspecified, len(iterable) is used if possible. If float("inf") or as a last resort, only basic progress statistics are displayed (no ETA, no progressbar). If ``gui`` is True and this parameter needs subsequent updating, - specify an initial arbitrary large positive integer, - e.g. int(9e9). + specify an initial arbitrary large positive number, + e.g. 9e9. * leave : bool, optional If [default: True], keeps all traces of the progressbar upon termination of iteration. @@ -347,7 +351,7 @@ Automatically adjusts ``miniters`` to correspond to ``mininterval`` after long display update lag. Only works if ``dynamic_miniters`` or monitor thread is enabled. -* miniters : int, optional +* miniters : int or float, optional Minimum progress display update interval, in iterations. If 0 and ``dynamic_miniters``, will automatically adjust to equal ``mininterval`` (more CPU efficient, good for tight loops). @@ -390,9 +394,10 @@ remaining, remaining_s. Note that a trailing ": " is automatically removed after {desc} if the latter is empty. -* initial : int, optional +* initial : int or float, optional The initial counter value. Useful when restarting a progress - bar [default: 0]. + bar [default: 0]. If using float, consider specifying ``{n:.3f}`` + or similar in ``bar_format``, or specifying ``unit_scale``. * position : int, optional Specify the line offset to print this bar (starting from 0) Automatic if unspecified. @@ -451,9 +456,10 @@ Parameters ---------- - n : int, optional + n : int or float, optional Increment to add to the internal counter of iterations - [default: 1]. + [default: 1]. If using float, consider specifying ``{n:.3f}`` + or similar in ``bar_format``, or specifying ``unit_scale``. """ def close(self): @@ -487,7 +493,7 @@ Parameters ---------- - total : int, optional. Total to use for the new bar. + total : int or float, optional. Total to use for the new bar. """ def set_description(self, desc=None, refresh=True): @@ -578,7 +584,7 @@ .. code:: python - from tqdm import trange + from tqdm import tqdm, trange from random import random, randint from time import sleep @@ -721,7 +727,7 @@ ``tqdm`` can easily support callbacks/hooks and manual updates. Here's an example with ``urllib``: -**urllib.urlretrieve documentation** +**``urllib.urlretrieve`` documentation** | [...] | If present, the hook function will be called once @@ -764,6 +770,41 @@ large differences in iteration speed (e.g. downloading a file over a patchy connection). +**Wrapping read/write methods** + +To measure throughput through a file-like object's ``read`` or ``write`` +methods, use ``CallbackIOWrapper``: + +.. code:: python + + from tqdm import tqdm + from tqdm.utils import CallbackIOWrapper + + with tqdm(total=file_obj.size, + unit='B', unit_scale=True, unit_divisor=1024) as t: + fobj = CallbackIOWrapper(t.update, file_obj, "read") + while True: + chunk = fobj.read(chunk_size) + if not chunk: + break + t.reset() + # ... continue to use `t` for something else + +Alternatively, use the even simpler ``wrapattr`` convenience function, +which would condense both the ``urllib`` and ``CallbackIOWrapper`` examples +down to: + +.. code:: python + + import urllib, os + from tqdm import tqdm + + eg_link = "https://caspersci.uk.to/matryoshka.zip" + with tqdm.wrapattr(open(os.devnull, "wb"), "write", + miniters=1, desc=eg_link.split('/')[-1]) as fout: + for chunk in urllib.urlopen(eg_link): + fout.write(chunk) + Pandas Integration ~~~~~~~~~~~~~~~~~~ @@ -959,23 +1000,8 @@ import contextlib import sys from tqdm import tqdm + from tqdm.contrib import DummyTqdmFile - class DummyTqdmFile(object): - """Dummy file-like that will write to tqdm""" - file = None - def __init__(self, file): - self.file = file - - def write(self, x): - # Avoid print() second call (useless \n) - if len(x.rstrip()) > 0: - tqdm.write(x, file=self.file) - - def flush(self): - return getattr(self.file, "flush", lambda: None)() - - def isatty(self): - return getattr(self.file, "isatty", lambda: False)() @contextlib.contextmanager def std_out_err_redirect_tqdm(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.39.0/examples/redirect_print.py new/tqdm-4.40.1/examples/redirect_print.py --- old/tqdm-4.39.0/examples/redirect_print.py 2019-11-22 18:15:18.000000000 +0100 +++ new/tqdm-4.40.1/examples/redirect_print.py 2019-12-06 17:14:54.000000000 +0100 @@ -15,25 +15,7 @@ import contextlib import sys from tqdm import tqdm - - -class DummyTqdmFile(object): - """Dummy file-like that will write to tqdm""" - file = None - - def __init__(self, file): - self.file = file - - def write(self, x): - # Avoid print() second call (useless \n) - if len(x.rstrip()) > 0: - tqdm.write(x, file=self.file) - - def flush(self): - return getattr(self.file, "flush", lambda: None)() - - def isatty(self): - return getattr(self.file, "isatty", lambda: False)() +from tqdm.contrib import DummyTqdmFile @contextlib.contextmanager diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.39.0/examples/tqdm_wget.py new/tqdm-4.40.1/examples/tqdm_wget.py --- old/tqdm-4.39.0/examples/tqdm_wget.py 2019-11-22 18:15:18.000000000 +0100 +++ new/tqdm-4.40.1/examples/tqdm_wget.py 2019-12-06 17:14:54.000000000 +0100 @@ -95,3 +95,9 @@ desc=eg_file) as t: # all optional kwargs urllib.urlretrieve(eg_link, filename=eg_out, reporthook=t.update_to, data=None) + +# Even simpler progress by wrapping the output file's `write()` +with tqdm.wrapattr(open(eg_out, "wb"), "write", + miniters=1, desc=eg_file) as fout: + for chunk in urllib.urlopen(eg_link): + fout.write(chunk) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.39.0/tqdm/_version.py new/tqdm-4.40.1/tqdm/_version.py --- old/tqdm-4.39.0/tqdm/_version.py 2019-11-22 18:15:18.000000000 +0100 +++ new/tqdm-4.40.1/tqdm/_version.py 2019-12-06 17:14:54.000000000 +0100 @@ -5,7 +5,7 @@ __all__ = ["__version__"] # major, minor, patch, -extra -version_info = 4, 39, 0 +version_info = 4, 40, 1 # Nice string for the version __version__ = '.'.join(map(str, version_info)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.39.0/tqdm/contrib/__init__.py new/tqdm-4.40.1/tqdm/contrib/__init__.py --- old/tqdm-4.39.0/tqdm/contrib/__init__.py 1970-01-01 01:00:00.000000000 +0100 +++ new/tqdm-4.40.1/tqdm/contrib/__init__.py 2019-12-06 17:14:54.000000000 +0100 @@ -0,0 +1,10 @@ +from tqdm import tqdm +from tqdm.utils import ObjectWrapper + + +class DummyTqdmFile(ObjectWrapper): + """Dummy file-like that will write to tqdm""" + def write(self, x, nolock=False): + # Avoid print() second call (useless \n) + if len(x.rstrip()) > 0: + tqdm.write(x, file=self._wrapped, nolock=nolock) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.39.0/tqdm/notebook.py new/tqdm-4.40.1/tqdm/notebook.py --- old/tqdm-4.39.0/tqdm/notebook.py 2019-11-22 18:15:18.000000000 +0100 +++ new/tqdm-4.40.1/tqdm/notebook.py 2019-12-06 17:14:54.000000000 +0100 @@ -32,32 +32,25 @@ IPY = 32 import warnings with warnings.catch_warnings(): - ipy_deprecation_msg = "The `IPython.html` package" \ - " has been deprecated" - warnings.filterwarnings('error', - message=".*" + ipy_deprecation_msg + ".*") + warnings.filterwarnings( + 'ignore', + message=".*The `IPython.html` package has been deprecated.*") try: import IPython.html.widgets as ipywidgets - except Warning as e: - if ipy_deprecation_msg not in str(e): - raise - warnings.simplefilter('ignore') - try: - import IPython.html.widgets as ipywidgets # NOQA - except ImportError: - pass except ImportError: pass try: # IPython 4.x / 3.x if IPY == 32: - from IPython.html.widgets import IntProgress, HBox, HTML + from IPython.html.widgets import FloatProgress as IProgress + from IPython.html.widgets import HBox, HTML IPY = 3 else: - from ipywidgets import IntProgress, HBox, HTML + from ipywidgets import FloatProgress as IProgress + from ipywidgets import HBox, HTML except ImportError: try: # IPython 2.x - from IPython.html.widgets import IntProgressWidget as IntProgress + from IPython.html.widgets import FloatProgressWidget as IProgress from IPython.html.widgets import ContainerWidget as HBox from IPython.html.widgets import HTML IPY = 2 @@ -100,15 +93,15 @@ # Prepare IPython progress bar try: if total: - pbar = IntProgress(min=0, max=total) + pbar = IProgress(min=0, max=total) else: # No total? Show info style bar with no progress tqdm status - pbar = IntProgress(min=0, max=1) + pbar = IProgress(min=0, max=1) pbar.value = 1 pbar.bar_style = 'info' except NameError: # #187 #451 #558 raise ImportError( - "IntProgress not found. Please update jupyter and ipywidgets." + "FloatProgress not found. Please update jupyter and ipywidgets." " See https://ipywidgets.readthedocs.io/en/stable" "/user_install.html") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.39.0/tqdm/std.py new/tqdm-4.40.1/tqdm/std.py --- old/tqdm-4.39.0/tqdm/std.py 2019-11-22 18:15:18.000000000 +0100 +++ new/tqdm-4.40.1/tqdm/std.py 2019-12-06 17:14:54.000000000 +0100 @@ -7,19 +7,18 @@ >>> for i in trange(10): #same as: for i in tqdm(xrange(10)) ... ... """ -from __future__ import absolute_import -# integer division / : float, // : int -from __future__ import division +from __future__ import absolute_import, division # compatibility functions and utilities from .utils import _supports_unicode, _environ_cols_wrapper, _range, _unich, \ _term_move_up, _unicode, WeakSet, _basestring, _OrderedDict, _text_width, \ - Comparable, RE_ANSI, _is_ascii, SimpleTextIOWrapper, FormatReplace + Comparable, RE_ANSI, _is_ascii, FormatReplace, \ + SimpleTextIOWrapper, CallbackIOWrapper from ._monitor import TMonitor # native libraries +from contextlib import contextmanager import sys from numbers import Number from time import time -from contextlib import contextmanager # For parallelism safety import threading as th from warnings import warn @@ -144,7 +143,9 @@ BLANK = " " def __init__(self, frac, default_len=10, charset=UTF): - assert 0 <= frac <= 1 + if not (0 <= frac <= 1): + warn("clamping frac to range [0, 1]", TqdmWarning, stacklevel=2) + frac = max(0, min(1, frac)) assert default_len > 0 self.frac = frac self.default_len = default_len @@ -315,11 +316,11 @@ Parameters ---------- - n : int + n : int or float Number of finished iterations. - total : int - The expected total number of iterations. If meaningless (), only - basic progress statistics are displayed (no ETA). + total : int or float + The expected total number of iterations. If meaningless (None), + only basic progress statistics are displayed (no ETA). elapsed : float Number of seconds passed since start. ncols : int, optional @@ -372,7 +373,7 @@ """ # sanity check: total - if total and n > total: + if total and n >= (total + 0.5): # allow float imprecision (#849) total = None # apply custom scale if necessary @@ -466,7 +467,11 @@ bar_format = "{l_bar}{bar}{r_bar}" full_bar = FormatReplace() - nobar = bar_format.format(bar=full_bar, **format_dict) + try: + nobar = bar_format.format(bar=full_bar, **format_dict) + except UnicodeEncodeError: + bar_format = _unicode(bar_format) + nobar = bar_format.format(bar=full_bar, **format_dict) if not full_bar.format_called: # no {bar}, we can just format and return return nobar @@ -786,14 +791,14 @@ Leave blank to manually manage the updates. desc : str, optional Prefix for the progressbar. - total : int, optional + total : int or float, optional The number of expected iterations. If unspecified, len(iterable) is used if possible. If float("inf") or as a last resort, only basic progress statistics are displayed (no ETA, no progressbar). If `gui` is True and this parameter needs subsequent updating, - specify an initial arbitrary large positive integer, - e.g. int(9e9). + specify an initial arbitrary large positive number, + e.g. 9e9. leave : bool, optional If [default: True], keeps all traces of the progressbar upon termination of iteration. @@ -815,7 +820,7 @@ Automatically adjusts `miniters` to correspond to `mininterval` after long display update lag. Only works if `dynamic_miniters` or monitor thread is enabled. - miniters : int, optional + miniters : int or float, optional Minimum progress display update interval, in iterations. If 0 and `dynamic_miniters`, will automatically adjust to equal `mininterval` (more CPU efficient, good for tight loops). @@ -858,9 +863,10 @@ remaining, remaining_s. Note that a trailing ": " is automatically removed after {desc} if the latter is empty. - initial : int, optional + initial : int or float, optional The initial counter value. Useful when restarting a progress - bar [default: 0]. + bar [default: 0]. If using float, consider specifying `{n:.3f}` + or similar in `bar_format`, or specifying `unit_scale`. position : int, optional Specify the line offset to print this bar (starting from 0) Automatic if unspecified. @@ -1161,9 +1167,10 @@ Parameters ---------- - n : int, optional + n : int or float, optional Increment to add to the internal counter of iterations - [default: 1]. + [default: 1]. If using float, consider specifying `{n:.3f}` + or similar in `bar_format`, or specifying `unit_scale`. """ # N.B.: see __iter__() for more comments. if self.disable: @@ -1315,7 +1322,7 @@ Parameters ---------- - total : int, optional. Total to use for the new bar. + total : int or float, optional. Total to use for the new bar. """ self.last_print_n = self.n = 0 self.last_print_t = self.start_t = self._time() @@ -1424,6 +1431,27 @@ if pos: self.moveto(-pos) + @classmethod + @contextmanager + def wrapattr(tclass, stream, method, total=None, bytes=True, **tkwargs): + """ + stream : file-like object. + method : str, "read" or "write". The result of `read()` and + the first argument of `write()` should have a `len()`. + + >>> with tqdm.wrapattr(file_obj, "read", total=file_obj.size) as fobj: + ... while True: + ... chunk = fobj.read(chunk_size) + ... if not chunk: + ... break + """ + with tclass(total=total, **tkwargs) as t: + if bytes: + t.unit = "B" + t.unit_scale = True + t.unit_divisor = 1024 + yield CallbackIOWrapper(t.update, stream, method) + def trange(*args, **kwargs): """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.39.0/tqdm/tests/tests_tqdm.py new/tqdm-4.40.1/tqdm/tests/tests_tqdm.py --- old/tqdm-4.39.0/tqdm/tests/tests_tqdm.py 2019-11-22 18:15:18.000000000 +0100 +++ new/tqdm-4.40.1/tqdm/tests/tests_tqdm.py 2019-12-06 17:14:54.000000000 +0100 @@ -10,11 +10,13 @@ from nose.plugins.skip import SkipTest from nose.tools import assert_raises from contextlib import contextmanager +from warnings import catch_warnings, simplefilter from tqdm import tqdm from tqdm import trange from tqdm import TqdmDeprecationWarning from tqdm.std import Bar +from tqdm.contrib import DummyTqdmFile try: from StringIO import StringIO @@ -1314,6 +1316,11 @@ assert t.desc == '' assert "World" not in our_file.getvalue() + # unicode + with closing(StringIO()) as our_file: + with tqdm(total=10, file=our_file) as t: + t.set_description(u"\xe1\xe9\xed\xf3\xfa") + @with_setup(pretest, posttest) def test_deprecated_gui(): @@ -1667,19 +1674,6 @@ assert "j 8.00" in res -class DummyTqdmFile(object): - """Dummy file-like that will write to tqdm""" - file = None - - def __init__(self, file): - self.file = file - - def write(self, x): - # Avoid print() second call (useless \n) - if len(x.rstrip()) > 0: - tqdm.write(x, file=self.file, nolock=True) - - @contextmanager def std_out_err_redirect_tqdm(tqdm_file=sys.stderr): orig_out_err = sys.stdout, sys.stderr @@ -1800,3 +1794,41 @@ from tqdm import autonotebook, auto backendCheck(autonotebook) backendCheck(auto) + + +@with_setup(pretest, posttest) +def test_wrapattr(): + """Test wrapping file-like objects""" + data = "a twenty-char string" + + with closing(StringIO()) as our_file: + with closing(StringIO()) as writer: + with tqdm.wrapattr( + writer, "write", file=our_file, bytes=True) as wrap: + wrap.write(data) + res = writer.getvalue() + assert data == res + res = our_file.getvalue() + assert ('%.1fB [' % len(data)) in res + + with closing(StringIO()) as our_file: + with closing(StringIO()) as writer: + with tqdm.wrapattr( + writer, "write", file=our_file, bytes=False) as wrap: + wrap.write(data) + res = our_file.getvalue() + assert ('%dit [' % len(data)) in res + + +@with_setup(pretest, posttest) +def test_float_progress(): + """Test float totals""" + with closing(StringIO()) as our_file: + with trange(10, total=9.6, file=our_file) as t: + with catch_warnings(record=True) as w: + simplefilter("always") + for i in t: + if i < 9: + assert not w + assert w + assert "clamping frac" in str(w[-1].message) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.39.0/tqdm/utils.py new/tqdm-4.40.1/tqdm/utils.py --- old/tqdm-4.39.0/tqdm/utils.py 2019-11-22 18:15:18.000000000 +0100 +++ new/tqdm-4.40.1/tqdm/utils.py 2019-12-06 17:14:54.000000000 +0100 @@ -1,7 +1,8 @@ +from functools import wraps import os -import subprocess from platform import system as _curos import re +import subprocess CUR_OS = _curos() IS_WIN = CUR_OS in ['Windows', 'cli'] IS_NIX = (not IS_WIN) and any( @@ -161,28 +162,72 @@ return not self < other -class SimpleTextIOWrapper(object): +class ObjectWrapper(object): + def __getattr__(self, name): + return getattr(self._wrapped, name) + + def __setattr__(self, name, value): + return setattr(self._wrapped, name, value) + + def wrapper_getattr(self, name): + """Actual `self.getattr` rather than self._wrapped.getattr""" + try: + return object.__getattr__(self, name) + except AttributeError: # py2 + return getattr(self, name) + + def wrapper_setattr(self, name, value): + """Actual `self.setattr` rather than self._wrapped.setattr""" + return object.__setattr__(self, name, value) + + def __init__(self, wrapped): + """ + Thin wrapper around a given object + """ + self.wrapper_setattr('_wrapped', wrapped) + + +class SimpleTextIOWrapper(ObjectWrapper): """ Change only `.write()` of the wrapped object by encoding the passed value and passing the result to the wrapped object's `.write()` method. """ # pylint: disable=too-few-public-methods def __init__(self, wrapped, encoding): - object.__setattr__(self, '_wrapped', wrapped) - object.__setattr__(self, 'encoding', encoding) + super(SimpleTextIOWrapper, self).__init__(wrapped) + self.wrapper_setattr('encoding', encoding) def write(self, s): """ Encode `s` and pass to the wrapped object's `.write()` method. """ - return getattr(self, '_wrapped').write(s.encode(getattr( - self, 'encoding'))) + return self._wrapped.write(s.encode(self.wrapper_getattr('encoding'))) - def __getattr__(self, name): - return getattr(self._wrapped, name) - def __setattr__(self, name, value): # pragma: no cover - return setattr(self._wrapped, name, value) +class CallbackIOWrapper(ObjectWrapper): + def __init__(self, callback, stream, method="read"): + """ + Wrap a given `file`-like object's `read()` or `write()` to report + lengths to the given `callback` + """ + super(CallbackIOWrapper, self).__init__(stream) + func = getattr(stream, method) + if method == "write": + @wraps(func) + def write(data, *args, **kwargs): + res = func(data, *args, **kwargs) + callback(len(data)) + return res + self.wrapper_setattr('write', write) + elif method == "read": + @wraps(func) + def read(*args, **kwargs): + data = func(*args, **kwargs) + callback(len(data)) + return data + self.wrapper_setattr('read', read) + else: + raise KeyError("Can only wrap read/write methods") def _is_utf(encoding): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.39.0/tqdm.egg-info/PKG-INFO new/tqdm-4.40.1/tqdm.egg-info/PKG-INFO --- old/tqdm-4.39.0/tqdm.egg-info/PKG-INFO 2019-11-22 18:15:36.000000000 +0100 +++ new/tqdm-4.40.1/tqdm.egg-info/PKG-INFO 2019-12-06 17:15:11.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: tqdm -Version: 4.39.0 +Version: 4.40.1 Summary: Fast, Extensible Progress Meter Home-page: https://github.com/tqdm/tqdm Maintainer: tqdm developers @@ -110,9 +110,13 @@ |Snapcraft| + There are 3 channels to choose from: + .. code:: sh - snap install tqdm + snap install tqdm # implies --stable, i.e. latest tagged release + snap install tqdm --candidate # master branch + snap install tqdm --edge # devel branch Latest Docker release ~~~~~~~~~~~~~~~~~~~~~ @@ -326,14 +330,14 @@ Leave blank to manually manage the updates. * desc : str, optional Prefix for the progressbar. - * total : int, optional + * total : int or float, optional The number of expected iterations. If unspecified, len(iterable) is used if possible. If float("inf") or as a last resort, only basic progress statistics are displayed (no ETA, no progressbar). If ``gui`` is True and this parameter needs subsequent updating, - specify an initial arbitrary large positive integer, - e.g. int(9e9). + specify an initial arbitrary large positive number, + e.g. 9e9. * leave : bool, optional If [default: True], keeps all traces of the progressbar upon termination of iteration. @@ -355,7 +359,7 @@ Automatically adjusts ``miniters`` to correspond to ``mininterval`` after long display update lag. Only works if ``dynamic_miniters`` or monitor thread is enabled. - * miniters : int, optional + * miniters : int or float, optional Minimum progress display update interval, in iterations. If 0 and ``dynamic_miniters``, will automatically adjust to equal ``mininterval`` (more CPU efficient, good for tight loops). @@ -398,9 +402,10 @@ remaining, remaining_s. Note that a trailing ": " is automatically removed after {desc} if the latter is empty. - * initial : int, optional + * initial : int or float, optional The initial counter value. Useful when restarting a progress - bar [default: 0]. + bar [default: 0]. If using float, consider specifying ``{n:.3f}`` + or similar in ``bar_format``, or specifying ``unit_scale``. * position : int, optional Specify the line offset to print this bar (starting from 0) Automatic if unspecified. @@ -459,9 +464,10 @@ Parameters ---------- - n : int, optional + n : int or float, optional Increment to add to the internal counter of iterations - [default: 1]. + [default: 1]. If using float, consider specifying ``{n:.3f}`` + or similar in ``bar_format``, or specifying ``unit_scale``. """ def close(self): @@ -495,7 +501,7 @@ Parameters ---------- - total : int, optional. Total to use for the new bar. + total : int or float, optional. Total to use for the new bar. """ def set_description(self, desc=None, refresh=True): @@ -586,7 +592,7 @@ .. code:: python - from tqdm import trange + from tqdm import tqdm, trange from random import random, randint from time import sleep @@ -729,7 +735,7 @@ ``tqdm`` can easily support callbacks/hooks and manual updates. Here's an example with ``urllib``: - **urllib.urlretrieve documentation** + **``urllib.urlretrieve`` documentation** | [...] | If present, the hook function will be called once @@ -772,6 +778,41 @@ large differences in iteration speed (e.g. downloading a file over a patchy connection). + **Wrapping read/write methods** + + To measure throughput through a file-like object's ``read`` or ``write`` + methods, use ``CallbackIOWrapper``: + + .. code:: python + + from tqdm import tqdm + from tqdm.utils import CallbackIOWrapper + + with tqdm(total=file_obj.size, + unit='B', unit_scale=True, unit_divisor=1024) as t: + fobj = CallbackIOWrapper(t.update, file_obj, "read") + while True: + chunk = fobj.read(chunk_size) + if not chunk: + break + t.reset() + # ... continue to use `t` for something else + + Alternatively, use the even simpler ``wrapattr`` convenience function, + which would condense both the ``urllib`` and ``CallbackIOWrapper`` examples + down to: + + .. code:: python + + import urllib, os + from tqdm import tqdm + + eg_link = "https://caspersci.uk.to/matryoshka.zip" + with tqdm.wrapattr(open(os.devnull, "wb"), "write", + miniters=1, desc=eg_link.split('/')[-1]) as fout: + for chunk in urllib.urlopen(eg_link): + fout.write(chunk) + Pandas Integration ~~~~~~~~~~~~~~~~~~ @@ -967,23 +1008,8 @@ import contextlib import sys from tqdm import tqdm + from tqdm.contrib import DummyTqdmFile - class DummyTqdmFile(object): - """Dummy file-like that will write to tqdm""" - file = None - def __init__(self, file): - self.file = file - - def write(self, x): - # Avoid print() second call (useless \n) - if len(x.rstrip()) > 0: - tqdm.write(x, file=self.file) - - def flush(self): - return getattr(self.file, "flush", lambda: None)() - - def isatty(self): - return getattr(self.file, "isatty", lambda: False)() @contextlib.contextmanager def std_out_err_redirect_tqdm(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tqdm-4.39.0/tqdm.egg-info/SOURCES.txt new/tqdm-4.40.1/tqdm.egg-info/SOURCES.txt --- old/tqdm-4.39.0/tqdm.egg-info/SOURCES.txt 2019-11-22 18:15:36.000000000 +0100 +++ new/tqdm-4.40.1/tqdm.egg-info/SOURCES.txt 2019-12-06 17:15:11.000000000 +0100 @@ -41,6 +41,7 @@ tqdm.egg-info/entry_points.txt tqdm.egg-info/requires.txt tqdm.egg-info/top_level.txt +tqdm/contrib/__init__.py tqdm/tests/tests_main.py tqdm/tests/tests_pandas.py tqdm/tests/tests_perf.py
