Skip to content

Commit

Permalink
✨ Add support for help for CLI arguments (#123)
Browse files Browse the repository at this point in the history
* ✨ Implement custom TyperCommand and TyperArgument

to support help in CLI Arguments

* ✨ Update data models to store help for CLI Arguments

* ✨ Add help to typer.Argument()

* ✨ Use new TyperCommand and TyperArgument classes by default

to support CLI Arguments with help

* ✅ Update failing test

* ✨ Make showing default values default to true and support arguments hidden from help text

* ✨ Add docs for CLI arguments with defaults, help, and new features

* 📝 Update CLI options with new show_default=True by default

* 📝 Add example of CLI arguments with tuples

* 📝 Update docs with new generated help text for CLI arguments

* 📝 Use Optional for None example in micro-intro to types

* ✅ Add tests for new CLI argument features from docs examples

* ✅ Update tests with new generated help text for CLI arguments

* ✅ Add tests for CLI arguments with tuple defaults

* 🔧 Update MkDocs with new sections

* ✨ make show_envvar default to True

* ✨ Add docs for envvar in CLI arguments

* ✅ Add tests for CLI arguments with env vars from docs

* 🎨 Add format to CLI usage examples in docs

* 🔧 Add section about CLI arguments with env vars to MkDocs

* 📝 Fix link in docs with new structure

* 🎨 Fix format in first-steps
  • Loading branch information
tiangolo authored Jun 24, 2020
1 parent 338d05f commit 15380dd
Show file tree
Hide file tree
Showing 57 changed files with 1,466 additions and 117 deletions.
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ $ python main.py --help

Usage: main.py [OPTIONS] NAME

Arguments:
NAME [required]

Options:
--install-completion Install completion for the current shell.
--show-completion Show completion for the current shell, to copy it or customize the installation.
Expand Down Expand Up @@ -187,6 +190,9 @@ $ python main.py hello --help

Usage: main.py hello [OPTIONS] NAME

Arguments:
NAME [required]

Options:
--help Show this message and exit.

Expand All @@ -196,8 +202,11 @@ $ python main.py goodbye --help

Usage: main.py goodbye [OPTIONS] NAME

Arguments:
NAME [required]

Options:
--formal / --no-formal
--formal / --no-formal [default: False]
--help Show this message and exit.

// Automatic --formal and --no-formal for the bool option 🎉
Expand Down
11 changes: 10 additions & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ $ python main.py --help

Usage: main.py [OPTIONS] NAME

Arguments:
NAME [required]

Options:
--install-completion Install completion for the current shell.
--show-completion Show completion for the current shell, to copy it or customize the installation.
Expand Down Expand Up @@ -187,6 +190,9 @@ $ python main.py hello --help

Usage: main.py hello [OPTIONS] NAME

Arguments:
NAME [required]

Options:
--help Show this message and exit.

Expand All @@ -196,8 +202,11 @@ $ python main.py goodbye --help

Usage: main.py goodbye [OPTIONS] NAME

Arguments:
NAME [required]

Options:
--formal / --no-formal
--formal / --no-formal [default: False]
--help Show this message and exit.

// Automatic --formal and --no-formal for the bool option 🎉
Expand Down
File renamed without changes.
15 changes: 15 additions & 0 deletions docs/src/arguments/default/tutorial002.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import random

import typer


def get_name():
return random.choice(["Deadpool", "Rick", "Morty", "Hiro"])


def main(name: str = typer.Argument(get_name)):
typer.echo(f"Hello {name}")


if __name__ == "__main__":
typer.run(main)
9 changes: 9 additions & 0 deletions docs/src/arguments/envvar/tutorial001.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import typer


def main(name: str = typer.Argument("World", envvar="AWESOME_NAME")):
typer.echo(f"Hello Mr. {name}")


if __name__ == "__main__":
typer.run(main)
9 changes: 9 additions & 0 deletions docs/src/arguments/envvar/tutorial002.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import typer


def main(name: str = typer.Argument("World", envvar=["AWESOME_NAME", "GOD_NAME"])):
typer.echo(f"Hello Mr. {name}")


if __name__ == "__main__":
typer.run(main)
9 changes: 9 additions & 0 deletions docs/src/arguments/envvar/tutorial003.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import typer


def main(name: str = typer.Argument("World", envvar="AWESOME_NAME", show_envvar=False)):
typer.echo(f"Hello Mr. {name}")


if __name__ == "__main__":
typer.run(main)
9 changes: 9 additions & 0 deletions docs/src/arguments/help/tutorial001.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import typer


def main(name: str = typer.Argument(..., help="The name of the user to greet")):
typer.echo(f"Hello {name}")


if __name__ == "__main__":
typer.run(main)
12 changes: 12 additions & 0 deletions docs/src/arguments/help/tutorial002.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import typer


def main(name: str = typer.Argument(..., help="The name of the user to greet")):
"""
Say hi to NAME very gently, like Dirk.
"""
typer.echo(f"Hello {name}")


if __name__ == "__main__":
typer.run(main)
12 changes: 12 additions & 0 deletions docs/src/arguments/help/tutorial003.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import typer


def main(name: str = typer.Argument("World", help="Who to greet")):
"""
Say hi to NAME very gently, like Dirk.
"""
typer.echo(f"Hello {name}")


if __name__ == "__main__":
typer.run(main)
12 changes: 12 additions & 0 deletions docs/src/arguments/help/tutorial004.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import typer


def main(name: str = typer.Argument("World", help="Who to greet", show_default=False)):
"""
Say hi to NAME very gently, like Dirk.
"""
typer.echo(f"Hello {name}")


if __name__ == "__main__":
typer.run(main)
13 changes: 13 additions & 0 deletions docs/src/arguments/help/tutorial005.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import typer


def main(
name: str = typer.Argument(
"Wade Wilson", help="Who to greet", show_default="Deadpoolio the amazing's name"
)
):
typer.echo(f"Hello {name}")


if __name__ == "__main__":
typer.run(main)
9 changes: 9 additions & 0 deletions docs/src/arguments/help/tutorial006.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import typer


def main(name: str = typer.Argument("World", metavar="✨username✨")):
typer.echo(f"Hello {name}")


if __name__ == "__main__":
typer.run(main)
12 changes: 12 additions & 0 deletions docs/src/arguments/help/tutorial007.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import typer


def main(name: str = typer.Argument("World", hidden=True)):
"""
Say hi to NAME very gently, like Dirk.
"""
typer.echo(f"Hello {name}")


if __name__ == "__main__":
typer.run(main)
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from typing import Optional

import typer


def main(name: str = typer.Argument(None)):
def main(name: Optional[str] = typer.Argument(None)):
if name is None:
typer.echo("Hello World!")
else:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from typing import Tuple

import typer


def main(
names: Tuple[str, str, str] = typer.Argument(
("Harry", "Hermione", "Ron"), help="Select 3 characters to play with"
)
):
for name in names:
typer.echo(f"Hello {name}")


if __name__ == "__main__":
typer.run(main)
2 changes: 1 addition & 1 deletion docs/src/options/help/tutorial002.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import typer


def main(fullname: str = typer.Option("Wade Wilson", show_default=True)):
def main(fullname: str = typer.Option("Wade Wilson", show_default=False)):
typer.echo(f"Hello {fullname}")


Expand Down
99 changes: 99 additions & 0 deletions docs/tutorial/arguments/default.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
We can also use the same `typer.Argument()` to set a default value.

That way the *CLI argument* will be optional *and also* have a default value.

## An optional *CLI argument* with a default

We can also use `typer.Argument()` to make a *CLI argument* have a default value other than `None`:

```Python hl_lines="4"
{!./src/arguments/default/tutorial001.py!}
```

!!! tip
Because now the value will be a `str` passed by the user or the default value of `"Wade Wilson"` which is also a `str`, we know the value will never be `None`, so we don't have to (and shouldn't) use `Optional[str]`.

