Skip to content

feat(ui): add rich as an opt-in output format - #1152

Open
latiefdole wants to merge 4 commits into
Kaggle:mainfrom
latiefdole:feature/tui-upgrade
Open

feat(ui): add rich as an opt-in output format#1152
latiefdole wants to merge 4 commits into
Kaggle:mainfrom
latiefdole:feature/tui-upgrade

Conversation

@latiefdole

@latiefdole latiefdole commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Description

Adds rich as a fourth value of the existing OutputFormat enum, alongside csv, table and json. It renders list output as a bordered, colorized table.

This is opt-in only. The default table output is byte-for-byte unchanged, so no existing command, script or piped invocation changes behaviour unless the format is explicitly requested.

Usage

kaggle competitions list --format rich

Or set a persistent default for an interactive shell:

export KAGGLE_OUTPUT_FORMAT=rich

KAGGLE_OUTPUT_FORMAT only applies when neither --csv nor --format is passed; an explicit flag always wins, and unrecognized values fall back to table. Projections work as with the other formats, e.g. --format "rich(ref,reward)".

Dependency

rich is an optional extra, not a runtime dependency:

pip install kaggle[rich]

If --format rich is requested without the package installed, the CLI prints an install hint to stderr and falls back to print_table rather than failing.

Behaviour notes

  • Cell values render via rich.text.Text, so API content containing square brackets is never interpreted as console markup.
  • Columns use overflow="fold", so long values wrap instead of being truncated.
  • Column alignment and the empty-result case match print_table (an empty list prints nothing).

Docs and tests

  • Documented in docs/output_format.md next to the existing formats.
  • Tests assert on rendered output rather than on mocks.

Testing

  • 1268 passed, 0 failed
  • black --check . clean
  • mypy clean on the changed files

Note

This PR previously also contained unrelated CLI usability fixes. Those have been split out into #1171 so this one stays scoped to the output format.

@google-cla

google-cla Bot commented Jul 29, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@latiefdole

Copy link
Copy Markdown
Contributor Author

@googlebot I signed it!

@stevemessick

Copy link
Copy Markdown
Contributor

/gcbrun

@stevemessick

Copy link
Copy Markdown
Contributor

@latiefdole Looks like you need to update the formatting of three files.

Just to be clear, we have not yet decided if we want this feature, but are considering it.

@stevemessick stevemessick left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After taking a close look at this PR we have found a number of problems:

  1. Introduces behavioral regressions.
  2. Causes failure of automated checks.
  3. Fails unit tests.
  4. Adds dead code.
  5. Doesn't follow repro coding practices.
  6. Provides no escape mechanism to obtain legacy formatting (contrary to claims).

Addresses the review feedback on Kaggle#1152. The previous approach intercepted
print_table and rendered every table through rich, which changed the default
output for all list commands.

Because rich was a hard dependency, the RICH_AVAILABLE guard was always true
and the documented "falls back when piped" behavior never triggered: there was
no isatty() check, so piped output still got box borders, long values were
truncated with an ellipsis, empty results started printing a "No data found"
panel, and values containing square brackets were parsed as console markup.

Instead, add rich as a new OutputFormat alongside csv/table/json:

- `--format rich` (and `KAGGLE_OUTPUT_FORMAT=rich` for a persistent default)
  opt into the bordered output; the default table output is unchanged.
- rich moves to an optional extra; `--format rich` without it installed prints
  an install hint to stderr and falls back to print_table.
- Values render via Text() so API content is never treated as markup, and
  columns use overflow="fold" so no data is dropped on narrow terminals.
- Column alignment and the empty-items behavior match print_table.
- Drop the unused print_info/print_error/print_success helpers.

Replace the mock-only tests with tests that assert on rendered output, and
document the format in docs/output_format.md next to the existing formats.
@latiefdole
latiefdole force-pushed the feature/tui-upgrade branch from 465a6d2 to b704138 Compare August 10, 2026 05:01
@latiefdole latiefdole changed the title feat(ui): add rich terminal UI for output formatting feat(ui): add rich as an opt-in output format Aug 10, 2026
@latiefdole

Copy link
Copy Markdown
Contributor Author

Thanks for the detailed review - all six points were correct, and I've reworked the PR rather than patching around them.

I want to specifically own the worst one: the claim that output fell back to plain text when piped was false. There was no isatty() check anywhere in the code, and since rich was a hard dependency the RICH_AVAILABLE guard was always true, so piped output still got box borders. I documented behaviour I had not implemented or verified. That's on me.

Design change: instead of intercepting print_table, rich is now a fourth value of the existing OutputFormat enum (csv/table/json/rich), used via --format rich, with KAGGLE_OUTPUT_FORMAT for a persistent default. Nothing renders through rich unless it is explicitly requested. This is what resolves points 1 and 6: the default table output is byte-for-byte unchanged, so there is no legacy formatting to escape back to - plain output is the default, and the escape mechanism is simply not passing the flag.

Point by point:

  1. Behavioral regressions - resolved by the opt-in design above. I also fixed the underlying rendering bugs so the rich path itself doesn't lose data: values render via Text() so API content isn't parsed as console markup (previously a[bold]b[/bold]c printed as abc), columns use overflow="fold" instead of truncating with an ellipsis, and column alignment plus the empty-list case now match print_table (an empty list printed a "No data found." panel before; it now prints nothing, as the original did).
  2. Automated checks - black --check . is clean (69 files unchanged; 3 files would have been reformatted before). mypy on the changed files is clean; it previously reported 2 errors in ui.py.
  3. Unit tests - 1268 pass, 0 fail. Before this rework: 1213 passed, 1 failed. The failing test was one of my own mock-only tests; I replaced that suite with tests that assert on actually-rendered output.
  4. Dead code - removed the unused print_info/print_error/print_success helpers.
  5. Repo practices - rich is now an optional extra (kaggle[rich]) rather than a hard dependency, and requesting the format without it installed prints an install hint to stderr and falls back to print_table. I also deleted documentation/intro.md (which carried the false piping claim) and documented the format in docs/output_format.md alongside the existing formats, where it belongs.
  6. Escape mechanism - covered above.

The branch is rebased onto current main.

On whether you want the feature at all: understood, and no pressure either way. The point of the opt-in design is that it costs nothing in default behaviour if you're undecided - but if you'd rather not carry it, I'm happy to close this. Just say which.

Separately, this PR previously also carried some unrelated CLI usability fixes (error message hints, a --debug flag, examples in --help). Those are independent of the rich work, so I've split them out into #1171 to keep this PR scoped.

@stevemessick

Copy link
Copy Markdown
Contributor

/gcbrun

@stevemessick stevemessick left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost there! I think the lint dependencies need to be updated to allow the checks to pass:

Step #3 - "lint": src/kaggle/ui.py:20: error: Cannot find implementation or library stub for module named "rich" [import-not-found]

Otherwise, this looks really good and I look forward to getting it merged!

Comment thread docs/output_format.md
kaggle competitions list --format csv
kaggle competitions list --format table
kaggle competitions list --format json
kaggle competitions list --format rich

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great!

@stevemessick

Copy link
Copy Markdown
Contributor

/gcbrun

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants