Add FastBarIterator: native iterator accelerator for python-progressbar - #15
Conversation
A small Cython cdef iterator (Approach B) that wraps an iterable and drives a progressbar2 ProgressBar, counting items in a C field so the per-item loop does no Python attribute writes. `bar.value`/`previous_value` are synced only at redraw crossings (~20x/sec, like tqdm.n) and once more at finish. The bar implements a small protocol consumed here: `_fast_begin()`, `_fast_tick(value)`, `_fast_end()`, `_fast_end_dirty()` plus the plain attributes `value`, `_next_update`, `_finished`. `__dealloc__` calls `_fast_end_dirty()` when iteration is abandoned (break/exception) so redirected streams are restored, matching the generator's GeneratorExit cleanup (progressbar issue #212). progressbar2 imports this automatically as the `progressbar2[fast]` extra and falls back to its pure-Python gate when absent. Measured ~5 ns/iter (~4x faster than rich) vs ~30 ns pure Python. Includes a .pyi stub and indirect integration tests (fake bar) since Cython modules can't be measured by Python coverage.
There was a problem hiding this comment.
Code Review
This pull request introduces a native iterator accelerator (FastBarIterator) implemented in Cython to optimize the performance of progressbar2. It includes the Cython implementation, type stubs, setup configuration, and comprehensive integration tests using a mock progress bar. The feedback suggests catching BaseException instead of Exception in the __dealloc__ method of FastBarIterator to ensure no exceptions escape during teardown or interpreter shutdown.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
Pull request overview
This PR introduces a new Cython-backed iterator accelerator (speedups.progressbar.FastBarIterator) intended to drive progressbar2’s ProgressBar via a small “fast protocol”, reducing per-iteration Python overhead by keeping the counter in a C field and only syncing state at redraw boundaries and at completion.
Changes:
- Added
FastBarIteratoras a new Cython extension module (speedups/progressbar.pyx). - Added a matching type stub (
speedups/progressbar.pyi) and new integration-style tests using a protocol-compatible fake bar (tests/test_progressbar.py). - Registered the new extension for build via
setup.py.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
speedups/progressbar.pyx |
Adds the Cython FastBarIterator implementation (fast protocol driver + __dealloc__ dirty-finish teardown). |
speedups/progressbar.pyi |
Provides typing for the new native iterator module. |
tests/test_progressbar.py |
Adds integration tests validating iteration order, tick crossings, and dealloc dirty-finish behavior. |
setup.py |
Includes speedups.progressbar in the cythonized extension list. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e4a228f691
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
- Finish exactly once on exhaustion: repeated next() on an exhausted iterator no longer re-runs bar._fast_end(), matching the pure-Python generator fallback. - Catch BaseException (not Exception) in __dealloc__ so nothing can escape teardown during interpreter shutdown. - Clarify module/stub docstrings: the iterator writes back only bar.value; previous_value is maintained by the bar's own _fast_tick()/_fast_end() hooks via update()/finish(). - Add tests: dirty finish on consumer/source exceptions, single clean finish on repeated next().
numpy 2.3+ stubs use PEP 695 `type` statements, which mypy rejects when python_version targets <3.12. Pyright keeps enforcing 3.10 compatibility.
Adds a small Cython
cdefiterator (speedups.progressbar.FastBarIterator, "Approach B") that wraps an iterable and drives aprogressbar2ProgressBar, counting items in a C field so the per-item loop does no Python attribute writes.bar.value/previous_valueare synced only at redraw crossings (~20x/sec, liketqdm.n) and once more at finish.progressbar2 imports this automatically as the
progressbar2[fast]extra and falls back to its pure-Python gate when absent. Measured ~5 ns/iter (~4x faster than rich, ~11x faster than tqdm) vs ~30 ns pure Python.Protocol consumed from the bar:
_fast_begin(),_fast_tick(value),_fast_end(),_fast_end_dirty()+ plain attrsvalue/_next_update/_finished.__dealloc__calls_fast_end_dirty()on early break/exception so redirected streams are restored (progressbar issue #212).Includes a
.pyistub and indirect integration tests (Cython modules can't be measured by Python coverage). Companion progressbar2 PR: wolph/python-progressbar#316.