Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: wolph/python-progressbar
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: develop
Choose a base ref
...
head repository: wolph/python-progressbar
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: fast-progressbar
Choose a head ref
Checking mergeability… Don’t worry, you can still create the pull request.
  • 15 commits
  • 23 files changed
  • 1 contributor

Commits on Jun 23, 2026

  1. Add behavior-preserving fast path: ~31 ns/iter (was ~254)

    Wrapping a loop with progressbar2 dropped from ~254 ns/iter to ~31 ns
    (~11x faster, ~1.8x faster than tqdm, 2nd only to rich), with no change
    to observable behavior.
    
    How: an integer "next-update" gate. The common iteration is just an
    increment, a compare, and the value/previous_value liveness stores; the
    expensive redraw machinery (clock read + widget formatting) only runs at
    rate-limited crossings (~20x/sec). The gate calibrates _gate_step from a
    real timing measurement and self-corrects via a tqdm-style closed loop,
    so it can only skip iterations, never force a wrong redraw. The iterator
    path is a single inlined generator (the shortcut wrapper layer is
    collapsed); the manual update()/+= path skips its per-call clock read
    below the threshold.
    
    Backward compatibility:
    - Public API unchanged; bar.value and previous_value stay byte-identical
      to the pre-gate behavior on every iteration.
    - Same widgets, same redraw cadence, same finish/break/exception handling.
    - PROGRESSBAR_DISABLE_FASTPATH (and min_poll_interval=0) revert to the
      original per-iteration path.
    
    Also:
    - Reproducible benchmark suite (benchmarks/) vs tqdm/rich/alive-progress/
      click, all rendered to a real pseudo-terminal; documented in README.
    - CI per-iteration performance budget guard (machine-independent ratio)
      to prevent regressions.
    - no_color/len_color skip the ANSI-strip regex on plain text (cuts the
      forced-redraw render cost).
    - Full suite green at 100% branch coverage; ruff and pyright clean.
    wolph committed Jun 23, 2026
    Configuration menu
    Copy the full SHA
    c89cb10 View commit details
    Browse the repository at this point in the history
  2. Add optional native iterator accelerator: ~5 ns/iter, ~4x faster than…

    … rich
    
    `ProgressBar.__iter__` now dispatches to `speedups.progressbar.FastBarIterator`
    (the `progressbar2[fast]` extra) when it is importable, falling back to the
    pure-Python generator otherwise. The native iterator counts items in a C field
    and only calls back into Python at redraw crossings via a small protocol
    (`_fast_begin`/`_fast_tick`/`_fast_end`/`_fast_end_dirty`), reusing the existing
    gate/redraw/calibration machinery so the redraw cadence is identical. The only
    behavioural difference is that `value`/`previous_value` are synced at crossings
    rather than every iteration, so reads between redraws lag slightly (like
    tqdm.n); `PROGRESSBAR_DISABLE_FASTPATH=1` forces the pure-Python path.
    
    This makes progressbar2 the fastest progress bar measured: ~5 ns/iter vs rich
    19, tqdm 55. Pure Python stays ~30 ns (no native build), still ~1.8x faster
    than tqdm and 2nd to rich.
    
    Also:
    - hoist `_gate_enabled` to a local in the pure-Python iterator (free, no
      behaviour change), trimming the fallback hot path a few ns.
    - conftest `disable_native_accelerator` autouse fixture forces the pure-Python
      path for the rest of the suite; native behaviour is covered explicitly in
      tests/test_native_accelerator.py (dispatch + hooks covered without the
      compiled package via a fake/direct calls, so CI stays at 100% coverage; real
      end-to-end equivalence + issue #212 break/exception cleanup tests run where
      speedups is installed).
    - refresh benchmark artifacts + README performance section.
    wolph committed Jun 23, 2026
    Configuration menu
    Copy the full SHA
    c2308c2 View commit details
    Browse the repository at this point in the history

