Rendering and the update gate¶
bar.update(value) does not redraw the line every time you call it. If it
did, a tight loop over a million items would spend most of its time writing
to the terminal instead of doing the work the bar is measuring. Instead,
every ProgressBar (and everything built on it, including
FastProgressBar) sits behind two independent checks, described below,
that decide whether a given update() call actually produces output.
Both live in progressbar.bar.
The integer gate: deciding whether to even look¶
The cheapest possible check is one integer comparison, and that is what runs
on every iteration of progressbar.progressbar(iterable) and every
explicit update() call. Internally, each bar tracks a threshold,
_next_update: while value is still below it, update() records the
new value and returns immediately, without touching _needs_update() or
any widget at all.
The threshold is not a fixed step. After each real redraw, it is recalibrated
from how much value changed and how much wall-clock time that took, aimed
at roughly one redraw per min_poll_interval window (see below). A loop
that turns out to run faster than expected – the threshold was reached but
no redraw was actually due – causes the step to double instead of shrink,
backing off further. The gate starts at a step of 1 (redraw every call)
so a slow loop, where real time passes between iterations, is never skipped
before a timing measurement has had a chance to grow it.
This gate keeps the common iteration down to an increment, a compare, and a store. That is why the fast path can claim single-digit-nanosecond overhead per iteration in the common case: most iterations never reach the more expensive check described next.
_needs_update(): deciding whether to actually redraw¶
Once the integer gate lets a call through, _needs_update() makes the
real decision, in this order:
If the bar is paused, never redraw.
If less than
min_poll_intervalseconds have passed since the last redraw, don’t redraw – this is the hard rate limit.If
poll_intervalis set and more than that many seconds have passed, redraw unconditionally, even if the value hasn’t changed. This exists for widgets that depend on elapsed time alone, such as aTimeror an animated marker, which need to keep moving even whilevalueis static.If
max_valueis unknown, redraw whenevervaluehas changed at all (still subject to the rate limit above).Otherwise, redraw only if the value crossed a threshold large enough to move the rendered bar by at least one terminal column, computed from
max_valueand the currentterm_width. A bar with a hugemax_valueon a normal-width terminal redraws far less often, per unit ofvalue, than one where each unit is visually significant.
force=True¶
update(value, force=True) always redraws: the integer gate never skips
a forced call, and force overrides whatever _needs_update()
answers, so the line is redrawn regardless of timing or value movement.
start() and finish() both call update(..., force=True)
internally, which is why a bar always shows 0% on start and 100% (or its
final state) on finish even if the gate would otherwise have skipped that
exact value. MultiBar also renders its child
bars with force=True on every tick of its own render loop, since it
manages the redraw cadence itself.
min_poll_interval vs. poll_interval¶
These are opposites: one is a floor, the other is a ceiling.
min_poll_intervalis the minimum time between redraws – a rate limit. It defaults to0.050seconds (20 redraws/second), and cannot go lower: the effective floor is the largest of the constructor argument, the hard-coded0.050minimum, and thePROGRESSBAR_MINIMUM_UPDATE_INTERVALenvironment variable. That environment variable can only raise the floor above whatever the code already requested, never lower it.poll_intervalis the maximum time between redraws – a forced refresh. It has no default (None, meaning “never force a redraw on time alone”). Set it when your widgets show elapsed time or an animation and should keep visibly moving even while the tracked value sits still.
Both are seconds (or anything progressbar.utils.deltas_to_seconds()
accepts, such as a datetime.timedelta), and both are documented as
constructor arguments on ProgressBar.
Why this matters for piped output¶
Because the rate limit applies regardless of how fast value changes,
redirecting a bar’s output through tee or into a log file (see
Terminal detection) still produces a bounded number of lines rather
than one per unit of progress – the gate throttles it the same way it
throttles a fast interactive redraw.