Skip to content

[REQUEST] SpeedColumn for arbitrary units (rows, files, it/s) #4034

@mohdarif8299

Description

@mohdarif8299

Feature Request: Add a Generic SpeedColumn to progress.py

Summary

Add a new SpeedColumn progress column that displays speed for arbitrary units (iterations, rows, files, etc.), complementing the existing TransferSpeedColumn which is specialized for byte transfer speeds.

Problem / Motivation

Currently, TransferSpeedColumn displays transfer speed in bytes (e.g., 3.2 MB/s), which is ideal for file downloads or data transfers. However, many progress use cases track non-byte quantities:

  • Rows processed in a CSV or database
  • Web requests completed
  • Images or items processed
  • API calls made
  • Generic iterations over any sequence

There is no standalone column in rich/progress.py that can display speed for these cases. While TaskProgressColumn has a show_speed option that displays iterations per second when total is unknown, it is embedded within the percentage column and uses a fixed "it/s" unit with scientific notation (e.g., 5.0×10³ it/s). Users cannot easily show a dedicated speed column with a custom unit like "rows/s" or "files/s".

Proposed Solution

Add a new ProgressColumn class called SpeedColumn in rich/progress.py with the following behavior:

API

SpeedColumn(unit: str = "it", table_column: Optional[Column] = None)
  • unit: A short string describing the unit (e.g., "it", "rows", "files", "req"). Default is "it" (short for "iterations").

Display Format

Condition Output Example
task.speed is None ? it/s (or ? {unit}/s)
Speed < 1000 42.5 it/s, 7.3 rows/s
Speed ≥ 1000 1.2k it/s, 15.3k rows/s
  • Format: "{value} {unit}/s" for normal speeds
  • For speeds ≥ 1000: use "k" suffix to keep output compact (e.g., 1.2k it/s instead of 1200.0 it/s)

Example Usage

from rich.progress import Progress, BarColumn, TextColumn, SpeedColumn, TimeRemainingColumn

# Processing rows from a file
progress = Progress(
    TextColumn("[progress.description]{task.description}"),
    BarColumn(),
    TextColumn("{task.completed}/{task.total}"),
    SpeedColumn(unit="rows"),
    TimeRemainingColumn(),
)
with progress:
    task_id = progress.add_task("Processing CSV", total=10000)
    for row in csv_reader:
        process(row)
        progress.advance(task_id)

# Web requests
progress = Progress(
    TextColumn("{task.description}"),
    BarColumn(),
    SpeedColumn(unit="req"),
    TimeRemainingColumn(),
)

Implementation Notes

  • The column should read task.finished_speed or task.speed (consistent with TransferSpeedColumn and TaskProgressColumn.render_speed)
  • Use the progress.data.speed style (or equivalent) for consistency with TransferSpeedColumn
  • Consider using filesize.pick_unit_and_suffix or similar logic for the "k" formatting, or implement a simple formatter (e.g., f"{speed/1000:.1f}k" when speed >= 1000)

Testing

Add tests in tests/test_progress.py covering:

  1. Speed is None"? it/s" (or "? {unit}/s" for custom unit)
  2. Small speed (e.g., 42.5) → "42.5 it/s"
  3. Large speed (≥ 1000, e.g., 1234.5) → "1.2k it/s" (or equivalent)
  4. Custom unit (e.g., unit="rows") → "7.3 rows/s", "? rows/s"

Checklist for Contributors

  • Add SpeedColumn class to rich/progress.py
  • Export SpeedColumn in rich/progress.py (if there is an __all__ or public API)
  • Add tests in tests/test_progress.py
  • Update docs/source/progress.rst to document the new column
  • Update CHANGELOG.md (when submitting PR)

References

  • TransferSpeedColumn (lines 694–704 in rich/progress.py) - byte-specific speed column
  • TaskProgressColumn.render_speed (lines 531–544) - iterations/s with scientific notation
  • Progress documentation
  • CONTRIBUTING.md

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions