Skip to content

Commit

Permalink
✨ Show help items in order of definition (#944)
Browse files Browse the repository at this point in the history
  • Loading branch information
svlandeg authored Aug 26, 2024
1 parent 0f9b8fd commit 1f684d4
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 32 deletions.
33 changes: 33 additions & 0 deletions docs/tutorial/commands/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,39 @@ Commands:

</div>


## Sorting of the commands

Note that by design, **Typer** shows the commands in the order they've been declared.

So, if we take our original example, with `create` and `delete` commands, and reverse the order in the Python file:

```Python hl_lines="7 12"
{!../docs_src/commands/index/tutorial004.py!}
```

Then we will see the `delete` command first in the help output:

<div class="termy">

```console
// Check the help
$ python main.py --help

Usage: main.py [OPTIONS] COMMAND [ARGS]...

Options:
--install-completion Install completion for the current shell.
--show-completion Show completion for the current shell, to copy it or customize the installation.
--help Show this message and exit.

Commands:
delete
create
```

</div>

## Click Group

If you come from Click, a `typer.Typer` app with subcommands is more or less the equivalent of a <a href="https://click.palletsprojects.com/en/7.x/quickstart/#nesting-commands" class="external-link" target="_blank">Click Group</a>.
Expand Down
17 changes: 17 additions & 0 deletions docs_src/commands/index/tutorial004.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import typer

app = typer.Typer()


@app.command()
def delete():
print("Deleting user: Hiro Hamada")


@app.command()
def create():
print("Creating user: Hiro Hamada")


if __name__ == "__main__":
app()
32 changes: 16 additions & 16 deletions tests/assets/cli/multiapp-docs-title.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,41 +18,41 @@ The end

**Commands**:

* `sub`
* `top`: Top command
* `sub`

## `multiapp sub`
## `multiapp top`

Top command

**Usage**:

```console
$ multiapp sub [OPTIONS] COMMAND [ARGS]...
$ multiapp top [OPTIONS]
```

**Options**:

* `--help`: Show this message and exit.

**Commands**:

* `bye`: Say bye
* `hello`: Say Hello
* `hi`: Say Hi

### `multiapp sub bye`

Say bye
## `multiapp sub`

**Usage**:

```console
$ multiapp sub bye [OPTIONS]
$ multiapp sub [OPTIONS] COMMAND [ARGS]...
```

**Options**:

* `--help`: Show this message and exit.

**Commands**:

* `hello`: Say Hello
* `hi`: Say Hi
* `bye`: Say bye

### `multiapp sub hello`

Say Hello
Expand Down Expand Up @@ -87,14 +87,14 @@ $ multiapp sub hi [OPTIONS] [USER]

* `--help`: Show this message and exit.

## `multiapp top`
### `multiapp sub bye`

Top command
Say bye

**Usage**:

```console
$ multiapp top [OPTIONS]
$ multiapp sub bye [OPTIONS]
```

**Options**:
Expand Down
32 changes: 16 additions & 16 deletions tests/assets/cli/multiapp-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,41 +18,41 @@ The end

**Commands**:

* `sub`
* `top`: Top command
* `sub`

## `multiapp sub`
## `multiapp top`

Top command

**Usage**:

```console
$ multiapp sub [OPTIONS] COMMAND [ARGS]...
$ multiapp top [OPTIONS]
```

**Options**:

* `--help`: Show this message and exit.

**Commands**:

* `bye`: Say bye
* `hello`: Say Hello
* `hi`: Say Hi

### `multiapp sub bye`

Say bye
## `multiapp sub`

**Usage**:

```console
$ multiapp sub bye [OPTIONS]
$ multiapp sub [OPTIONS] COMMAND [ARGS]...
```

**Options**:

* `--help`: Show this message and exit.

**Commands**:

* `hello`: Say Hello
* `hi`: Say Hi
* `bye`: Say bye

### `multiapp sub hello`

Say Hello
Expand Down Expand Up @@ -87,14 +87,14 @@ $ multiapp sub hi [OPTIONS] [USER]

* `--help`: Show this message and exit.

## `multiapp top`
### `multiapp sub bye`

Top command
Say bye

**Usage**:

```console
$ multiapp top [OPTIONS]
$ multiapp sub bye [OPTIONS]
```

**Options**:
Expand Down
45 changes: 45 additions & 0 deletions tests/test_tutorial/test_commands/test_index/test_tutorial004.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import subprocess
import sys

from typer.testing import CliRunner

from docs_src.commands.index import tutorial004 as mod

app = mod.app

runner = CliRunner()


def test_help():
result = runner.invoke(app, ["--help"])
assert result.exit_code == 0
assert "[OPTIONS] COMMAND [ARGS]..." in result.output
print(result.output)
assert "Commands" in result.output
assert "create" in result.output
assert "delete" in result.output
# Test that the 'delete' command precedes the 'create' command in the help output
create_char = result.output.index("create")
delete_char = result.output.index("delete")
assert delete_char < create_char


def test_create():
result = runner.invoke(app, ["create"])
assert result.exit_code == 0
assert "Creating user: Hiro Hamada" in result.output


def test_delete():
result = runner.invoke(app, ["delete"])
assert result.exit_code == 0
assert "Deleting user: Hiro Hamada" in result.output


def test_script():
result = subprocess.run(
[sys.executable, "-m", "coverage", "run", mod.__file__, "--help"],
capture_output=True,
encoding="utf-8",
)
assert "Usage" in result.stdout
6 changes: 6 additions & 0 deletions typer/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -744,3 +744,9 @@ def format_help(self, ctx: click.Context, formatter: click.HelpFormatter) -> Non
ctx=ctx,
markup_mode=self.rich_markup_mode,
)

def list_commands(self, ctx: click.Context) -> List[str]:
"""Returns a list of subcommand names.
Note that in Click's Group class, these are sorted.
In Typer, we wish to maintain the original order of creation (cf Issue #933)"""
return [n for n, c in self.commands.items()]

0 comments on commit 1f684d4

Please sign in to comment.