Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions humanfriendly/terminal/spinners.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@

# Modules included in our package.
from humanfriendly import Timer
from humanfriendly.compat import monotonic
from humanfriendly.deprecation import deprecated_args
from humanfriendly.terminal import ANSI_ERASE_LINE

Expand Down Expand Up @@ -166,7 +167,7 @@ def __init__(self, **options):
self.total = options.get('total')
# Define instance variables.
self.counter = 0
self.last_update = 0
self.last_update = None
# Try to automatically discover whether the stream is connected to
# a terminal, but don't fail if no isatty() method is available.
if self.interactive is None:
Expand Down Expand Up @@ -199,8 +200,8 @@ def step(self, progress=0, label=None):
99% of the time.
"""
if self.interactive:
time_now = time.time()
if time_now - self.last_update >= self.interval:
time_now = monotonic()
if self.last_update is None or time_now - self.last_update >= self.interval:
self.last_update = time_now
state = self.states[self.counter % len(self.states)]
label = label or self.label
Expand All @@ -226,7 +227,7 @@ def sleep(self):
This method doesn't redraw the spinner, you still have to call
:func:`step()` in order to do that.
"""
time.sleep(MINIMUM_INTERVAL)
time.sleep(self.interval)

def clear(self):
"""
Expand Down
25 changes: 25 additions & 0 deletions humanfriendly/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,31 @@ def test_spinner(self):
self.assertTrue(all('%' in line for line in lines))
self.assertEqual(sorted(set(lines)), sorted(lines))

def test_spinner_uses_elapsed_time(self):
"""Keep redrawing after a wall-clock correction and at clock zero."""
from humanfriendly.terminal import spinners
stream = StringIO()
clock = [0.0]
with PatchedAttribute(spinners, 'monotonic', lambda: clock[0]):
with PatchedAttribute(spinners.time, 'time', lambda: 1000 - clock[0]):
spinner = Spinner(label='working', stream=stream, interactive=True, interval=1)
spinner.step()
assert spinner.counter == 1
clock[0] = 0.5
spinner.step()
assert spinner.counter == 1
clock[0] = 1.0
spinner.step()
assert spinner.counter == 2

def test_spinner_sleep_uses_configured_interval(self):
"""Sleep for the interval chosen for the spinner's redraw cadence."""
from humanfriendly.terminal import spinners
sleep = MagicMock()
with PatchedAttribute(spinners.time, 'sleep', sleep):
Spinner(interval=0.7).sleep()
sleep.assert_called_once_with(0.7)

def test_automatic_spinner(self):
"""
Test :func:`humanfriendly.AutomaticSpinner`.
Expand Down