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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions