Re-implement completion, fixes, add PowerShell support (#66)

*  Re-implement completion

*  Add tests for re-implemented completion

* 🎨 Move mypy config to file

* 🙈 Update .gitignore

*  Remove click-completion, add support for autodetection with shellingham

*  Fix test for PowerShell

* 🐛 Fix PowerShell permissions/test

* 🎨 Format test

* 🏁 Fix PowerShell script for Windows and PowerShell 5

* 📝 Update docs, internal implementation of completion
This commit is contained in:
Sebastián Ramírez 2020-03-16 13:21:58 +01:00 committed by GitHub
parent d651b7dc77
commit 1d3337a4da
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 1198 additions and 86 deletions

1
.gitignore vendored
View file

@ -9,3 +9,4 @@ dist
site
.coverage
htmlcov
.pytest_cache

View file

@ -31,7 +31,7 @@ Typer is library to build <abbr title="command line interface, programs executed
The key features are:
* **Intuitive to write**: Great editor support. <abbr title="also known as auto-complete, autocompletion, IntelliSense">Completion</abbr> everywhere. Less time debugging. Designed to be easy to use and learn. Less time reading docs.
* **Easy to use**: It's easy to use for the final users. Automatic help, and (optional) automatic completion for all shells.
* **Easy to use**: It's easy to use for the final users. Automatic help, and automatic completion for all shells.
* **Short**: Minimize code duplication. Multiple features from each parameter declaration. Fewer bugs.
* **Start simple**: The simplest example adds only 2 lines of code to your app: **1 import, 1 function call**.
* **Grow large**: Grow in complexity as much as you want, create arbitrarily complex trees of commands and groups of subcommands, with options and arguments.
@ -243,7 +243,7 @@ And similarly for **files**, **paths**, **enums** (choices), etc. And there are
**You get**: great editor support, including **completion** and **type checks** everywhere.
**Your users get**: automatic **`--help`**, (optional) **auto completion** in their terminal (Bash, Zsh, Fish, PowerShell) when they install your package or when using <a href="https://typer.tiangolo.com/typer-cli/" class="internal-link" target="_blank">Typer CLI</a>.
**Your users get**: automatic **`--help`**, **auto completion** in their terminal (Bash, Zsh, Fish, PowerShell) when they install your package or when using <a href="https://typer.tiangolo.com/typer-cli/" class="internal-link" target="_blank">Typer CLI</a>.
For a more complete example including more features, see the <a href="https://typer.tiangolo.com/tutorial/">Tutorial - User Guide</a>.
@ -256,9 +256,11 @@ But you can also install extras:
* <a href="https://pypi.org/project/colorama/" class="external-link" target="_blank"><code>colorama</code></a>: and Click will automatically use it to make sure your terminal's colors always work correctly, even in Windows.
* Then you can use any tool you want to output your terminal's colors in all the systems, including the integrated `typer.style()` and `typer.secho()` (provided by Click).
* Or any other tool, e.g. <a href="https://pypi.org/project/wasabi/" class="external-link" target="_blank"><code>wasabi</code></a>, <a href="https://github.com/erikrose/blessings" class="external-link" target="_blank"><code>blessings</code></a>.
* <a href="https://github.com/click-contrib/click-completion" class="external-link" target="_blank"><code>click-completion</code></a>: and Typer will automatically configure it to provide completion for all the shells, including installation commands.
* <a href="https://github.com/sarugaku/shellingham" class="external-link" target="_blank"><code>shellingham</code></a>: and Typer will automatically detect the current shell when installing completion.
* With `shellingham` you can just use `--install-completion`.
* Without `shellingham`, you have to pass a *CLI Option value* with the name of the shell to install completion, e.g. `--install-completion bash`.
You can install `typer` with `colorama` and `click-completion` with `pip install typer[all]`.
You can install `typer` with `colorama` and `shellingham` with `pip install typer[all]`.
## Other tools and plug-ins

View file

@ -63,6 +63,17 @@ It was built with some great ideas and design using the features available in th
As someone pointed out: <em><a href="https://twitter.com/fishnets88/status/1210126833745838080" class="external-link" target="_blank">"Nice to see it is built on Click but adds the type stuff. Me gusta!"</a></em>
### <a href="https://github.com/click-contrib/click-completion" class="external-link" target="_blank">`click-completion`</a>
`click-completion` is a plug-in for Click. It was created to extend completion support for shells when Click only had support for Bash completion.
Previous versions of **Typer** had deep integrations with `click-completion` and used it as an optional dependency. But now all the completion logic is implemented internally in **Typer** itself, the internal logic was heavily inspired and using some parts of `click-completion`.
And now **Typer** improved it to have new features, tests, some bug fixes (for issues in plain `click-completion` and Click), and better support for shells, including modern versions of PowerShell (e.g. the default versions that come with Windows 10).
!!! check "Inspired **Typer** to"
Provide auto completion for all the shells.
### <a href="https://fastapi.tiangolo.com/" class="external-link" target="_blank">FastAPI</a>
I created **FastAPI** to provide an easy way to build APIs with autocompletion for everything in the code (and some other <a href="https://fastapi.tiangolo.com/features/" class="external-link" target="_blank">features</a>).

View file

@ -42,21 +42,31 @@ But by default, it all **"just works"**.
The resulting CLI apps created with **Typer** have the nice features of many "pro" command line programs you probably already love.
* Automatic help options for the main CLI program and all the its subcommands.
* Automatic help options for the main CLI program and all its subcommands.
* Automatic command and subcommand structure handling (you will see more about subcommands in the Tutorial - User Guide).
* Automatic completion for the CLI app in all operating systems, in all the shells (Bash, Zsh, Fish, PowerShell), so that the final user of your app can just hit <kbd>TAB</kbd> and get the available options or subcommands. *
!!! note "* Auto completion"
For the autocompletion to work on all shells you also need to add the dependency `click-completion`.
Auto completion works when you create a package (installable with `pip`). Or when using [Typer CLI](typer-cli.md){.internal-link target=_blank}.
If **Typer** detects `click-completion` installed, it will automatically create 2 *CLI options*:
If you also add `shellingham` as a dependency, **Typer** will use it to auto-detect the current shell when installing completion.
**Typer** will automatically create 2 *CLI options*:
* `--install-completion`: Install completion for the current shell.
* `--show-completion`: Show completion for the current shell, to copy it or customize the installation.
* `--show-completion`: Show completion for the current shell, to copy it or customize the installation.
Then you can tell the user to run that command and the rest will just work.
If you didn't add `shellingham` those *CLI Options* take a parameter with the name of the shell to install completion for, e.g.:
* `--install-completion bash`.
* `--show-completion powershell`.
It works when you create a package (installable with `pip`). Or when using [Typer CLI](typer-cli.md){.internal-link target=_blank}.
Then you can tell the user to install completion after installing your CLI program and the rest will just work.
!!! tip
**Typer**'s completion is implemented internally, it uses ideas and components from Click and ideas from `click-completion`, but it doesn't use `click-completion` internally.
Then it extends those ideas with features and bug fixes. For example, **Typer** programs also support modern versions of PowerShell (e.g. in Windows 10) among all the other shells.
## The power of Click

View file

@ -31,7 +31,7 @@ Typer is library to build <abbr title="command line interface, programs executed
The key features are:
* **Intuitive to write**: Great editor support. <abbr title="also known as auto-complete, autocompletion, IntelliSense">Completion</abbr> everywhere. Less time debugging. Designed to be easy to use and learn. Less time reading docs.
* **Easy to use**: It's easy to use for the final users. Automatic help, and (optional) automatic completion for all shells.
* **Easy to use**: It's easy to use for the final users. Automatic help, and automatic completion for all shells.
* **Short**: Minimize code duplication. Multiple features from each parameter declaration. Fewer bugs.
* **Start simple**: The simplest example adds only 2 lines of code to your app: **1 import, 1 function call**.
* **Grow large**: Grow in complexity as much as you want, create arbitrarily complex trees of commands and groups of subcommands, with options and arguments.
@ -243,7 +243,7 @@ And similarly for **files**, **paths**, **enums** (choices), etc. And there are
**You get**: great editor support, including **completion** and **type checks** everywhere.
**Your users get**: automatic **`--help`**, (optional) **auto completion** in their terminal (Bash, Zsh, Fish, PowerShell) when they install your package or when using <a href="https://typer.tiangolo.com/typer-cli/" class="internal-link" target="_blank">Typer CLI</a>.
**Your users get**: automatic **`--help`**, **auto completion** in their terminal (Bash, Zsh, Fish, PowerShell) when they install your package or when using <a href="https://typer.tiangolo.com/typer-cli/" class="internal-link" target="_blank">Typer CLI</a>.
For a more complete example including more features, see the <a href="https://typer.tiangolo.com/tutorial/">Tutorial - User Guide</a>.
@ -256,9 +256,11 @@ But you can also install extras:
* <a href="https://pypi.org/project/colorama/" class="external-link" target="_blank"><code>colorama</code></a>: and Click will automatically use it to make sure your terminal's colors always work correctly, even in Windows.
* Then you can use any tool you want to output your terminal's colors in all the systems, including the integrated `typer.style()` and `typer.secho()` (provided by Click).
* Or any other tool, e.g. <a href="https://pypi.org/project/wasabi/" class="external-link" target="_blank"><code>wasabi</code></a>, <a href="https://github.com/erikrose/blessings" class="external-link" target="_blank"><code>blessings</code></a>.
* <a href="https://github.com/click-contrib/click-completion" class="external-link" target="_blank"><code>click-completion</code></a>: and Typer will automatically configure it to provide completion for all the shells, including installation commands.
* <a href="https://github.com/sarugaku/shellingham" class="external-link" target="_blank"><code>shellingham</code></a>: and Typer will automatically detect the current shell when installing completion.
* With `shellingham` you can just use `--install-completion`.
* Without `shellingham`, you have to pass a *CLI Option value* with the name of the shell to install completion, e.g. `--install-completion bash`.
You can install `typer` with `colorama` and `click-completion` with `pip install typer[all]`.
You can install `typer` with `colorama` and `shellingham` with `pip install typer[all]`.
## Other tools and plug-ins

View file

@ -1,2 +1,6 @@
[mypy]
ignore_missing_imports = True
disallow_untyped_defs = True
[mypy-tests.*]
disallow_untyped_defs = False

View file

@ -37,7 +37,7 @@ Documentation = "https://typer.tiangolo.com/"
[tool.flit.metadata.requires-extra]
test = [
"click-completion",
"shellingham",
"pytest >=4.4.0",
"pytest-cov",
"coverage",
@ -54,5 +54,5 @@ doc = [
]
all = [
"colorama",
"click-completion"
"shellingham"
]

View file

@ -3,6 +3,6 @@
set -e
set -x
mypy typer --disallow-untyped-defs
mypy typer
black typer tests --check
isort --multi-line=3 --trailing-comma --force-grid-wrap=0 --combine-as --line-width 88 --recursive --check-only --thirdparty typer typer tests

View file

@ -1,51 +0,0 @@
import os
import subprocess
import sys
from pathlib import Path
import typer
from typer.testing import CliRunner
from first_steps import tutorial001 as mod
runner = CliRunner()
app = typer.Typer()
app.command()(mod.main)
def test_show_completion():
result = subprocess.run(
[
"bash",
"-c",
f"{sys.executable} -m coverage run {mod.__file__} --show-completion",
],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={**os.environ, "SHELL": "/bin/bash"},
)
assert "_TUTORIAL001.PY_COMPLETE=complete-bash" in result.stdout
def test_install_completion():
bash_completion_path: Path = Path.home() / ".bash_completion"
text = ""
if bash_completion_path.is_file():
text = bash_completion_path.read_text()
result = subprocess.run(
[
"bash",
"-c",
f"{sys.executable} -m coverage run {mod.__file__} --install-completion",
],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={**os.environ, "SHELL": "/bin/bash"},
)
new_text = bash_completion_path.read_text()
bash_completion_path.write_text(text)
assert "_TUTORIAL001.PY_COMPLETE=complete-bash" in new_text
assert "completion installed in" in result.stdout
assert "Completion will take effect once you restart the terminal." in result.stdout

View file

View file

@ -0,0 +1,174 @@
import os
import subprocess
import sys
from pathlib import Path
from first_steps import tutorial001 as mod
def test_show_completion():
result = subprocess.run(
[
"bash",
"-c",
f"{sys.executable} -m coverage run {mod.__file__} --show-completion",
],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={**os.environ, "SHELL": "/bin/bash", "_TYPER_COMPLETE_TESTING": "True",},
)
assert "_TUTORIAL001.PY_COMPLETE=complete_bash" in result.stdout
def test_install_completion():
bash_completion_path: Path = Path.home() / ".bash_completion"
text = ""
if bash_completion_path.is_file():
text = bash_completion_path.read_text()
result = subprocess.run(
[
"bash",
"-c",
f"{sys.executable} -m coverage run {mod.__file__} --install-completion",
],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={**os.environ, "SHELL": "/bin/bash", "_TYPER_COMPLETE_TESTING": "True",},
)
new_text = bash_completion_path.read_text()
bash_completion_path.write_text(text)
assert 'eval "$(_TUTORIAL001.PY_COMPLETE=source_bash tutorial001.py)' in new_text
assert "completion installed in" in result.stdout
assert "Completion will take effect once you restart the terminal." in result.stdout
def test_completion_invalid_instruction():
result = subprocess.run(
["coverage", "run", mod.__file__],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TUTORIAL001.PY_COMPLETE": "sourcebash",
"_TYPER_COMPLETE_TESTING": "True",
},
)
assert result.returncode != 0
assert "Invalid completion instruction." in result.stderr
def test_completion_source_bash():
result = subprocess.run(
["coverage", "run", mod.__file__],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TUTORIAL001.PY_COMPLETE": "source_bash",
"_TYPER_COMPLETE_TESTING": "True",
},
)
assert (
"complete -o default -F _tutorial001py_completion tutorial001.py"
in result.stdout
)
def test_completion_source_invalid_shell():
result = subprocess.run(
["coverage", "run", mod.__file__],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TUTORIAL001.PY_COMPLETE": "source_xxx",
"_TYPER_COMPLETE_TESTING": "True",
},
)
assert "Shell xxx not supported." in result.stderr
def test_completion_source_invalid_instruction():
result = subprocess.run(
["coverage", "run", mod.__file__],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TUTORIAL001.PY_COMPLETE": "explode_bash",
"_TYPER_COMPLETE_TESTING": "True",
},
)
assert "Hello World" in result.stdout
def test_completion_source_zsh():
result = subprocess.run(
["coverage", "run", mod.__file__],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TUTORIAL001.PY_COMPLETE": "source_zsh",
"_TYPER_COMPLETE_TESTING": "True",
},
)
assert "compdef _tutorial001py_completion tutorial001.py" in result.stdout
def test_completion_source_fish():
result = subprocess.run(
["coverage", "run", mod.__file__],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TUTORIAL001.PY_COMPLETE": "source_fish",
"_TYPER_COMPLETE_TESTING": "True",
},
)
assert "complete --command tutorial001.py --no-files" in result.stdout
def test_completion_source_powershell():
result = subprocess.run(
["coverage", "run", mod.__file__],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TUTORIAL001.PY_COMPLETE": "source_powershell",
"_TYPER_COMPLETE_TESTING": "True",
},
)
assert (
"Register-ArgumentCompleter -Native -CommandName tutorial001.py -ScriptBlock $scriptblock"
in result.stdout
)
def test_completion_source_pwsh():
result = subprocess.run(
["coverage", "run", mod.__file__],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TUTORIAL001.PY_COMPLETE": "source_pwsh",
"_TYPER_COMPLETE_TESTING": "True",
},
)
assert (
"Register-ArgumentCompleter -Native -CommandName tutorial001.py -ScriptBlock $scriptblock"
in result.stdout
)

