Repository: pallets/click

Issue:
Progress bar should advance at end of loop
I'm using the progress bar with a piece of code like this:

``` python
with click.progressbar(some_list) as bar:
    for element in bar:
        element.long_calculation
```

It seems that the progress bar updates every time it enters the loop, which for the last element means the bar shows everything has been completed, although the last computation just started. It would be great if it would show the progress in terms of results, not as an iterator.

I looked briefly at the implementation in [_termui_impl.py](https://github.com/pallets/click/blob/master/click/_termui_impl.py), but the rendering seems to be closely linked to the `pos` attribute and I don't feel comfortable to mess with that. 😒

hide '0d' when eta<24h
t>0 is always true (and we always see '0d ...') if floor is not taken...



Discussion:
This just seems like a off-by-one error to me.

Might be. The ETA is definitely off by one.

Here is the small script I used to test this:

``` python
import time

import click

@click.command()
@click.option('--count', default=1)
def test(count):
    with click.progressbar(range(count)) as bar:
        for x in bar:
            time.sleep(2)

if __name__ == '__main__':
    test()
```

Please add tests. 


Resolving patch:
diff --git a/click/_termui_impl.py b/click/_termui_impl.py
--- a/click/_termui_impl.py
+++ b/click/_termui_impl.py
@@ -102,7 +102,7 @@ def __iter__(self):
         if not self.entered:
             raise RuntimeError('You need to use progress bars in a with block.')
         self.render_progress()
-        return self
+        return self.generator()
 
     def render_finish(self):
         if self.is_hidden:
@@ -130,13 +130,13 @@ def eta(self):
 
     def format_eta(self):
         if self.eta_known:
-            t = self.eta + 1
+            t = int(self.eta)
             seconds = t % 60
-            t /= 60
+            t //= 60
             minutes = t % 60
-            t /= 60
+            t //= 60
             hours = t % 24
-            t /= 24
+            t //= 24
             if t > 0:
                 days = t
                 return '%dd %02d:%02d:%02d' % (days, hours, minutes, seconds)
@@ -253,23 +253,25 @@ def finish(self):
         self.current_item = None
         self.finished = True
 
-    def next(self):
+    def generator(self):
+        """
+        Returns a generator which yields the items added to the bar during
+        construction, and updates the progress bar *after* the yielded block
+        returns.
+        """
+        if not self.entered:
+            raise RuntimeError('You need to use progress bars in a with block.')
+
         if self.is_hidden:
-            return next(self.iter)
-        try:
-            rv = next(self.iter)
-            self.current_item = rv
-        except StopIteration:
+            for rv in self.iter:
+                yield rv
+        else:
+            for rv in self.iter:
+                self.current_item = rv
+                yield rv
+                self.update(1)
             self.finish()
             self.render_progress()
-            raise StopIteration()
-        else:
-            self.update(1)
-            return rv
-
-    if not PY2:
-        __next__ = next
-        del next
 
 
 def pager(text, color=None):


Test patch:
diff --git a/tests/test_termui.py b/tests/test_termui.py
--- a/tests/test_termui.py
+++ b/tests/test_termui.py
@@ -1,4 +1,5 @@
 import click
+import time
 
 
 def test_progressbar_strip_regression(runner, monkeypatch):
@@ -69,3 +70,41 @@ def test_secho(runner):
         click.secho(None, nl=False)
         bytes = out.getvalue()
         assert bytes == b''
+
+
+def test_progressbar_yields_all_items(runner):
+    with click.progressbar(range(3)) as progress:
+        assert len(list(progress)) == 3
+
+
+def test_progressbar_update(runner, monkeypatch):
+    class FakeClock(object):
+        def __init__(self):
+            self.now = time.time()
+
+        def advance_time(self, seconds=1):
+            self.now += seconds
+
+        def time(self):
+            return self.now
+
+    fake_clock = FakeClock()
+
+    @click.command()
+    def cli():
+        with click.progressbar(range(4)) as progress:
+            for _ in progress:
+                fake_clock.advance_time()
+                print("")
+
+    monkeypatch.setattr(time, 'time', fake_clock.time)
+    monkeypatch.setattr(click._termui_impl, 'isatty', lambda _: True)
+    output = runner.invoke(cli, []).output
+
+    lines = [line for line in output.split('\n') if '[' in line]
+
+    assert '  0%' in lines[0]
+    assert ' 25%  00:00:03' in lines[1]
+    assert ' 50%  00:00:02' in lines[2]
+    assert ' 75%  00:00:01' in lines[3]
+    assert '100%          ' in lines[4]