Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
837be15
fix: point deprecation warnings at the caller
wolph Sep 8, 2026
235e7e7
Merge branch 'fix/deprecation-stacklevel' into develop
wolph Sep 13, 2026
5924c0c
Make StreamWrapper listener handling safe on free-threaded builds
wolph Oct 22, 2026
fded969
Stop format templates from reaching dunder attributes
wolph Oct 22, 2026
77b29a3
Harden the pv-style command line copy
wolph Oct 22, 2026
697d5eb
Keep batch labels inert and finish every task bar in the parallel dis…
wolph Oct 22, 2026
a0bdce4
Only run the Windows console-mode probe on a tty
wolph Oct 22, 2026
ce69c60
Guard the release workflow against the wrong ref and stray files
wolph Oct 22, 2026
06f6efa
Keep StreamWrapper transitions free of call-outs and fork-safe
wolph Oct 25, 2026
f09451b
Keep the desc prefix a FormatLabel and refuse frame attribute walks
wolph Oct 25, 2026
cd11b16
Close the remaining ways the CLI could truncate or exit non-zero
wolph Oct 25, 2026
b448a01
Tolerate a MultiBar bar removed while its finished frame renders
wolph Oct 25, 2026
629f35d
Read an annotated __version__ in the release guard and anchor stray n…
wolph Oct 25, 2026
7f11953
Make the imap_unordered ordering test independent of timing
wolph Oct 25, 2026
517d16d
Finish the deep-copied bars in the stream pickle test
wolph Oct 25, 2026
8529679
Deliver the other stream's text when one original stream fails
wolph Oct 25, 2026
224d51c
Restore the SIGWINCH handler however the last bar goes away
wolph Oct 28, 2026
fe620af
Keep a captured write from being lost while the buffer drains
wolph Oct 28, 2026
d99a254
Treat an -o path that aliases stdout like stdout on a broken pipe
wolph Oct 28, 2026
0b178ca
Require python-utils 4.1.4 for a non-negative scale_1024 power
wolph Oct 28, 2026
5d80bb1
Fix stream capture callbacks and closed stdin validation
wolph Oct 29, 2026
1b79d06
Notify capture hooks when stream operations fail
wolph Oct 29, 2026
61e6cf4
[autofix.ci] apply automated fixes
autofix-ci[bot] Oct 29, 2026
aadba28
Fix remaining stream, formatter and MultiBar regressions
wolph Oct 29, 2026
2e9c6bf
Preserve custom label callbacks and chained stream errors
wolph Oct 29, 2026
ee395cd
Merge commit '2e9c6bf' into perf/match-4.6.0
wolph Nov 4, 2026
14233ac
test: strip the default widget colours in two format-security label t…
wolph Oct 28, 2026
63dded0
docs: state the CLI exit statuses and that desc is literal text after…
wolph Oct 28, 2026
244d894
fix: guard the fork hook by platform so pyright on Windows accepts it…
wolph Oct 28, 2026
d9ae577
Validate a format template once and render it with str.format_map
wolph Nov 4, 2026
8e4701f
test: strip the default widget colours in the three specialised-templ…
wolph Nov 4, 2026
16050a3
Merge branch 'perf/template' into perf/match-4.6.0
wolph Nov 4, 2026
0c870fc
Take the capture lock cheaply and skip empty stream flushes on redraw
wolph Nov 4, 2026
1219151
Keep IndexError for positional fields in validated templates
wolph Nov 4, 2026
0e118ad
Merge branch 'perf/stream' into perf/match-4.6.0
wolph Nov 4, 2026
9546103
Render default widget colours without the remap and Color round trip
wolph Nov 4, 2026
79dfde9
Merge branch 'perf/colour' into perf/match-4.6.0
wolph Nov 4, 2026
0c848e4
Call flush() again when the last capture stops
wolph Nov 4, 2026
ffe3738
Mention the python-utils floor in the changelog
wolph Nov 4, 2026
2200764
Merge branch 'perf/hooks' into perf/match-4.6.0
wolph Nov 4, 2026
f4f780b
Remember rendered colour escapes per gradient and keep str subclasses
wolph Nov 4, 2026
47e7095
Merge branch 'perf/colour' into perf/match-4.6.0
wolph Nov 4, 2026
f3bcd8a
Keep the 4.6.0 width arithmetic on the fast path when there are no la…
wolph Nov 4, 2026
de14b03
Split asserts with side effects and drop the colour cache global
wolph Nov 4, 2026
58562f0
Force colours in the coloured-label fast path test
wolph Nov 4, 2026
8b2ccb9
Build the no-init subclass fixtures without a constructor
wolph Nov 4, 2026
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
61 changes: 60 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ on:
required: true
type: string