View file

@ -0,0 +1,178 @@
import os
import subprocess
from commands.help import tutorial001 as mod
def test_completion_complete_subcommand_bash():
result = subprocess.run(
["coverage", "run", mod.__file__, " "],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TUTORIAL001.PY_COMPLETE": "complete_bash",
"COMP_WORDS": "tutorial001.py del",
"COMP_CWORD": "1",
"_TYPER_COMPLETE_TESTING": "True",
},
)
assert "delete\ndelete-all" in result.stdout
def test_completion_complete_subcommand_bash_invalid():
result = subprocess.run(
["coverage", "run", mod.__file__, " "],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TUTORIAL001.PY_COMPLETE": "complete_bash",
"COMP_WORDS": "tutorial001.py del",
"COMP_CWORD": "42",
"_TYPER_COMPLETE_TESTING": "True",
},
)
assert "create\ndelete\ndelete-all\ninit" in result.stdout
def test_completion_complete_subcommand_zsh():
result = subprocess.run(
["coverage", "run", mod.__file__, " "],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TUTORIAL001.PY_COMPLETE": "complete_zsh",
"_TYPER_COMPLETE_ARGS": "tutorial001.py del",
"_TYPER_COMPLETE_TESTING": "True",
},
)
assert (
"""_arguments '*: :(("delete":"Delete a user with USERNAME."\n"""
"""\"delete-all":"Delete ALL users in the database."))'"""
) in result.stdout
def test_completion_complete_subcommand_zsh_files():
result = subprocess.run(
["coverage", "run", mod.__file__, " "],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TUTORIAL001.PY_COMPLETE": "complete_zsh",
"_TYPER_COMPLETE_ARGS": "tutorial001.py delete ",
"_TYPER_COMPLETE_TESTING": "True",
},
)
assert ("_files") in result.stdout
def test_completion_complete_subcommand_fish():
result = subprocess.run(
["coverage", "run", mod.__file__, " "],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TUTORIAL001.PY_COMPLETE": "complete_fish",
"_TYPER_COMPLETE_ARGS": "tutorial001.py del",
"_TYPER_COMPLETE_FISH_ACTION": "get-args",
"_TYPER_COMPLETE_TESTING": "True",
},
)
assert (
"delete\tDelete a user with USERNAME.\ndelete-all\tDelete ALL users in the database."
in result.stdout
)
def test_completion_complete_subcommand_fish_should_complete():
result = subprocess.run(
["coverage", "run", mod.__file__, " "],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TUTORIAL001.PY_COMPLETE": "complete_fish",
"_TYPER_COMPLETE_ARGS": "tutorial001.py del",
"_TYPER_COMPLETE_FISH_ACTION": "is-args",
"_TYPER_COMPLETE_TESTING": "True",
},
)
assert result.returncode == 0
def test_completion_complete_subcommand_fish_should_complete_no():
result = subprocess.run(
["coverage", "run", mod.__file__, " "],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TUTORIAL001.PY_COMPLETE": "complete_fish",
"_TYPER_COMPLETE_ARGS": "tutorial001.py delete ",
"_TYPER_COMPLETE_FISH_ACTION": "is-args",
"_TYPER_COMPLETE_TESTING": "True",
},
)
assert result.returncode != 0
def test_completion_complete_subcommand_powershell():
result = subprocess.run(
["coverage", "run", mod.__file__, " "],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TUTORIAL001.PY_COMPLETE": "complete_powershell",
"_TYPER_COMPLETE_ARGS": "tutorial001.py del",
"_TYPER_COMPLETE_TESTING": "True",
},
)
assert (
"delete:::Delete a user with USERNAME.\ndelete-all:::Delete ALL users in the database."
) in result.stdout
def test_completion_complete_subcommand_pwsh():
result = subprocess.run(
["coverage", "run", mod.__file__, " "],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TUTORIAL001.PY_COMPLETE": "complete_pwsh",
"_TYPER_COMPLETE_ARGS": "tutorial001.py del",
"_TYPER_COMPLETE_TESTING": "True",
},
)
assert (
"delete:::Delete a user with USERNAME.\ndelete-all:::Delete ALL users in the database."
) in result.stdout
def test_completion_complete_subcommand_noshell():
result = subprocess.run(
["coverage", "run", mod.__file__, " "],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TUTORIAL001.PY_COMPLETE": "complete_noshell",
"_TYPER_COMPLETE_ARGS": "tutorial001.py del",
"_TYPER_COMPLETE_TESTING": "True",
},
)
assert ("") in result.stdout

View file

@ -0,0 +1,69 @@
import os
import subprocess
from commands.index import tutorial002 as mod
def test_completion_complete_subcommand_zsh():
result = subprocess.run(
["coverage", "run", mod.__file__, " "],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TUTORIAL002.PY_COMPLETE": "complete_zsh",
"_TYPER_COMPLETE_ARGS": "tutorial002.py ",
"_TYPER_COMPLETE_TESTING": "True",
},
)
assert "create" in result.stdout
assert "delete" in result.stdout
def test_completion_complete_subcommand_fish():
result = subprocess.run(
["coverage", "run", mod.__file__, " "],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TUTORIAL002.PY_COMPLETE": "complete_fish",
"_TYPER_COMPLETE_ARGS": "tutorial002.py ",
"_TYPER_COMPLETE_FISH_ACTION": "get-args",
"_TYPER_COMPLETE_TESTING": "True",
},
)
assert "create\ndelete" in result.stdout
def test_completion_complete_subcommand_powershell():
result = subprocess.run(
["coverage", "run", mod.__file__, " "],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TUTORIAL002.PY_COMPLETE": "complete_powershell",
"_TYPER_COMPLETE_ARGS": "tutorial002.py ",
"_TYPER_COMPLETE_TESTING": "True",
},
)
assert ("create::: \ndelete::: ") in result.stdout
def test_completion_complete_subcommand_pwsh():
result = subprocess.run(
["coverage", "run", mod.__file__, " "],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TUTORIAL002.PY_COMPLETE": "complete_pwsh",
"_TYPER_COMPLETE_ARGS": "tutorial002.py ",
"_TYPER_COMPLETE_TESTING": "True",
},
)
assert ("create::: \ndelete::: ") in result.stdout

View file

@ -0,0 +1,140 @@
import os
import subprocess
from pathlib import Path
from unittest import mock
import shellingham
import typer
from typer.testing import CliRunner
from first_steps import tutorial001 as mod
runner = CliRunner()
app = typer.Typer()
app.command()(mod.main)
def test_completion_install_no_shell():
result = subprocess.run(
["coverage", "run", mod.__file__, "--install-completion"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TYPER_COMPLETE_TESTING": "True",
"_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION": "True",
},
)
assert "Error: --install-completion option requires an argument" in result.stderr
def test_completion_install_bash():
bash_completion_path: Path = Path.home() / ".bash_completion"
text = ""
if bash_completion_path.is_file():
text = bash_completion_path.read_text()
result = subprocess.run(
["coverage", "run", mod.__file__, "--install-completion", "bash"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TYPER_COMPLETE_TESTING": "True",
"_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION": "True",
},
)
new_text = bash_completion_path.read_text()
bash_completion_path.write_text(text)
install_script = 'eval "$(_TUTORIAL001.PY_COMPLETE=source_bash tutorial001.py)'
assert install_script not in text
assert install_script in new_text
assert "completion installed in" in result.stdout
assert "Completion will take effect once you restart the terminal." in result.stdout
def test_completion_install_zsh():
completion_path: Path = Path.home() / ".zshrc"
text = ""
if completion_path.is_file():
text = completion_path.read_text()
result = subprocess.run(
["coverage", "run", mod.__file__, "--install-completion", "zsh"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TYPER_COMPLETE_TESTING": "True",
"_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION": "True",
},
)
new_text = completion_path.read_text()
completion_path.write_text(text)
install_script = 'eval "$(_TUTORIAL001.PY_COMPLETE=source_szh tutorial001.py)"'
assert install_script not in text
assert install_script in new_text
assert "completion installed in" in result.stdout
assert "Completion will take effect once you restart the terminal." in result.stdout
def test_completion_install_fish():
script_path = Path(mod.__file__)
completion_path: Path = Path.home() / f".config/fish/completions/{script_path.name}.fish"
text = ""
if completion_path.is_file():
text = completion_path.read_text()
result = subprocess.run(
["coverage", "run", mod.__file__, "--install-completion", "fish"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TYPER_COMPLETE_TESTING": "True",
"_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION": "True",
},
)
new_text = completion_path.read_text()
completion_path.write_text(text)
install_script = "eval (env _TUTORIAL001.PY_COMPLETE=source_fish tutorial001.py)"
assert install_script not in text
assert install_script in new_text
assert "completion installed in" in result.stdout
assert "Completion will take effect once you restart the terminal." in result.stdout
runner = CliRunner()
app = typer.Typer()
app.command()(mod.main)
def test_completion_install_powershell():
completion_path: Path = Path.home() / f".config/powershell/Microsoft.PowerShell_profile.ps1"
completion_path_bytes = f"{completion_path}\n".encode("windows-1252")
text = ""
if completion_path.is_file():
text = completion_path.read_text()
with mock.patch.object(
shellingham, "detect_shell", return_value=("pwsh", "/usr/bin/pwsh")
):
with mock.patch.object(
subprocess,
"run",
return_value=subprocess.CompletedProcess(
["pwsh"], returncode=0, stdout=completion_path_bytes,
),
):
result = runner.invoke(app, ["--install-completion"])
install_script = "Register-ArgumentCompleter -Native -CommandName typer -ScriptBlock $scriptblock"
parent: Path = completion_path.parent
parent.mkdir(parents=True, exist_ok=True)
completion_path.write_text(install_script)
new_text = completion_path.read_text()
completion_path.write_text(text)
assert install_script not in text
assert install_script in new_text
assert "completion installed in" in result.stdout
assert "Completion will take effect once you restart the terminal." in result.stdout

