Render a set of articles into a fixed-width, newspaper-style grid in the terminal. Each article is a row of side-by-side text columns; the text inside a column is word-wrapped to that column's width, and articles are framed by horizontal rules.
The input is an array of arrays of objects (articles):
-
The outer array is the list of articles, rendered top to bottom.
-
Each article is an array of columns, rendered left to right.
-
Each column is an object describing one block of text:
Field Type Meaning textstrThe content to display. widthintThe column's inner width, in characters.
A single global constant gwidth defines the inner width of the horizontal
rules that separate the articles.
gwidth = 32
articles = [
[
{"text": "This is a short article.", "width": 15},
{"text": "Now for a longer article. This one has a lot of text.", "width": 16},
],
[
{"text": "Another article with medium length.", "width": 32},
],
]For every article:
- Word wrap each column's
textto itswidth. Wrapping happens on spaces only — a word is never split. Each wrapped piece becomes one line of that column. - Align columns side by side. An article is as tall as its tallest column; shorter columns are padded with blank lines so every column has the same number of lines.
- Print row by row. For each line index, print the corresponding piece of
every column, left-justified and padded to its
width, separated and bordered by|. - Frame the articles. A horizontal rule
+{'-' * gwidth}+is printed before the first article and after every article.
A column is wrapped by repeatedly taking the longest prefix that fits within
width characters, breaking at the last space at or before position
width + 1. The remainder continues on the next line until everything fits.
Note: a single word longer than
widthis not handled — the layout assumes each word fits within its column width.
Running the sample input above produces:
+--------------------------------+
|This is a short|Now for a longer|
|article. |article. This |
| |one has a lot of|
| |text. |
+--------------------------------+
|Another article with medium |
|length. |
+--------------------------------+
Note that the column widths (15 + 16) plus their separating/bordering pipes
are expected to line up with gwidth (32) so the rules match the columns.
python3 main.pyThis project uses uv for dependency management and ruff for linting and formatting.
uv sync # install dev dependencies (pytest, ruff)
uv run pytest -v # run the test suite
uv run ruff check # lint
uv run ruff format # auto-format (use --check in CI)Linting and tests run automatically on every push and pull request via GitHub
Actions (see .github/workflows/ci.yml): a lint job runs ruff, and a test
job runs pytest across Python 3.9–3.13.
Licensed under the GNU General Public License v3.0.