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


Reply via email to