View file

@ -0,0 +1,107 @@
import os
import subprocess
from first_steps import tutorial001 as mod
def test_completion_install_source_bash():
result = subprocess.run(
["coverage", "run", mod.__file__],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TUTORIAL001.PY_COMPLETE": "install-source_bash",
"_TYPER_COMPLETE_TESTING": "True",
},
)
assert (
'eval "$(_TUTORIAL001.PY_COMPLETE=source_bash tutorial001.py)"' in result.stdout
)
def test_completion_install_source_zsh():
result = subprocess.run(
["coverage", "run", mod.__file__],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TUTORIAL001.PY_COMPLETE": "install-source_zsh",
"_TYPER_COMPLETE_TESTING": "True",
},
)
assert (
'eval "$(_TUTORIAL001.PY_COMPLETE=source_szh tutorial001.py)"' in result.stdout
)
def test_completion_install_source_fish():
result = subprocess.run(
["coverage", "run", mod.__file__],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TUTORIAL001.PY_COMPLETE": "install-source_fish",
"_TYPER_COMPLETE_TESTING": "True",
},
)
assert (
"eval (env _TUTORIAL001.PY_COMPLETE=source_fish tutorial001.py)"
in result.stdout
)
def test_completion_install_source_powershell():
result = subprocess.run(
["coverage", "run", mod.__file__],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TUTORIAL001.PY_COMPLETE": "install-source_powershell",
"_TYPER_COMPLETE_TESTING": "True",
},
)
assert (
"Register-ArgumentCompleter -Native -CommandName tutorial001.py -ScriptBlock $scriptblock"
in result.stdout
)
def test_completion_install_source_pwsh():
result = subprocess.run(
["coverage", "run", mod.__file__],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TUTORIAL001.PY_COMPLETE": "install-source_pwsh",
"_TYPER_COMPLETE_TESTING": "True",
},
)
assert (
"Register-ArgumentCompleter -Native -CommandName tutorial001.py -ScriptBlock $scriptblock"
in result.stdout
)
def test_completion_install_source_noshell():
result = subprocess.run(
["coverage", "run", mod.__file__],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TUTORIAL001.PY_COMPLETE": "install-source_noshell",
"_TYPER_COMPLETE_TESTING": "True",
},
)
assert "" in result.stdout

View file