Commits on Jun 24, 2026

  1. Lighten import (~48->24ms) and trim forced-render overhead

    Import: with the companion python_utils lazy-import change, `import progressbar`
    no longer eagerly pulls in asyncio or typing_extensions, dropping cold import
    from ~48ms to ~24ms (net of interpreter startup) -- on par with tqdm/click and
    roughly half of rich. (Requires the python_utils release that defers those
    imports; progressbar itself imports python_utils lazily where it can.)
    
    Render: FormatLabel.__call__ no longer wraps every mapping entry in a
    contextlib.suppress on the redraw hot path -- a missing key (the only common
    miss) is tested directly and only the value transform is guarded. The bulk of
    the forced-per-update render cost (~24us) is inherent to the richer default
    widgets (gradient bar, time widgets), so this is a modest trim, not a headline.
    
    Benchmark artifacts + README refreshed: import ~24ms, iteration ~5ns (fastest),
    forced render ~24us.
    wolph committed Jun 24, 2026
    Configuration menu
    Copy the full SHA
    f93843a View commit details
    Browse the repository at this point in the history
  2. Configuration menu
    Copy the full SHA
    42390a3 View commit details
    Browse the repository at this point in the history
  3. Configuration menu
    Copy the full SHA
    21e908b View commit details
    Browse the repository at this point in the history
  4. Configuration menu
    Copy the full SHA
    d17db1e View commit details
    Browse the repository at this point in the history
  5. Configuration menu
    Copy the full SHA
    53fdb1e View commit details
    Browse the repository at this point in the history
  6. Configuration menu
    Copy the full SHA
    f956d3c View commit details
    Browse the repository at this point in the history
  7. Configuration menu
    Copy the full SHA
    058eb74 View commit details
    Browse the repository at this point in the history
  8. Configuration menu
    Copy the full SHA
    c011529 View commit details
    Browse the repository at this point in the history
  9. Configuration menu
    Copy the full SHA
    17961bd View commit details
    Browse the repository at this point in the history
  10. docs: document fast default path; refresh benchmark artifacts

    The default progressbar() now auto-uses the lean FastProgressBar: ~5 ns/iter,
    ~5 us/render (~2x faster than tqdm), ~1.5 ms import (lazy). Full widget/gradient
    bar remains via widgets=/fast=False/ProgressBar(...). Benchmark adds a
    fast-render scenario; README Performance section rewritten for all three axes.
    wolph committed Jun 24, 2026
    Configuration menu
    Copy the full SHA
    253c6a0 View commit details
    Browse the repository at this point in the history
  11. Configuration menu
    Copy the full SHA
    c9fc9b2 View commit details
    Browse the repository at this point in the history
  12. fix: clear CodeQL cyclic-import + mixed-import findings

    - bar.py loads widgets via importlib helper (_load_widgets) instead of static
      `from . import widgets`, removing the static bar->widgets cycle edges CodeQL
      flagged while keeping the deferred (fast-path-safe) load. Widget annotations
      typed loosely to drop the last TYPE_CHECKING bar->widgets edge; the public
      progressbar() shortcut keeps precise WidgetBase typing.
    - tests/test_fast_default.py uses a single import style (alias, no `from`).
    wolph committed Jun 24, 2026
    Configuration menu
    Copy the full SHA
    27df953 View commit details
    Browse the repository at this point in the history

Commits on Jun 25, 2026

  1. fix: pre-warm widgets in multi.py to avoid render-thread import race

    After deferring the widgets import out of bar.py (fast-path import slim-down),
    a MultiBar child bar's first start() would import the widgets module lazily --
    and MultiBar runs start()/render from background threads, so that cold import
    could race MultiBar._label_bar's `assert bar.widgets` (intermittent CI flake on
    test_multibar). multi.py now imports widgets eagerly at module load (single-
    threaded; this module is itself lazy-loaded only when MultiBar is used), so the
    fast path and bare `import progressbar` stay widgets-free.
    wolph committed Jun 25, 2026
    Configuration menu
    Copy the full SHA
    94918a8 View commit details
    Browse the repository at this point in the history
Loading