permissions:
contents: read

jobs:
build:
name: Build distributions
Expand All @@ -18,15 +21,71 @@ jobs:
- uses: actions/checkout@v6
with:
ref: ${{ inputs.ref || github.ref }}
- name: Verify the ref is a release tag matching __version__
env:
EVENT_NAME: ${{ github.event_name }}
EVENT_REF: ${{ github.ref }}
INPUT_REF: ${{ inputs.ref }}
run: |
set -euo pipefail
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
ref="$INPUT_REF"
else
ref="$EVENT_REF"
fi
case "$ref" in
refs/tags/v*) tag="${ref#refs/tags/}" ;;
v*)
if [ "$EVENT_NAME" != "workflow_dispatch" ]; then
echo "::error::ref '$ref' is not a tag ref"
exit 1
fi
tag="$ref"
;;
*)
echo "::error::ref '$ref' is not a v* release tag"
exit 1
;;
esac
if ! [[ "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+[0-9A-Za-z.+-]*$ ]]; then
echo "::error::tag '$tag' does not look like vX.Y.Z"
exit 1
fi
version="$(sed -n -E "s/^__version__(: *str)? = ['\"]([^'\"]*)['\"].*/\2/p" progressbar/__about__.py)"
if [ -z "$version" ]; then
echo "::error::could not read __version__ from progressbar/__about__.py"
exit 1
fi
if [ "$tag" != "v$version" ]; then
echo "::error::tag '$tag' does not match __version__ '$version'"
exit 1
fi
tag_commit="$(git rev-parse --verify --quiet "refs/tags/$tag^{commit}")" || {
echo "::error::tag '$tag' is not present in the checkout"
exit 1
}
if [ "$tag_commit" != "$(git rev-parse HEAD)" ]; then
echo "::error::checked-out commit is not the commit of tag '$tag'"
exit 1
fi
echo "Releasing $tag ($tag_commit)"
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Build sdist and wheel
run: uv build --no-sources
- name: Verify wheel contains only the progressbar package
- name: Verify distribution contents
run: |
set -euo pipefail
unpacked="$(unzip -Z1 dist/*.whl | cut -d/ -f1 | sort -u)"
echo "$unpacked"
test "$(echo "$unpacked" | grep -cvE '^progressbar$|^progressbar2-.*\.dist-info$')" -eq 0
sdist="$(tar tzf dist/*.tar.gz)"
echo "$sdist"
stray_pattern='\.sync-conflict-|\.(orig|rej|bak)$'
if { unzip -Z1 dist/*.whl; echo "$sdist"; } | grep -E "$stray_pattern"; then
echo "::error::distribution contains stray conflict or backup files"
exit 1
fi
- uses: actions/upload-artifact@v6
with:
name: dist
Expand Down
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,11 @@ docs/_build/

# Built at docs build time for the in-browser console
docs/_static/wheels/

# Local secrets and environments
.env
.env.*
.venv/
*.pem
*.key
.pypirc
2 changes: 2 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ repos:
rev: v6.0.0
hooks:
- id: end-of-file-fixer
# Preserve the upstream bytes copied by the vendor update command.
exclude: '^docs/_static/vendor/xterm\.js$'
- id: trailing-whitespace
- id: check-yaml
- id: check-added-large-files
Expand Down
38 changes: 38 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,44 @@
Changelog
=========

Unreleased
----------

The ``progressbar`` and ``bar`` commands, and ``python -m progressbar``,
now exit with status 130 after Ctrl-C interrupts a transfer. Previously
they exited with status 0. Scripts that check the exit status can now
distinguish an interrupted transfer from a completed copy.

The ``python-utils`` dependency now requires 4.1.4 or newer, up from
3.8.1. Older releases render a negative ``scale_1024`` power as
``0.1 YiB`` instead of the byte count. An environment that pins
``python-utils<4`` has to lift that pin before upgrading.

Captured output now survives a drain failure on the other stream, and
unwrapping restores the original stream even when its capture buffer is
closed. Built-in ``StringIO`` capture writes and drains remain atomic.
Caller-replaced buffers keep their method overrides, which run without
stream locks. As in 4.6.0, callers synchronise concurrent writes and drains
on those custom buffers. A capture-hook failure now retains the other
stream's exception in the primary failure's context chain.

Specialised format templates retain their escaping and result types.
Their field paths, including nested format specifications, are checked
before calling the application's formatter.

MultiBar expiry keeps a replacement registered under the same exact
``str`` label. Assignment, update, in-place union and ordinary removal
methods share its membership guard. Lifecycle tracking uses bar identity
without calling application hash or equality methods, and the guard resets
after fork.
Custom label objects retain their hash and equality hooks and their
identity as dictionary keys. Using one for mutation or indexing switches
that MultiBar instance permanently to the 4.6.0 concurrency contract:
callers synchronise concurrent mutation and rendering, including expiry.
These hooks run without the membership guard, even for later exact
``str`` lookups. Clearing the instance does not reset this mode. Direct
calls to ``dict`` base-class mutation methods bypass this protocol.

For the most recent changes to the Python Progressbar please look at the Git
releases or the commit log:

Expand Down
14 changes: 10 additions & 4 deletions CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,20 @@ Ready to contribute? Here's how to set up `python-progressbar` for local develop

Now you can make your changes locally.

5. When you're done making changes, check that your changes pass the
linter and the tests, including other Python versions with tox::
5. Before committing, format the changed Python files, then check the
whole tree and run the tests, including other Python versions with tox::

$ uv run ruff check progressbar tests
$ uvx ruff@latest format path/to/changed.py
$ uvx ruff@latest check .
$ uvx ruff@latest format --check .
$ uv run pytest
$ uv run tox

The ``uv sync`` from step 3 already installed all of these.
Stage the formatted changes before committing. A checkout-local
pre-commit gate, when installed, checks the staged snapshot with current
Ruff and the configured pre-commit hooks, including the CI-pinned Ruff
version. It leaves partially staged files untouched. Configured fixes
run only in the disposable snapshot and cause the commit to be rejected.

6. Commit your changes and push your branch to GitHub with `git-flow-avh`_::

Expand Down
2 changes: 1 addition & 1 deletion benchmarks/results.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,4 @@
}
}
}
}
}
5 changes: 3 additions & 2 deletions docs/howto/tqdm-style.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ here.
.. demo:: howto/tqdm-style

``desc`` and ``total`` work on any ``ProgressBar``: ``desc`` becomes the
prefix (rendered as ``f'{desc}: '``), and ``total`` becomes ``max_value``
when ``max_value`` isn't given explicitly. ``unit=``/``unit_scale=``
prefix (rendered as ``f'{desc}: '``, as literal text, so braces in a file
name stay braces), and ``total`` becomes ``max_value`` when ``max_value``
isn't given explicitly. ``unit=``/``unit_scale=``
don't render anything by themselves -- they only show up through a
widget that reads them, such as ``UnitProgress()`` -- so pass a widget
list that includes one if you want them visible, as this demo does
Expand Down
12 changes: 12 additions & 0 deletions docs/reference/cli.rst
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,15 @@ General options

``-h``/``--help`` shows the built-in ``argparse`` help and exits.
``-P``/``--pidfile`` is accepted but not wired up.

Exit status
==============

A complete copy exits ``0``, and so does a copy whose reader goes away
early (``progressbar big.bin | head``): the bar is finished quietly and
nothing is printed about the broken pipe. An invalid size value, a
missing, unreadable or directory input, a directory as output, or an
output path that is also one of the inputs is reported before the output
file is opened, so the output is left untouched, and the command exits
``2`` like any other usage error. Ctrl-C finishes the bar and exits
``130``.
Loading
Loading