@ -0,0 +1,103 @@
import os
import subprocess
from first_steps import tutorial001 as mod
def test_completion_show_no_shell():
result = subprocess.run(
["coverage", "run", mod.__file__, "--show-completion"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TYPER_COMPLETE_TESTING": "True",
"_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION": "True",
},
)
assert "Error: --show-completion option requires an argument" in result.stderr
def test_completion_show_bash():
result = subprocess.run(
["coverage", "run", mod.__file__, "--show-completion", "bash"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TYPER_COMPLETE_TESTING": "True",
"_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION": "True",
},
)
assert (
"complete -o default -F _tutorial001py_completion tutorial001.py"
in result.stdout
)
def test_completion_source_zsh():
result = subprocess.run(
["coverage", "run", mod.__file__, "--show-completion", "zsh"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TYPER_COMPLETE_TESTING": "True",
"_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION": "True",
},
)
assert "compdef _tutorial001py_completion tutorial001.py" in result.stdout
def test_completion_source_fish():
result = subprocess.run(
["coverage", "run", mod.__file__, "--show-completion", "fish"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TYPER_COMPLETE_TESTING": "True",
"_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION": "True",
},
)
assert "complete --command tutorial001.py --no-files" in result.stdout
def test_completion_source_powershell():
result = subprocess.run(
["coverage", "run", mod.__file__, "--show-completion", "powershell"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TYPER_COMPLETE_TESTING": "True",
"_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION": "True",
},
)
assert (
"Register-ArgumentCompleter -Native -CommandName tutorial001.py -ScriptBlock $scriptblock"
in result.stdout
)
def test_completion_source_pwsh():
result = subprocess.run(
["coverage", "run", mod.__file__, "--show-completion", "pwsh"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={
**os.environ,
"_TYPER_COMPLETE_TESTING": "True",
"_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION": "True",
},
)
assert (
"Register-ArgumentCompleter -Native -CommandName tutorial001.py -ScriptBlock $scriptblock"
in result.stdout
)

View file

@ -1,6 +1,9 @@
from typing import Optional
from unittest import mock
import shellingham
import typer
import typer.completion
from typer.main import solve_typer_info_defaults, solve_typer_info_help
from typer.models import TyperInfo
from typer.testing import CliRunner
@ -49,3 +52,20 @@ def test_defaults_from_info():
# Mainly for coverage/completeness
value = solve_typer_info_defaults(TyperInfo())
assert value
def test_install_invalid_shell():
app = typer.Typer()
@app.command()
def main():
typer.echo("Hello World")
typer.completion.Shells
with mock.patch.object(
shellingham, "detect_shell", return_value=("xshell", "/usr/bin/xshell")
):
result = runner.invoke(app, ["--install-completion"])
assert "Shell xshell is not supported." in result.stdout
result = runner.invoke(app)
assert "Hello World" in result.stdout

View file

@ -1,19 +1,44 @@
import inspect
import os
import re
import subprocess
import sys
from typing import Any
from enum import Enum
from pathlib import Path
from typing import Any, Optional, Tuple
import click
import click_completion
import click_completion.core
import click._bashcomplete
from .params import Option
click_completion.init()
try:
import shellingham
except ImportError: # pragma: nocover
shellingham = None
_click_patched = False
def get_completion_inspect_parameters() -> Tuple[inspect.Parameter, inspect.Parameter]:
completion_init()
test_disable_detection = os.getenv("_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION")
if shellingham and not test_disable_detection:
signature = inspect.signature(_install_completion_placeholder_function)
else:
signature = inspect.signature(_install_completion_no_auto_placeholder_function)
install_param, show_param = signature.parameters.values()
return install_param, show_param
def install_callback(ctx: click.Context, param: click.Parameter, value: Any) -> Any:
if not value or ctx.resilient_parsing:
return value # pragma no cover
shell, path = click_completion.core.install()
if isinstance(value, str):
shell, path = install(shell=value)
else:
shell, path = install()
click.secho(f"{shell} completion installed in {path}.", fg="green")
click.echo("Completion will take effect once you restart the terminal.")
sys.exit(0)
@ -22,10 +47,26 @@ def install_callback(ctx: click.Context, param: click.Parameter, value: Any) ->
def show_callback(ctx: click.Context, param: click.Parameter, value: Any) -> Any:
if not value or ctx.resilient_parsing:
return value # pragma no cover
click.echo(click_completion.core.get_code())
prog_name = ctx.find_root().info_name
assert prog_name
complete_var = "_{}_COMPLETE".format(prog_name.replace("-", "_").upper())
if isinstance(value, str):
shell = value
elif shellingham:
shell, _ = shellingham.detect_shell()
script_content = get_completion_script(prog_name, complete_var, shell)
click.echo(script_content)
sys.exit(0)
class Shells(str, Enum):
bash = "bash"
zsh = "zsh"
fish = "fish"
powershell = "powershell"
pwsh = "pwsh"
# Create a fake command function to extract the completion parameters
def _install_completion_placeholder_function(
install_completion: bool = Option(
@ -46,3 +87,311 @@ def _install_completion_placeholder_function(
),
) -> Any:
pass # pragma no cover
def _install_completion_no_auto_placeholder_function(
install_completion: Shells = Option(
None,
callback=install_callback,
expose_value=False,
help="Install completion for the specified shell.",
),
show_completion: Shells = Option(
None,
callback=show_callback,
expose_value=False,
help="Show completion for the specified shell, to copy it or customize the installation.",
),
) -> Any:
pass # pragma no cover
COMPLETION_SCRIPT_BASH = """
%(complete_func)s() {
local IFS=$'\n'
COMPREPLY=( $( env COMP_WORDS="${COMP_WORDS[*]}" \\
COMP_CWORD=$COMP_CWORD \\
%(autocomplete_var)s=complete_bash $1 ) )
return 0
}
complete -o default -F %(complete_func)s %(prog_name)s
"""
COMPLETION_SCRIPT_ZSH = """
#compdef %(prog_name)s
%(complete_func)s() {
eval $(env _TYPER_COMPLETE_ARGS="${words[1,$CURRENT]}" %(autocomplete_var)s=complete_zsh %(prog_name)s)
}
compdef %(complete_func)s %(prog_name)s
"""
COMPLETION_SCRIPT_FISH = 'complete --command %(prog_name)s --no-files --arguments "(env %(autocomplete_var)s=complete_fish _TYPER_COMPLETE_FISH_ACTION=get-args _TYPER_COMPLETE_ARGS=(commandline -cp) %(prog_name)s)" --condition "env %(autocomplete_var)s=complete_fish _TYPER_COMPLETE_FISH_ACTION=is-args _TYPER_COMPLETE_ARGS=(commandline -cp) %(prog_name)s'
COMPLETION_SCRIPT_POWER_SHELL = """
Import-Module PSReadLine
Set-PSReadLineKeyHandler -Chord Tab -Function MenuComplete
$scriptblock = {
param($wordToComplete, $commandAst, $cursorPosition)
$Env:%(autocomplete_var)s = "complete_powershell"
$Env:_TYPER_COMPLETE_ARGS = $commandAst.ToString()
$Env:_TYPER_COMPLETE_WORD_TO_COMPLETE = $wordToComplete
%(prog_name)s | ForEach-Object {
$commandArray = $_ -Split ":::"
$command = $commandArray[0]
$helpString = $commandArray[1]
[System.Management.Automation.CompletionResult]::new(
$command, $command, 'ParameterValue', $helpString)
}
$Env:%(autocomplete_var)s = ""
$Env:_TYPER_COMPLETE_ARGS = ""
$Env:_TYPER_COMPLETE_WORD_TO_COMPLETE = ""
}
Register-ArgumentCompleter -Native -CommandName %(prog_name)s -ScriptBlock $scriptblock
"""
def install(
shell: Optional[str] = None,
prog_name: Optional[str] = None,
complete_var: Optional[str] = None,
) -> Tuple[str, Path]:
prog_name = prog_name or click.get_current_context().find_root().info_name
assert prog_name
if complete_var is None:
complete_var = "_{}_COMPLETE".format(prog_name.replace("-", "_").upper())
if shell is None and shellingham is not None:
shell, _ = shellingham.detect_shell()
mode = None
if shell == "bash":
path_obj = Path.home() / ".bash_completion"
mode = mode or "a"
elif shell == "zsh":
path_obj = Path.home() / ".zshrc"
mode = mode or "a"
elif shell == "fish":
path_obj = Path.home() / f".config/fish/completions/{prog_name}.fish"
mode = mode or "w"
elif shell in {"powershell", "pwsh"}:
subprocess.run(
[
shell,
"-Command",
"Set-ExecutionPolicy",
"Unrestricted",
"-Scope",
"CurrentUser",
]
)
result = subprocess.run(
[shell, "-NoProfile", "-Command", "echo", "$profile"],
check=True,
stdout=subprocess.PIPE,
)
if result.returncode != 0: # pragma: nocover
click.echo("Couldn't get PowerShell user profile", err=True)
raise click.exceptions.Exit(result.returncode)
path_str = ""
if isinstance(result.stdout, str): # pragma: nocover
path_str = result.stdout
if isinstance(result.stdout, bytes):
try:
# PowerShell would be predominant in Windows
path_str = result.stdout.decode("windows-1252")
except UnicodeDecodeError: # pragma: nocover
try:
path_str = result.stdout.decode("utf8")
except UnicodeDecodeError:
click.echo("Couldn't decode the path automatically", err=True)
raise click.exceptions.Exit(1)
path_obj = Path(path_str.strip())
mode = mode or "a"
else:
click.echo(f"Shell {shell} is not supported.")
raise click.exceptions.Exit(1)
parent_dir: Path = path_obj.parent
parent_dir.mkdir(parents=True, exist_ok=True)
script_content = get_installable_script(prog_name, complete_var, shell)
with path_obj.open(mode=mode) as f:
f.write(f"{script_content}\n")
return shell, path_obj
def get_installable_script(prog_name: str, complete_var: str, shell: str) -> str:
if shell == "bash":
return f'eval "$({complete_var}=source_bash {prog_name})"'
elif shell == "zsh":
return f'eval "$({complete_var}=source_szh {prog_name})"'
elif shell == "fish":
return f"eval (env {complete_var}=source_fish {prog_name})"
elif shell in {"powershell", "pwsh"}:
return get_completion_script(prog_name, complete_var, shell)
return ""
def do_bash_complete(cli: click.Command, prog_name: str) -> bool:
cwords = click.parser.split_arg_string(os.getenv("COMP_WORDS", ""))
cword = int(os.getenv("COMP_CWORD", 0))
args = cwords[1:cword]
try:
incomplete = cwords[cword]
except IndexError:
incomplete = ""
for item in click._bashcomplete.get_choices(cli, prog_name, args, incomplete):
click.echo(item[0])
return True
def do_zsh_complete(cli: click.Command, prog_name: str) -> bool:
completion_args = os.getenv("_TYPER_COMPLETE_ARGS", "")
cwords = click.parser.split_arg_string(completion_args)
args = cwords[1:]
if args and not completion_args.endswith(" "):
incomplete = args[-1]
args = args[:-1]
else:
incomplete = ""
def escape(s: str) -> str:
return (
s.replace('"', '""')
.replace("'", "''")
.replace("$", "\\$")
.replace("`", "\\`")
)
res = []
for item, help in click._bashcomplete.get_choices(cli, prog_name, args, incomplete):
if help:
res.append(f'"{escape(item)}":"{escape(help)}"')
else:
res.append(f'"{escape(item)}"')
if res:
args_str = "\n".join(res)
click.echo(f"_arguments '*: :(({args_str}))'")
else:
click.echo("_files")
return True
def do_fish_complete(cli: click.Command, prog_name: str) -> bool:
completion_args = os.getenv("_TYPER_COMPLETE_ARGS", "")
complete_action = os.getenv("_TYPER_COMPLETE_FISH_ACTION", "")
cwords = click.parser.split_arg_string(completion_args)
args = cwords[1:]
if args and not completion_args.endswith(" "):
incomplete = args[-1]
args = args[:-1]
else:
incomplete = ""
show_args = []
for item, help in click._bashcomplete.get_choices(cli, prog_name, args, incomplete):
if help:
formatted_help = re.sub(r"\s", " ", help)
show_args.append(f"{item}\t{formatted_help}")
else:
show_args.append(item)
if complete_action == "get-args":
if show_args:
for arg in show_args:
click.echo(arg)
elif complete_action == "is-args":
if show_args:
# Activate complete args (no files)
sys.exit(0)
else:
# Deactivate complete args (allow files)
sys.exit(1)
return True
def do_powershell_complete(cli: click.Command, prog_name: str) -> bool:
completion_args = os.getenv("_TYPER_COMPLETE_ARGS", "")
incomplete = os.getenv("_TYPER_COMPLETE_WORD_TO_COMPLETE", "")
cwords = click.parser.split_arg_string(completion_args)
args = cwords[1:]
for item, help in click._bashcomplete.get_choices(cli, prog_name, args, incomplete):
click.echo(f"{item}:::{help or ' '}")
return True
def do_shell_complete(*, cli: click.Command, prog_name: str, shell: str) -> bool:
if shell == "bash":
return do_bash_complete(cli, prog_name)
elif shell == "zsh":
return do_zsh_complete(cli, prog_name)
elif shell == "fish":
return do_fish_complete(cli, prog_name)
elif shell in {"powershell", "pwsh"}:
return do_powershell_complete(cli, prog_name)
return False
_completion_scripts = {
"bash": COMPLETION_SCRIPT_BASH,
"zsh": COMPLETION_SCRIPT_ZSH,
"fish": COMPLETION_SCRIPT_FISH,
"powershell": COMPLETION_SCRIPT_POWER_SHELL,
"pwsh": COMPLETION_SCRIPT_POWER_SHELL,
}
def get_completion_script(prog_name: str, complete_var: str, shell: str) -> str:
cf_name = click._bashcomplete._invalid_ident_char_re.sub(
"", prog_name.replace("-", "_")
)
script = _completion_scripts.get(shell)
if script is None:
click.echo(f"Shell {shell} not supported.", err=True)
sys.exit(1)
return (
script
% dict(
complete_func="_{}_completion".format(cf_name),
prog_name=prog_name,
autocomplete_var=complete_var,
)
).strip()
def handle_shell_complete(
cli: click.Command, prog_name: str, complete_var: str, complete_instr: str
) -> bool:
if "_" not in complete_instr:
click.echo("Invalid completion instruction.", err=True)
sys.exit(1)
command, shell = complete_instr.split("_", 1)
if command == "source":
click.echo(get_completion_script(prog_name, complete_var, shell))
return True
elif command == "install-source":
click.echo(get_installable_script(prog_name, complete_var, shell))
return True
elif command == "complete":
return do_shell_complete(cli=cli, prog_name=prog_name, shell=shell)
return False
def completion_init() -> None:
global _click_patched
if not _click_patched:
testing = os.getenv("_TYPER_COMPLETE_TESTING")
def testing_handle_shell_complete(
cli: click.Command, prog_name: str, complete_var: str, complete_instr: str
) -> bool:
result = handle_shell_complete(cli, prog_name, complete_var, complete_instr)
if result:
# Avoid fast_exit(1) in Click so Coverage can finish
sys.exit(1)
return result
if testing:
click._bashcomplete.bashcomplete = testing_handle_shell_complete
else:
click._bashcomplete.bashcomplete = handle_shell_complete
_click_patched = True

View file

@ -8,6 +8,7 @@ from uuid import UUID
import click
from .completion import get_completion_inspect_parameters
from .models import (
AnyType,
ArgumentInfo,
@ -26,16 +27,9 @@ from .models import (
TyperInfo,
)
try:
import click_completion
from .completion import _install_completion_placeholder_function
except ImportError: # pragma: no cover
click_completion = None
def get_install_completion_arguments() -> Tuple[click.Parameter, click.Parameter]:
signature = inspect.signature(_install_completion_placeholder_function)
install_param, show_param = signature.parameters.values()
install_param, show_param = get_completion_inspect_parameters()
click_install_param, _ = get_click_param(install_param)
click_show_param, _ = get_click_param(show_param)
return click_install_param, click_show_param
@ -223,8 +217,7 @@ def get_group(typer_instance: Typer) -> click.Command:
def get_command(typer_instance: Typer) -> click.Command:
if typer_instance._add_completion and click_completion:
click_completion.init()
if typer_instance._add_completion:
click_install_param, click_show_param = get_install_completion_arguments()
if (
typer_instance.registered_callback
@ -234,14 +227,14 @@ def get_command(typer_instance: Typer) -> click.Command:
):
# Create a Group
click_command = get_group(typer_instance)
if typer_instance._add_completion and click_completion:
if typer_instance._add_completion:
click_command.params.append(click_install_param)
click_command.params.append(click_show_param)
return click_command
elif len(typer_instance.registered_commands) == 1:
# Create a single Command
click_command = get_command_from_info(typer_instance.registered_commands[0])
if typer_instance._add_completion and click_completion:
if typer_instance._add_completion:
click_command.params.append(click_install_param)
click_command.params.append(click_show_param)
return click_command