Have in mind that the `Optional[something]` tells Python that a value "could be `None`". But the use of `Optional` doesn't affect Typer in any way, e.g. it doesn't tell Typer if a value is required or not.

Check it:

<div class="termy">

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

// Notice the [default: Wade Wilson] ✨
Usage: main.py [OPTIONS] [NAME]

Arguments:
[NAME] [default: Wade Wilson]

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.

// With no optional CLI argument
$ python main.py

Hello Wade Wilson

// With one CLI argument
$ python main.py Camila

Hello Camila
```

</div>

## Dynamic default value

And we can even make the default value be dynamically generated by passing a function as the first function argument:

```Python hl_lines="6 7 10"
{!./src/arguments/default/tutorial002.py!}
```

In this case, we created the function `get_name` that will just return a random `str` each time.

And we pass it as the first function argument to `typer.Argument()`.

Check it:

<div class="termy">

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

Usage: main.py [OPTIONS] [NAME]

Arguments:
[NAME] [default: (dynamic)]

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.

// Try it several times, it will use a random default each time
$ python main.py

Hello Deadpool

$ python main.py

Hello Hiro

$ python main.py

Hello Rick

// Now pass a value for the CLI argument
$ python main.py Camila

Hello Camila
```

</div>
Loading

1 comment on commit 15380dd

@github-actions
Copy link

Choose a reason for hiding this comment

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

Please sign in to comment.