Skip to content

count=True options are shown as INTEGER in help #1869

Description

@MicroMilo

Description

When an option is declared with count=True, Typer parses it as a no-value counting flag, but the generated help still displays it as an INTEGER option.

This makes the help text suggest that the user should pass an integer value such as --verbose 3, while the parser actually rejects that form.

Minimal reproducible example

import typer
from typer.testing import CliRunner

app = typer.Typer()

@app.command()
def main(verbose: int = typer.Option(0, count=True)):
    print(f"verbose={verbose!r}:{type(verbose).__name__}")

runner = CliRunner()

for args in [
    ["--help"],
    ["--verbose"],
    ["--verbose", "--verbose"],
    ["--verbose", "3"],
]:
    result = runner.invoke(app, args)
    print("ARGS:", args)
    print("EXIT:", result.exit_code)
    print(result.output)

Actual behavior

--verbose is parsed as a counting flag:

ARGS: ['--verbose']
EXIT: 0
verbose=1:int

ARGS: ['--verbose', '--verbose']
EXIT: 0
verbose=2:int

But help shows it as an integer-valued option:

--verbose INTEGER  [default: 0]

And the form implied by the help fails:

ARGS: ['--verbose', '3']
EXIT: 2
Got unexpected extra argument (3)

Expected behavior

For count=True, the help output should not display INTEGER, because the option is consumed as a no-value counting flag.

For comparison, Click's native count=True help displays the option as a flag:

import click

@click.command()
@click.option("--verbose", "-v", count=True)
def cli(verbose):
    click.echo(verbose)
Options:
  -v, --verbose
  --help         Show this message and exit.

Why this matters

This is a contract mismatch between:

  • the option declaration: typer.Option(..., count=True);
  • the parser behavior: count=True is consumed as a no-value counting flag;
  • the help/feedback layer: it is displayed as an INTEGER option.

Users can be misled by the help into passing --verbose 3, which is rejected at runtime.

Version / source checked

I reproduced this against the current master branch locally.

Relevant code paths appear to be:

  • typer/main.py: the parameter is passed through as count=parameter_info.count when constructing TyperOption.
  • typer/core.py: TyperOption.add_to_parser() uses action="count" when self.count is true.

Possible fix direction

Treat count=True as a flag-like option for help/metavar rendering, so the generated help does not show INTEGER for a no-value counting flag.

A regression test could assert that:

  • typer.Option(0, count=True) help does not show INTEGER;
  • --verbose --verbose returns 2;
  • --verbose 3 remains rejected, since count options do not consume an explicit value;
  • explicitly declared short flags such as typer.Option(0, "--verbose", "-v", count=True) still support -vvv.

Submitted with PilotDeck (OpenBMB/PilotDeck).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions