[BUG] `Columns(width=...)` raises `ZeroDivisionError` when the requested width does not fit

#4214 · open · 3 comments

View on GitHub ↗

anonymous756fsd

- [x] I've checked [docs](https://rich.readthedocs.io/en/latest/introduction.html) and [closed issues](https://github.com/Textualize/rich/issues?q=is%3Aissue+is%3Aclosed) for possible solutions. - [x] I can't find my issue in the [FAQ](https://github.com/Textualize/rich/blob/master/FAQ.md). <!-- WAIT! Is this a feature request? Rich isn't accepting new features. If it can be implemented in a third-party library, then that is what I recommend. Rich *may* accept enhancements to existing features, if they don't have a disruptive impact on the API. If you think your feature is exceptional, then please make your case in a discussion. --> **Describe the bug** With the default padding `(0, 1)`, `width_padding` is `1`. If the available console width is no greater than `width + 1`, this calculation returns `0`. This will result in an error. This bug might be related to the following code in `rich\columns.py` within rich. ``` def __rich_console__: ... if self.width is not None: column_count = (max_width) // (self.width + width_padding) ``` `__rich_console__` then passes `column_count` to `iter_renderables`, which evaluates `item_count % column_count` and raises `ZeroDivisionError`. **Reproduction** ```python from io import StringIO from rich.columns import Columns from rich.console import Console console = Console( file=StringIO(), width=16, height=24, force_terminal=True, color_system=None, highlight=False, ) print("size", console.width, "x", console.height) console.print(Columns([f"file_{i:02d}.txt" for i in range(8)], width=16)) ``` Observed: ``` size 16 x 24 Traceback (most recent call last): File "/home/test01.py", line 15, in <module> console.print(Columns([f"file_{i:02d}.txt" for i in range(8)], width=16)) File "/home/rich/rich/console.py", line 1731, in print extend(render(renderable, render_options)) File "/home/rich/rich/console.py", line 1339, in render for render_output in iter_render: File "/home/rich/rich/columns.py", line 145, in __rich_console__ _renderables = [ ^ File "/home/rich/rich/columns.py", line 115, in iter_renderables if item_count % column_count: ~~~~~~~~~~~^~~~~~~~~~~~~~ ZeroDivisionError: integer modulo by zero ``` Same function, zero divisor (optional second case): ```python console.print(Columns(["alpha", "beta"], width=0, padding=0)) ``` ``` File "/home/rich/rich/columns.py", line 124, in __rich_console__ column_count = (max_width) // (self.width + width_padding) ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ZeroDivisionError: integer division or modulo by zero ``` **Platform** - Python version: 3.12.3 - Rich version: 15.0.0 (`9d8f9a37`) - Linux x86_64

Comments

eminsk

I took a look at this and reproduced the crash with a test case. When `max_width <= self.width + width_padding` (or with `width=0, padding=0`), `column_count` in `rich/columns.py` evaluates to `0`, causing the `ZeroDivisionError` when iterating. Guarding `column_count` with `max(1, ...)` resolves the issue cleanly: ```python column_count = ( max(1, max_width // (self.width + width_padding)) if (self.width + width_padding) > 0 else 1 ) ``` I've put together a fix with regression tests on my fork: - Commit: [eminsk/rich@c9f68f3](https://github.com/eminsk/rich/commit/c9f68f339457092728cf5ac86264ad3d8571073c) - Branch: [`fix/columns-zero-division`](https://github.com/eminsk/rich/tree/fix/columns-zero-division) Since PRs are currently limited to collaborators on this repo, feel free to cherry-pick this directly or let me know if you'd like me to open a PR once permissions allow.

TrueFurina

I hit this while using `Columns` with a fixed width and traced it to `rich/columns.py`. Here's a minimal fix — happy to send a proper PR if collaborators would like one (I can't open one due to the repository's collaborator-only pull request policy). **Root cause:** in `__rich_console__`, when `self.width` is set: ```python column_count = (max_width) // (self.width + width_padding) ``` If `self.width + width_padding > max_width` (e.g. `Columns(width=10)` on a console of width 10 with the default padding `(0, 1)`), this evaluates to `0`. `iter_renderables` then evaluates `item_count % column_count`, raising `ZeroDivisionError`. **Fix:** clamp the column count to at least 1, falling back to a single column: ```python column_count = max(1, max_width // (self.width + width_padding)) ``` **Reproduction (rich 15.0.0):** ```python from io import StringIO from rich.console import Console from rich.columns import Columns console = Console(file=StringIO(), width=10) console.print(Columns(["a", "b", "c"], width=10)) # ZeroDivisionError ``` After the fix this renders the items stacked in a single column. Full fix with a regression test (`test_width_narrower_than_console_does_not_crash`, covering `width == console_width`, `width == 0` on a wider console, and `column_first` with an oversized width) is on my fork, built on current `main`: **https://github.com/TrueFurina/rich/compare/main...TrueFurina:fix/columns-zero-width-fallback** (`rich/columns.py` +4/-1, `tests/test_columns.py` +11; `pytest tests/test_columns.py` — 2 passed.) Feel free to cherry-pick or use it as a base if a collaborator picks this up.

Pitchfork-and-Torch

Opened a fix on a fork (CreatePullRequest scope blocked for this account against upstream): https://github.com/Textualize/rich/compare/main...Pitchfork-and-Torch:rich:cook/columns-zerodiv-narrow-width?expand=1 Clamps `column_count` to at least 1 when fixed `width` does not fit (and when `width + padding <= 0`). Includes a regression test for the issue repro.