From 421468c04c83e9c41174ffde5a8a1c4238bef0dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sun, 5 Jan 2020 21:52:46 +0100 Subject: [PATCH 1/2] :memo: Add/extend docs and examples for param types. bool, int. --- docs/src/parameter_types/bool/__init__.py | 0 docs/src/parameter_types/bool/tutorial001.py | 12 ++ docs/src/parameter_types/bool/tutorial002.py | 14 ++ docs/src/parameter_types/bool/tutorial003.py | 12 ++ docs/src/parameter_types/bool/tutorial004.py | 12 ++ .../src/parameter_types/number/tutorial003.py | 9 + docs/tutorial/parameter-types/bool.md | 190 ++++++++++++++++++ docs/tutorial/parameter-types/number.md | 48 +++++ 8 files changed, 297 insertions(+) create mode 100644 docs/src/parameter_types/bool/__init__.py create mode 100644 docs/src/parameter_types/bool/tutorial001.py create mode 100644 docs/src/parameter_types/bool/tutorial002.py create mode 100644 docs/src/parameter_types/bool/tutorial003.py create mode 100644 docs/src/parameter_types/bool/tutorial004.py create mode 100644 docs/src/parameter_types/number/tutorial003.py create mode 100644 docs/tutorial/parameter-types/bool.md diff --git a/docs/src/parameter_types/bool/__init__.py b/docs/src/parameter_types/bool/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/docs/src/parameter_types/bool/tutorial001.py b/docs/src/parameter_types/bool/tutorial001.py new file mode 100644 index 0000000..46f6335 --- /dev/null +++ b/docs/src/parameter_types/bool/tutorial001.py @@ -0,0 +1,12 @@ +import typer + + +def main(force: bool = typer.Option(False, "--force")): + if force: + typer.echo("Forcing operation") + else: + typer.echo("Not forcing") + + +if __name__ == "__main__": + typer.run(main) diff --git a/docs/src/parameter_types/bool/tutorial002.py b/docs/src/parameter_types/bool/tutorial002.py new file mode 100644 index 0000000..e887e14 --- /dev/null +++ b/docs/src/parameter_types/bool/tutorial002.py @@ -0,0 +1,14 @@ +import typer + + +def main(accept: bool = typer.Option(None, "--accept/--reject")): + if accept is None: + typer.echo("I don't know what you want yet") + elif accept: + typer.echo("Accepting!") + else: + typer.echo("Rejecting!") + + +if __name__ == "__main__": + typer.run(main) diff --git a/docs/src/parameter_types/bool/tutorial003.py b/docs/src/parameter_types/bool/tutorial003.py new file mode 100644 index 0000000..fda717e --- /dev/null +++ b/docs/src/parameter_types/bool/tutorial003.py @@ -0,0 +1,12 @@ +import typer + + +def main(force: bool = typer.Option(False, "--force/--no-force", "-f/-F")): + if force: + typer.echo("Forcing operation") + else: + typer.echo("Not forcing") + + +if __name__ == "__main__": + typer.run(main) diff --git a/docs/src/parameter_types/bool/tutorial004.py b/docs/src/parameter_types/bool/tutorial004.py new file mode 100644 index 0000000..8c7f47c --- /dev/null +++ b/docs/src/parameter_types/bool/tutorial004.py @@ -0,0 +1,12 @@ +import typer + + +def main(in_prod: bool = typer.Option(True, " /--demo", " /-d")): + if in_prod: + typer.echo("Running in production") + else: + typer.echo("Running demo") + + +if __name__ == "__main__": + typer.run(main) diff --git a/docs/src/parameter_types/number/tutorial003.py b/docs/src/parameter_types/number/tutorial003.py new file mode 100644 index 0000000..95737d0 --- /dev/null +++ b/docs/src/parameter_types/number/tutorial003.py @@ -0,0 +1,9 @@ +import typer + + +def main(verbose: int = typer.Option(0, "--verbose", "-v", count=True)): + typer.echo(f"Verbose level is {verbose}") + + +if __name__ == "__main__": + typer.run(main) diff --git a/docs/tutorial/parameter-types/bool.md b/docs/tutorial/parameter-types/bool.md new file mode 100644 index 0000000..cb7c56d --- /dev/null +++ b/docs/tutorial/parameter-types/bool.md @@ -0,0 +1,190 @@ +We have seen some examples of *CLI options* with `bool`, and how **Typer** creates `--something` and `--no-something` automatically. + +But we can customize those names. + +## Only `--force` + +Let's say that we want a `--force` *CLI option* only, we want to discard `--no-force`. + +We can do that by specifying the exact name we want: + +```Python hl_lines="4" +{!./src/parameter_types/bool/tutorial001.py!} +``` + +Now there's only a `--force` *CLI option*: + +
+ +```console +// Check the help +$ python main.py --help + +// Notice there's only --force, we no longer have --no-force +Usage: main.py [OPTIONS] + +Options: + --force + --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: +$ python main.py + +Not forcing + +// Now add --force +$ python main.py --force + +Forcing operation + +// And --no-force no longer exists ⛔️ +$ python main.py --no-force + +Usage: main.py [OPTIONS] +Try "main.py --help" for help. + +Error: no such option: --no-force +``` + +
+ +## Alternative names + +Now let's imagine we have a *CLI option* `--accept`. + +And we want to allow setting `--accept` or the contrary, but `--no-accept` looks ugly. + +We might want to instead have `--accept` and `--reject`. + +We can do that by passing a single `str` with the 2 names for the `bool` *CLI option* separated by `/`: + +```Python hl_lines="4" +{!./src/parameter_types/bool/tutorial002.py!} +``` + +Check it: + +
+ +```console +// Check the help +$ python main.py --help + +// Notice the --accept / --reject +Usage: main.py [OPTIONS] + +Options: + --accept / --reject + --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 +$ python main.py + +I don't know what you want yet + +// Now pass --accept +$ python main.py --accept + +Accepting! + +// And --reject +$ python main.py --reject + +Rejecting! +``` + +
+ +## Short names + +The same way, you can declare short versions of the names for these *CLI options*. + +For example, let's say we want `-f` for `--force` and `-F` for `--no-force`: + +```Python hl_lines="4" +{!./src/parameter_types/bool/tutorial003.py!} +``` + +Check it: + +
+ +```console +// Check the help +$ python main.py --help + +// Notice the -f, --force / -F, --no-force +Usage: main.py [OPTIONS] + +Options: + -f, --force / -F, --no-force + --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 with the short name -f +$ python main.py -f + +Forcing operation + +// Try with the short name -F +$ python main.py -F + +Not forcing +``` + +
+ +## Only names for `False` + +If you want to (although it might not be a good idea), you can declare only *CLI option* names to set the `False` value. + +To do that, use a space and a single `/` and pass the negative name after: + +```Python hl_lines="4" +{!./src/parameter_types/bool/tutorial004.py!} +``` + +!!! tip + Have in mind that it's a string with a preceding space and then a `/`. + + So, it's `" /-S"` not `"/-S"`. + +Check it: + +
+ +```console +// Check the help +$ python main.py --help + +// Notice the / -d, --demo +Usage: main.py [OPTIONS] + +Options: + / -d, --demo + --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 +$ python main.py + +Running in production + +// Now pass --demo +$ python main.py --demo + +Running demo + +// And the short version +$ python main.py -d + +Running demo +``` + +
diff --git a/docs/tutorial/parameter-types/number.md b/docs/tutorial/parameter-types/number.md index fc70ca7..adaba25 100644 --- a/docs/tutorial/parameter-types/number.md +++ b/docs/tutorial/parameter-types/number.md @@ -98,3 +98,51 @@ ID is 5 ``` + +## Counter *CLI options* + +You can make a *CLI option* work as a counter with the `counter` parameter: + +```Python hl_lines="4" +{!./src/parameter_types/number/tutorial003.py!} +``` + +It means that the *CLI option* will be like a boolean flag, e.g. `--verbose`. + +And the value you receive in the function will be the amount of times that `--verbose` was added: + +
+ +```console +// Check it +$ python main.py + +Verbose level is 0 + +// Now use one --verbose +$ python main.py --verbose + +Verbose level is 1 + +// Now 3 --verbose +$ python main.py --verbose --verbose --verbose + +Verbose level is 3 + +// And with the short name +$ python main.py -v + +Verbose level is 1 + +// And with the short name 3 times +$ python main.py -v -v -v + +Verbose level is 3 + +// As short names can be put together, this also works +$ python main.py -vvv + +Verbose level is 3 +``` + +
\ No newline at end of file From be75c9c3a36ae6c3912a98e45b1ca9bc69689483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sun, 5 Jan 2020 21:53:57 +0100 Subject: [PATCH 2/2] :white_check_mark: Add tests for extra param types, bool, number --- .../test_bool/__init__.py | 0 .../test_bool/test_tutorial001.py | 46 +++++++++++++++ .../test_bool/test_tutorial002.py | 52 +++++++++++++++++ .../test_bool/test_tutorial003.py | 39 +++++++++++++ .../test_bool/test_tutorial004.py | 45 +++++++++++++++ .../test_number/test_tutorial003.py | 57 +++++++++++++++++++ 6 files changed, 239 insertions(+) create mode 100644 tests/test_tutorial/test_parameter_types/test_bool/__init__.py create mode 100644 tests/test_tutorial/test_parameter_types/test_bool/test_tutorial001.py create mode 100644 tests/test_tutorial/test_parameter_types/test_bool/test_tutorial002.py create mode 100644 tests/test_tutorial/test_parameter_types/test_bool/test_tutorial003.py create mode 100644 tests/test_tutorial/test_parameter_types/test_bool/test_tutorial004.py create mode 100644 tests/test_tutorial/test_parameter_types/test_number/test_tutorial003.py diff --git a/tests/test_tutorial/test_parameter_types/test_bool/__init__.py b/tests/test_tutorial/test_parameter_types/test_bool/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_tutorial/test_parameter_types/test_bool/test_tutorial001.py b/tests/test_tutorial/test_parameter_types/test_bool/test_tutorial001.py new file mode 100644 index 0000000..975adf2 --- /dev/null +++ b/tests/test_tutorial/test_parameter_types/test_bool/test_tutorial001.py @@ -0,0 +1,46 @@ +import subprocess + +import typer +from typer.testing import CliRunner + +from parameter_types.bool import tutorial001 as mod + +runner = CliRunner() + +app = typer.Typer() +app.command()(mod.main) + + +def test_help(): + result = runner.invoke(app, ["--help"]) + assert result.exit_code == 0 + assert "--force" in result.output + assert "--no-force" not in result.output + + +def test_no_force(): + result = runner.invoke(app) + assert result.exit_code == 0 + assert "Not forcing" in result.output + + +def test_force(): + result = runner.invoke(app, ["--force"]) + assert result.exit_code == 0 + assert "Forcing operation" in result.output + + +def test_invalid_no_force(): + result = runner.invoke(app, ["--no-force"]) + assert result.exit_code != 0 + assert "Error: no such option: --no-force" in result.output + + +def test_script(): + result = subprocess.run( + ["coverage", "run", mod.__file__, "--help"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding="utf-8", + ) + assert "Usage" in result.stdout diff --git a/tests/test_tutorial/test_parameter_types/test_bool/test_tutorial002.py b/tests/test_tutorial/test_parameter_types/test_bool/test_tutorial002.py new file mode 100644 index 0000000..e5c32e9 --- /dev/null +++ b/tests/test_tutorial/test_parameter_types/test_bool/test_tutorial002.py @@ -0,0 +1,52 @@ +import subprocess + +import typer +from typer.testing import CliRunner + +from parameter_types.bool import tutorial002 as mod + +runner = CliRunner() + +app = typer.Typer() +app.command()(mod.main) + + +def test_help(): + result = runner.invoke(app, ["--help"]) + assert result.exit_code == 0 + assert "--accept / --reject" in result.output + assert "--no-accept" not in result.output + + +def test_main(): + result = runner.invoke(app) + assert result.exit_code == 0 + assert "I don't know what you want yet" in result.output + + +def test_accept(): + result = runner.invoke(app, ["--accept"]) + assert result.exit_code == 0 + assert "Accepting!" in result.output + + +def test_reject(): + result = runner.invoke(app, ["--reject"]) + assert result.exit_code == 0 + assert "Rejecting!" in result.output + + +def test_invalid_no_accept(): + result = runner.invoke(app, ["--no-accept"]) + assert result.exit_code != 0 + assert "Error: no such option: --no-accept" in result.output + + +def test_script(): + result = subprocess.run( + ["coverage", "run", mod.__file__, "--help"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding="utf-8", + ) + assert "Usage" in result.stdout diff --git a/tests/test_tutorial/test_parameter_types/test_bool/test_tutorial003.py b/tests/test_tutorial/test_parameter_types/test_bool/test_tutorial003.py new file mode 100644 index 0000000..b24d1d7 --- /dev/null +++ b/tests/test_tutorial/test_parameter_types/test_bool/test_tutorial003.py @@ -0,0 +1,39 @@ +import subprocess + +import typer +from typer.testing import CliRunner + +from parameter_types.bool import tutorial003 as mod + +runner = CliRunner() + +app = typer.Typer() +app.command()(mod.main) + + +def test_help(): + result = runner.invoke(app, ["--help"]) + assert result.exit_code == 0 + assert "-f, --force / -F, --no-force" in result.output + + +def test_force(): + result = runner.invoke(app, ["-f"]) + assert result.exit_code == 0 + assert "Forcing operation" in result.output + + +def test_no_force(): + result = runner.invoke(app, ["-F"]) + assert result.exit_code == 0 + assert "Not forcing" in result.output + + +def test_script(): + result = subprocess.run( + ["coverage", "run", mod.__file__, "--help"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding="utf-8", + ) + assert "Usage" in result.stdout diff --git a/tests/test_tutorial/test_parameter_types/test_bool/test_tutorial004.py b/tests/test_tutorial/test_parameter_types/test_bool/test_tutorial004.py new file mode 100644 index 0000000..3306074 --- /dev/null +++ b/tests/test_tutorial/test_parameter_types/test_bool/test_tutorial004.py @@ -0,0 +1,45 @@ +import subprocess + +import typer +from typer.testing import CliRunner + +from parameter_types.bool import tutorial004 as mod + +runner = CliRunner() + +app = typer.Typer() +app.command()(mod.main) + + +def test_help(): + result = runner.invoke(app, ["--help"]) + assert result.exit_code == 0 + assert "/ -d, --demo" in result.output + + +def test_main(): + result = runner.invoke(app) + assert result.exit_code == 0 + assert "Running in production" in result.output + + +def test_demo(): + result = runner.invoke(app, ["--demo"]) + assert result.exit_code == 0 + assert "Running demo" in result.output + + +def test_short_demo(): + result = runner.invoke(app, ["-d"]) + assert result.exit_code == 0 + assert "Running demo" in result.output + + +def test_script(): + result = subprocess.run( + ["coverage", "run", mod.__file__, "--help"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding="utf-8", + ) + assert "Usage" in result.stdout diff --git a/tests/test_tutorial/test_parameter_types/test_number/test_tutorial003.py b/tests/test_tutorial/test_parameter_types/test_number/test_tutorial003.py new file mode 100644 index 0000000..25946f7 --- /dev/null +++ b/tests/test_tutorial/test_parameter_types/test_number/test_tutorial003.py @@ -0,0 +1,57 @@ +import subprocess + +import typer +from typer.testing import CliRunner + +from parameter_types.number import tutorial003 as mod + +runner = CliRunner() + +app = typer.Typer() +app.command()(mod.main) + + +def test_main(): + result = runner.invoke(app) + assert result.exit_code == 0 + assert "Verbose level is 0" in result.output + + +def test_verbose_1(): + result = runner.invoke(app, ["--verbose"]) + assert result.exit_code == 0 + assert "Verbose level is 1" in result.output + + +def test_verbose_3(): + result = runner.invoke(app, ["--verbose", "--verbose", "--verbose"]) + assert result.exit_code == 0 + assert "Verbose level is 3" in result.output + + +def test_verbose_short_1(): + result = runner.invoke(app, ["-v"]) + assert result.exit_code == 0 + assert "Verbose level is 1" in result.output + + +def test_verbose_short_3(): + result = runner.invoke(app, ["-v", "-v", "-v"]) + assert result.exit_code == 0 + assert "Verbose level is 3" in result.output + + +def test_verbose_short_3_condensed(): + result = runner.invoke(app, ["-vvv"]) + assert result.exit_code == 0 + assert "Verbose level is 3" in result.output + + +def test_script(): + result = subprocess.run( + ["coverage", "run", mod.__file__, "--help"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding="utf-8", + ) + assert "Usage" in result.stdout