Repository: pallets/click

Issue:
Progress bar does not display until after the first item is complete
Take this example:
```python
    with click.progressbar(length=6, label="Doing something") as bar:
        for thing in [3, 2, 1]:
            time.sleep(thing)
            bar.update(thing)
```

This shows nothing until three seconds, at which point it shows:
```
Doing something  [##################------------------]   50%  00:00:03
```

Would it be reasonable for it to display a `0%` at first? 
This is mostly an issue with a small number of items which take a long time.


Discussion:
Duplicate of #1353.
I saw that issue – fine if you prefer to group them. It's not necessarily the same issue though: would be possible to display the last item _and_ start showing from the 0th item.
This was introduced in #487. I just ran into the same confusion when fixing the tests for #1353/#1354, the first line of progress wasn't shown and it wasn't clear why. Reading #487, I'm not convinced by the reasoning, if you have a fast machine or fast process, it doesn't hurt to output the progress bar. I'm going to revert that.

*Maybe* it would make sense to somehow hide the progress bar if *all* iterations finish quickly, but otherwise you end up in this weird situation where the bar displays late because the first iteration is always "fast". I don't see a simple way to implement that though, and in general want to avoid adding complexity to Click's progress bar at this point.

Resolving patch:
diff --git a/src/click/_termui_impl.py b/src/click/_termui_impl.py
--- a/src/click/_termui_impl.py
+++ b/src/click/_termui_impl.py
@@ -102,7 +102,6 @@ def __init__(
         self.current_item = None
         self.is_hidden = not isatty(self.file)
         self._last_line = None
-        self.short_limit = 0.5
 
     def __enter__(self):
         self.entered = True
@@ -126,11 +125,8 @@ def __next__(self):
         # twice works and does "what you want".
         return next(iter(self))
 
-    def is_fast(self):
-        return time.time() - self.start <= self.short_limit
-
     def render_finish(self):
-        if self.is_hidden or self.is_fast():
+        if self.is_hidden:
             return
         self.file.write(AFTER_BAR)
         self.file.flush()
@@ -263,7 +259,7 @@ def render_progress(self):
         line = "".join(buf)
         # Render the line only if it changed.
 
-        if line != self._last_line and not self.is_fast():
+        if line != self._last_line:
             self._last_line = line
             echo(line, file=self.file, color=self.color, nl=False)
             self.file.flush()
diff --git a/src/click/termui.py b/src/click/termui.py
--- a/src/click/termui.py
+++ b/src/click/termui.py
@@ -390,6 +390,9 @@ def progressbar(
     :param update_min_steps: Render only when this many updates have
         completed. This allows tuning for very fast iterators.
 
+    .. versionchanged:: 8.0
+        Output is shown even if execution time is less than 0.5 seconds.
+
     .. versionchanged:: 8.0
         Labels are echoed if the output is not a TTY. Reverts a change
         in 7.0 that removed all output.


Test patch:
diff --git a/tests/test_termui.py b/tests/test_termui.py
--- a/tests/test_termui.py
+++ b/tests/test_termui.py
@@ -27,16 +27,14 @@ def _create_progress(length=10, length_known=True, **kwargs):
 
 
 def test_progressbar_strip_regression(runner, monkeypatch):
-    fake_clock = FakeClock()
     label = "    padded line"
 
     @click.command()
     def cli():
         with _create_progress(label=label) as progress:
             for _ in progress:
-                fake_clock.advance_time()
+                pass
 
-    monkeypatch.setattr(time, "time", fake_clock.time)
     monkeypatch.setattr(click._termui_impl, "isatty", lambda _: True)
     assert label in runner.invoke(cli, []).output
 
@@ -60,30 +58,24 @@ def __next__(self):
 
         next = __next__
 
-    fake_clock = FakeClock()
-
     @click.command()
     def cli():
         with click.progressbar(Hinted(10), label="test") as progress:
             for _ in progress:
-                fake_clock.advance_time()
+                pass
 
-    monkeypatch.setattr(time, "time", fake_clock.time)
     monkeypatch.setattr(click._termui_impl, "isatty", lambda _: True)
     result = runner.invoke(cli, [])
     assert result.exception is None
 
 
 def test_progressbar_hidden(runner, monkeypatch):
-    fake_clock = FakeClock()
-
     @click.command()
     def cli():
         with _create_progress(label="working") as progress:
             for _ in progress:
-                fake_clock.advance_time()
+                pass
 
-    monkeypatch.setattr(time, "time", fake_clock.time)
     monkeypatch.setattr(click._termui_impl, "isatty", lambda _: False)
     assert runner.invoke(cli, []).output == "working\n"
 
@@ -193,21 +185,16 @@ def test_progressbar_iter_outside_with_exceptions(runner):
 
 
 def test_progressbar_is_iterator(runner, monkeypatch):
-    fake_clock = FakeClock()
-
     @click.command()
     def cli():
         with click.progressbar(range(10), label="test") as progress:
             while True:
                 try:
                     next(progress)
-                    fake_clock.advance_time()
                 except StopIteration:
                     break
 
-    monkeypatch.setattr(time, "time", fake_clock.time)
     monkeypatch.setattr(click._termui_impl, "isatty", lambda _: True)
-
     result = runner.invoke(cli, [])
     assert result.exception is None
 
@@ -289,50 +276,42 @@ def cli():
 
     lines = [line for line in output.split("\n") if "[" in line]
 
-    assert " 25%  00:00:03" in lines[0]
-    assert " 50%  00:00:02" in lines[1]
-    assert " 75%  00:00:01" in lines[2]
-    assert "100%          " in lines[3]
+    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]
 
 
 def test_progressbar_item_show_func(runner, monkeypatch):
-    fake_clock = FakeClock()
-
     @click.command()
     def cli():
         with click.progressbar(
-            range(4), item_show_func=lambda x: f"Custom {x}"
+            range(3), item_show_func=lambda x: f"Custom {x}"
         ) as progress:
             for _ in progress:
-                fake_clock.advance_time()
-                print("")
+                click.echo()
 
-    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 "Custom 0" in lines[0]
-    assert "Custom 1" in lines[1]
-    assert "Custom 2" in lines[2]
-    assert "Custom None" in lines[3]
+    lines = [line for line in output.splitlines() if "[" in line]
+    assert "Custom None" in lines[0]
+    assert "Custom 0" in lines[1]
+    assert "Custom 1" in lines[2]
+    assert "Custom 2" in lines[3]
+    assert "Custom None" in lines[4]
 
 
 def test_progressbar_update_with_item_show_func(runner, monkeypatch):
-    fake_clock = FakeClock()
-
     @click.command()
     def cli():
         with click.progressbar(
             length=6, item_show_func=lambda x: f"Custom {x}"
         ) as progress:
             while not progress.finished:
-                fake_clock.advance_time()
                 progress.update(2, progress.pos)
-                print("")
+                click.echo()
 
-    monkeypatch.setattr(time, "time", fake_clock.time)
     monkeypatch.setattr(click._termui_impl, "isatty", lambda _: True)
     output = runner.invoke(cli, []).output