Community Python Snippet
A stdout Progress Bar Without a Library (Python)
tqdm is wonderful but adds 30k of dependencies for a 30-line job. Here is the pure-stdlib progress bar I drop into ETL scripts when I just want to know how far through the file I am.
A stdout Progress Bar Without a Library (Python)
tqdm is wonderful but adds 30k of dependencies for a 30-line job. Here is the pure-stdlib progress bar I drop into ETL scripts when I just want to know how far through the file I am.
By @rajtanaka
March 31, 2026
·
Updated May 20, 2026
359 views
8
Rate
Three small choices keep this honest. The redraw throttle (0.05s) means the bar updates at 20fps regardless of how fast the loop is, so a hot loop does not pay a syscall on every iteration. The TTY check makes the bar collapse into a flat print when output is piped to a file, so log files stay readable. The rate calculation uses elapsed-since-start rather than a windowed estimate, which is slower to react but never produces a 'eta=0.1s' lie when the loop has been running for an hour. I have shipped this in two ETL scripts; the first time anyone needs more (multi-bar, nested progress, byte-rate units) I switch to tqdm and stop pretending.
The generator wrapper is what makes this a drop-in for any for x in items loop: replace items with track(items) and you get progress for free. The total parameter is needed because some iterables (generators, infinite sequences) have no __len__; for those you either pass total=N from the caller or fall back to materializing the full iterable, which the snippet does only when the caller does not supply total. Matching tqdm's API name and signature is deliberate: when the project grows up and we install tqdm, every call site that uses track(...) switches by changing one import line. I have done that migration twice and it took three minutes each time.
The bytes variant is the same shape with one swap: the human-readable formatter for sizes and rates. human_bytes walks the unit ladder until the value is below 1024, which is the cleanest pure-stdlib equivalent of humanize.naturalsize and avoids the dependency. The throttle still applies; flushing 100 times per second on a fast SSD copy is otherwise where you spend a measurable fraction of the wall-clock time. The TTY branch matters even more here because file-copy scripts are the ones most often piped to a log file; without it your nightly backup log fills with terminal escape codes.
