```console
-$ typer ./main.py run --name [TAB][TAB]
+$ typer ./main.py run --user [TAB][TAB]
-// The first time we trigger completion, we get all the names
+// The first time we trigger completion, we get all the users
Camila -- The reader of books.
Carlos -- The writer of scripts.
Sebastian -- The type hints guy.
-// Add a name and trigger completion again
-$ typer ./main.py run --name Sebastian --name Ca[TAB][TAB]
+// Add a user and trigger completion again
+$ typer ./main.py run --user Sebastian --user Ca[TAB][TAB]
-// Now we get completion only for the names we haven't used 🎉
+// Now we get completion only for the users we haven't used 🎉
Camila -- The reader of books.
Carlos -- The writer of scripts.
-// And if we add another of the available names:
-$ typer ./main.py run --name Sebastian --name Camila --name [TAB][TAB]
+// And if we add another of the available users:
+$ typer ./main.py run --user Sebastian --user Camila --user [TAB][TAB]
// We get completion for the only available one
Carlos -- The writer of scripts.
@@ -264,11 +264,43 @@ It's quite possible that if there's only one option left, your shell will comple
///
+## Reusing generic completer functions
+
+You may want to reuse completer functions across CLI applications or within the same CLI application. In this case, you need to first determine which parameter is being asked to complete.
+
+This can be done by declaring a parameter of type
, and accessing its `param.name` attribute.
+
+For example, lets revisit our above example and add a second greeter argument that reuses the same completer function, now called `complete_user_or_greeter`:
+
+{* docs_src/options_autocompletion/tutorial010_an.py hl[15:16] *}
+
+/// tip
+
+You may also return
+
+```console
+$ typer ./main.py run --user Sebastian --greeter Camila --greeter [TAB][TAB]
+
+// Our function returns Sebastian too because it is completing 'greeter', not 'user'
+Carlos -- The writer of scripts.
+Sebastian -- The type hints guy.
+```
+
+
+
+
## Getting the raw *CLI parameters*
You can also get the raw *CLI parameters*, just a `list` of `str` with everything passed in the command line before the incomplete value.
-For example, something like `["typer", "main.py", "run", "--name"]`.
+For example, something like `["typer", "main.py", "run", "--user"]`.
/// tip
@@ -319,10 +351,10 @@ And then we just print it to "standard error".
```console
-$ typer ./main.py run --name [TAB][TAB]
+$ typer ./main.py run --user [TAB][TAB]
// First we see the raw CLI parameters
-['./main.py', 'run', '--name']
+['./main.py', 'run', '--user']
// And then we see the actual completion
Camila -- The reader of books.
@@ -342,7 +374,7 @@ But it's probably useful only in very advanced use cases.
## Getting the Context and the raw *CLI parameters*
-Of course, you can declare everything if you need it, the context, the raw *CLI parameters*, and the incomplete `str`:
+Of course, you can declare everything if you need it, the context, the raw *CLI parameters*, the `Parameter` and the incomplete `str`:
{* docs_src/options_autocompletion/tutorial009_an.py hl[16] *}
@@ -351,20 +383,20 @@ Check it:
```console
-$ typer ./main.py run --name [TAB][TAB]
+$ typer ./main.py run --user [TAB][TAB]
// First we see the raw CLI parameters
-['./main.py', 'run', '--name']
+['./main.py', 'run', '--user']
// And then we see the actual completion
Camila -- The reader of books.
Carlos -- The writer of scripts.
Sebastian -- The type hints guy.
-$ typer ./main.py run --name Sebastian --name Ca[TAB][TAB]
+$ typer ./main.py run --user Sebastian --user Ca[TAB][TAB]
// Again, we see the raw CLI parameters
-['./main.py', 'run', '--name', 'Sebastian', '--name']
+['./main.py', 'run', '--user', 'Sebastian', '--user']
// And then we see the rest of the valid completion items
Camila -- The reader of books.
@@ -381,9 +413,10 @@ You can declare function parameters of these types:
* `str`: for the incomplete value.
* `typer.Context`: for the current context.
+* `click.Parameter`: for the CLI parameter being completed.
* `List[str]`: for the raw *CLI parameters*.
-It doesn't matter how you name them, in which order, or which ones of the 3 options you declare. It will all "**just work**" ✨
+It doesn't matter how you name them, in which order, or which ones of the 4 options you declare. It will all "**just work**" ✨
## Comparison to Click functionality
diff --git a/docs_src/options_autocompletion/tutorial001.py b/docs_src/options_autocompletion/tutorial001.py
index 1cfc18cc20..6f4b53a875 100644
--- a/docs_src/options_autocompletion/tutorial001.py
+++ b/docs_src/options_autocompletion/tutorial001.py
@@ -4,8 +4,8 @@
@app.command()
-def main(name: str = typer.Option("World", help="The name to say hi to.")):
- print(f"Hello {name}")
+def main(user: str = typer.Option("World", help="The user to say hi to.")):
+ print(f"Hello {user}")
if __name__ == "__main__":
diff --git a/docs_src/options_autocompletion/tutorial001_an.py b/docs_src/options_autocompletion/tutorial001_an.py
index d39641253b..df5ec665ed 100644
--- a/docs_src/options_autocompletion/tutorial001_an.py
+++ b/docs_src/options_autocompletion/tutorial001_an.py
@@ -5,8 +5,8 @@
@app.command()
-def main(name: Annotated[str, typer.Option(help="The name to say hi to.")] = "World"):
- print(f"Hello {name}")
+def main(user: Annotated[str, typer.Option(help="The user to say hi to.")] = "World"):
+ print(f"Hello {user}")
if __name__ == "__main__":
diff --git a/docs_src/options_autocompletion/tutorial002.py b/docs_src/options_autocompletion/tutorial002.py
index 6c14a97ce4..934710734f 100644
--- a/docs_src/options_autocompletion/tutorial002.py
+++ b/docs_src/options_autocompletion/tutorial002.py
@@ -1,7 +1,7 @@
import typer
-def complete_name():
+def complete_user():
return ["Camila", "Carlos", "Sebastian"]
@@ -10,11 +10,11 @@ def complete_name():
@app.command()
def main(
- name: str = typer.Option(
- "World", help="The name to say hi to.", autocompletion=complete_name
+ user: str = typer.Option(
+ "World", help="The user to say hi to.", autocompletion=complete_user
),
):
- print(f"Hello {name}")
+ print(f"Hello {user}")
if __name__ == "__main__":
diff --git a/docs_src/options_autocompletion/tutorial002_an.py b/docs_src/options_autocompletion/tutorial002_an.py
index a2df928a0f..e4dc7377bc 100644
--- a/docs_src/options_autocompletion/tutorial002_an.py
+++ b/docs_src/options_autocompletion/tutorial002_an.py
@@ -2,7 +2,7 @@
from typing_extensions import Annotated
-def complete_name():
+def complete_user():
return ["Camila", "Carlos", "Sebastian"]
@@ -11,11 +11,11 @@ def complete_name():
@app.command()
def main(
- name: Annotated[
- str, typer.Option(help="The name to say hi to.", autocompletion=complete_name)
+ user: Annotated[
+ str, typer.Option(help="The user to say hi to.", autocompletion=complete_user)
] = "World",
):
- print(f"Hello {name}")
+ print(f"Hello {user}")
if __name__ == "__main__":
diff --git a/docs_src/options_autocompletion/tutorial003.py b/docs_src/options_autocompletion/tutorial003.py
index 9af41f23b5..784ac04cf3 100644
--- a/docs_src/options_autocompletion/tutorial003.py
+++ b/docs_src/options_autocompletion/tutorial003.py
@@ -1,13 +1,13 @@
import typer
-valid_names = ["Camila", "Carlos", "Sebastian"]
+valid_users = ["Camila", "Carlos", "Sebastian"]
-def complete_name(incomplete: str):
+def complete_user(incomplete: str):
completion = []
- for name in valid_names:
- if name.startswith(incomplete):
- completion.append(name)
+ for user in valid_users:
+ if user.startswith(incomplete):
+ completion.append(user)
return completion
@@ -16,11 +16,11 @@ def complete_name(incomplete: str):
@app.command()
def main(
- name: str = typer.Option(
- "World", help="The name to say hi to.", autocompletion=complete_name
+ user: str = typer.Option(
+ "World", help="The user to say hi to.", autocompletion=complete_user
),
):
- print(f"Hello {name}")
+ print(f"Hello {user}")
if __name__ == "__main__":
diff --git a/docs_src/options_autocompletion/tutorial003_an.py b/docs_src/options_autocompletion/tutorial003_an.py
index ed874388af..0dc622e8ff 100644
--- a/docs_src/options_autocompletion/tutorial003_an.py
+++ b/docs_src/options_autocompletion/tutorial003_an.py
@@ -1,14 +1,14 @@
import typer
from typing_extensions import Annotated
-valid_names = ["Camila", "Carlos", "Sebastian"]
+valid_users = ["Camila", "Carlos", "Sebastian"]
-def complete_name(incomplete: str):
+def complete_user(incomplete: str):
completion = []
- for name in valid_names:
- if name.startswith(incomplete):
- completion.append(name)
+ for user in valid_users:
+ if user.startswith(incomplete):
+ completion.append(user)
return completion
@@ -17,11 +17,11 @@ def complete_name(incomplete: str):
@app.command()
def main(
- name: Annotated[
- str, typer.Option(help="The name to say hi to.", autocompletion=complete_name)
+ user: Annotated[
+ str, typer.Option(help="The user to say hi to.", autocompletion=complete_user)
] = "World",
):
- print(f"Hello {name}")
+ print(f"Hello {user}")
if __name__ == "__main__":
diff --git a/docs_src/options_autocompletion/tutorial004.py b/docs_src/options_autocompletion/tutorial004.py
index 3be0cf35db..cf03a0d10e 100644
--- a/docs_src/options_autocompletion/tutorial004.py
+++ b/docs_src/options_autocompletion/tutorial004.py
@@ -7,11 +7,11 @@
]
-def complete_name(incomplete: str):
+def complete_user(incomplete: str):
completion = []
- for name, help_text in valid_completion_items:
- if name.startswith(incomplete):
- completion_item = (name, help_text)
+ for user, help_text in valid_completion_items:
+ if user.startswith(incomplete):
+ completion_item = (user, help_text)
completion.append(completion_item)
return completion
@@ -21,11 +21,11 @@ def complete_name(incomplete: str):
@app.command()
def main(
- name: str = typer.Option(
- "World", help="The name to say hi to.", autocompletion=complete_name
+ user: str = typer.Option(
+ "World", help="The user to say hi to.", autocompletion=complete_user
),
):
- print(f"Hello {name}")
+ print(f"Hello {user}")
if __name__ == "__main__":
diff --git a/docs_src/options_autocompletion/tutorial004_an.py b/docs_src/options_autocompletion/tutorial004_an.py
index 673f8a4d34..475a0696a2 100644
--- a/docs_src/options_autocompletion/tutorial004_an.py
+++ b/docs_src/options_autocompletion/tutorial004_an.py
@@ -8,11 +8,11 @@
]
-def complete_name(incomplete: str):
+def complete_user(incomplete: str):
completion = []
- for name, help_text in valid_completion_items:
- if name.startswith(incomplete):
- completion_item = (name, help_text)
+ for user, help_text in valid_completion_items:
+ if user.startswith(incomplete):
+ completion_item = (user, help_text)
completion.append(completion_item)
return completion
@@ -22,11 +22,11 @@ def complete_name(incomplete: str):
@app.command()
def main(
- name: Annotated[
- str, typer.Option(help="The name to say hi to.", autocompletion=complete_name)
+ user: Annotated[
+ str, typer.Option(help="The user to say hi to.", autocompletion=complete_user)
] = "World",
):
- print(f"Hello {name}")
+ print(f"Hello {user}")
if __name__ == "__main__":
diff --git a/docs_src/options_autocompletion/tutorial005.py b/docs_src/options_autocompletion/tutorial005.py
index 10ac532ad2..9f993b89fd 100644
--- a/docs_src/options_autocompletion/tutorial005.py
+++ b/docs_src/options_autocompletion/tutorial005.py
@@ -7,10 +7,10 @@
]
-def complete_name(incomplete: str):
- for name, help_text in valid_completion_items:
- if name.startswith(incomplete):
- yield (name, help_text)
+def complete_user(incomplete: str):
+ for user, help_text in valid_completion_items:
+ if user.startswith(incomplete):
+ yield (user, help_text)
app = typer.Typer()
@@ -18,11 +18,11 @@ def complete_name(incomplete: str):
@app.command()
def main(
- name: str = typer.Option(
- "World", help="The name to say hi to.", autocompletion=complete_name
+ user: str = typer.Option(
+ "World", help="The user to say hi to.", autocompletion=complete_user
),
):
- print(f"Hello {name}")
+ print(f"Hello {user}")
if __name__ == "__main__":
diff --git a/docs_src/options_autocompletion/tutorial005_an.py b/docs_src/options_autocompletion/tutorial005_an.py
index bd1b62d7da..522eecacbf 100644
--- a/docs_src/options_autocompletion/tutorial005_an.py
+++ b/docs_src/options_autocompletion/tutorial005_an.py
@@ -8,10 +8,10 @@
]
-def complete_name(incomplete: str):
- for name, help_text in valid_completion_items:
- if name.startswith(incomplete):
- yield (name, help_text)
+def complete_user(incomplete: str):
+ for user, help_text in valid_completion_items:
+ if user.startswith(incomplete):
+ yield (user, help_text)
app = typer.Typer()
@@ -19,11 +19,11 @@ def complete_name(incomplete: str):
@app.command()
def main(
- name: Annotated[
- str, typer.Option(help="The name to say hi to.", autocompletion=complete_name)
+ user: Annotated[
+ str, typer.Option(help="The user to say hi to.", autocompletion=complete_user)
] = "World",
):
- print(f"Hello {name}")
+ print(f"Hello {user}")
if __name__ == "__main__":
diff --git a/docs_src/options_autocompletion/tutorial006.py b/docs_src/options_autocompletion/tutorial006.py
index 4e6dba302a..81744d5f2b 100644
--- a/docs_src/options_autocompletion/tutorial006.py
+++ b/docs_src/options_autocompletion/tutorial006.py
@@ -6,9 +6,9 @@
@app.command()
-def main(name: List[str] = typer.Option(["World"], help="The name to say hi to.")):
- for each_name in name:
- print(f"Hello {each_name}")
+def main(user: List[str] = typer.Option(["World"], help="The user to say hi to.")):
+ for u in user:
+ print(f"Hello {u}")
if __name__ == "__main__":
diff --git a/docs_src/options_autocompletion/tutorial006_an.py b/docs_src/options_autocompletion/tutorial006_an.py
index 46319c2ecd..a00b6b4138 100644
--- a/docs_src/options_autocompletion/tutorial006_an.py
+++ b/docs_src/options_autocompletion/tutorial006_an.py
@@ -8,10 +8,10 @@
@app.command()
def main(
- name: Annotated[List[str], typer.Option(help="The name to say hi to.")] = ["World"],
+ user: Annotated[List[str], typer.Option(help="The user to say hi to.")] = ["World"],
):
- for each_name in name:
- print(f"Hello {each_name}")
+ for u in user:
+ print(f"Hello {u}")
if __name__ == "__main__":
diff --git a/docs_src/options_autocompletion/tutorial007.py b/docs_src/options_autocompletion/tutorial007.py
index 7c56ac7549..acf4fb600c 100644
--- a/docs_src/options_autocompletion/tutorial007.py
+++ b/docs_src/options_autocompletion/tutorial007.py
@@ -9,11 +9,11 @@
]
-def complete_name(ctx: typer.Context, incomplete: str):
- names = ctx.params.get("name") or []
- for name, help_text in valid_completion_items:
- if name.startswith(incomplete) and name not in names:
- yield (name, help_text)
+def complete_user(ctx: typer.Context, incomplete: str):
+ previous_users = ctx.params.get("user") or []
+ for user, help_text in valid_completion_items:
+ if user.startswith(incomplete) and user not in previous_users:
+ yield (user, help_text)
app = typer.Typer()
@@ -21,12 +21,12 @@ def complete_name(ctx: typer.Context, incomplete: str):
@app.command()
def main(
- name: List[str] = typer.Option(
- ["World"], help="The name to say hi to.", autocompletion=complete_name
+ user: List[str] = typer.Option(
+ ["World"], help="The user to say hi to.", autocompletion=complete_user
),
):
- for n in name:
- print(f"Hello {n}")
+ for u in user:
+ print(f"Hello {u}")
if __name__ == "__main__":
diff --git a/docs_src/options_autocompletion/tutorial007_an.py b/docs_src/options_autocompletion/tutorial007_an.py
index de6c8054e4..c994ed7412 100644
--- a/docs_src/options_autocompletion/tutorial007_an.py
+++ b/docs_src/options_autocompletion/tutorial007_an.py
@@ -10,11 +10,11 @@
]
-def complete_name(ctx: typer.Context, incomplete: str):
- names = ctx.params.get("name") or []
- for name, help_text in valid_completion_items:
- if name.startswith(incomplete) and name not in names:
- yield (name, help_text)
+def complete_user(ctx: typer.Context, incomplete: str):
+ users = ctx.params.get("user") or []
+ for user, help_text in valid_completion_items:
+ if user.startswith(incomplete) and user not in users:
+ yield (user, help_text)
app = typer.Typer()
@@ -22,13 +22,13 @@ def complete_name(ctx: typer.Context, incomplete: str):
@app.command()
def main(
- name: Annotated[
+ user: Annotated[
List[str],
- typer.Option(help="The name to say hi to.", autocompletion=complete_name),
+ typer.Option(help="The user to say hi to.", autocompletion=complete_user),
] = ["World"],
):
- for n in name:
- print(f"Hello {n}")
+ for u in user:
+ print(f"Hello {u}")
if __name__ == "__main__":
diff --git a/docs_src/options_autocompletion/tutorial008.py b/docs_src/options_autocompletion/tutorial008.py
index 118f4dd346..8399034f95 100644
--- a/docs_src/options_autocompletion/tutorial008.py
+++ b/docs_src/options_autocompletion/tutorial008.py
@@ -12,11 +12,11 @@
err_console = Console(stderr=True)
-def complete_name(args: List[str], incomplete: str):
+def complete_user(args: List[str], incomplete: str):
err_console.print(f"{args}")
- for name, help_text in valid_completion_items:
- if name.startswith(incomplete):
- yield (name, help_text)
+ for user, help_text in valid_completion_items:
+ if user.startswith(incomplete):
+ yield (user, help_text)
app = typer.Typer()
@@ -24,12 +24,12 @@ def complete_name(args: List[str], incomplete: str):
@app.command()
def main(
- name: List[str] = typer.Option(
- ["World"], help="The name to say hi to.", autocompletion=complete_name
+ user: List[str] = typer.Option(
+ ["World"], help="The user to say hi to.", autocompletion=complete_user
),
):
- for n in name:
- print(f"Hello {n}")
+ for u in user:
+ print(f"Hello {u}")
if __name__ == "__main__":
diff --git a/docs_src/options_autocompletion/tutorial008_an.py b/docs_src/options_autocompletion/tutorial008_an.py
index 9dcb0df77e..d7b1847d95 100644
--- a/docs_src/options_autocompletion/tutorial008_an.py
+++ b/docs_src/options_autocompletion/tutorial008_an.py
@@ -13,11 +13,11 @@
err_console = Console(stderr=True)
-def complete_name(args: List[str], incomplete: str):
+def complete_user(args: List[str], incomplete: str):
err_console.print(f"{args}")
- for name, help_text in valid_completion_items:
- if name.startswith(incomplete):
- yield (name, help_text)
+ for user, help_text in valid_completion_items:
+ if user.startswith(incomplete):
+ yield (user, help_text)
app = typer.Typer()
@@ -25,13 +25,13 @@ def complete_name(args: List[str], incomplete: str):
@app.command()
def main(
- name: Annotated[
+ user: Annotated[
List[str],
- typer.Option(help="The name to say hi to.", autocompletion=complete_name),
+ typer.Option(help="The user to say hi to.", autocompletion=complete_user),
] = ["World"],
):
- for n in name:
- print(f"Hello {n}")
+ for u in user:
+ print(f"Hello {u}")
if __name__ == "__main__":
diff --git a/docs_src/options_autocompletion/tutorial009.py b/docs_src/options_autocompletion/tutorial009.py
index 7e82c7ff07..958beb821f 100644
--- a/docs_src/options_autocompletion/tutorial009.py
+++ b/docs_src/options_autocompletion/tutorial009.py
@@ -1,6 +1,7 @@
from typing import List
import typer
+from click.core import Parameter
from rich.console import Console
valid_completion_items = [
@@ -12,12 +13,14 @@
err_console = Console(stderr=True)
-def complete_name(ctx: typer.Context, args: List[str], incomplete: str):
+def complete_user(
+ ctx: typer.Context, args: List[str], param: Parameter, incomplete: str
+):
err_console.print(f"{args}")
- names = ctx.params.get("name") or []
- for name, help_text in valid_completion_items:
- if name.startswith(incomplete) and name not in names:
- yield (name, help_text)
+ previous_users = ctx.params.get(param.name) or []
+ for user, help_text in valid_completion_items:
+ if user.startswith(incomplete) and user not in previous_users:
+ yield (user, help_text)
app = typer.Typer()
@@ -25,12 +28,12 @@ def complete_name(ctx: typer.Context, args: List[str], incomplete: str):
@app.command()
def main(
- name: List[str] = typer.Option(
- ["World"], help="The name to say hi to.", autocompletion=complete_name
+ user: List[str] = typer.Option(
+ ["World"], help="The user to say hi to.", autocompletion=complete_user
),
):
- for n in name:
- print(f"Hello {n}")
+ for u in user:
+ print(f"Hello {u}")
if __name__ == "__main__":
diff --git a/docs_src/options_autocompletion/tutorial009_an.py b/docs_src/options_autocompletion/tutorial009_an.py
index c5b825eaf0..64640ebf59 100644
--- a/docs_src/options_autocompletion/tutorial009_an.py
+++ b/docs_src/options_autocompletion/tutorial009_an.py
@@ -1,6 +1,7 @@
from typing import List
import typer
+from click.core import Parameter
from rich.console import Console
from typing_extensions import Annotated
@@ -13,12 +14,14 @@
err_console = Console(stderr=True)
-def complete_name(ctx: typer.Context, args: List[str], incomplete: str):
+def complete_user(
+ ctx: typer.Context, args: List[str], param: Parameter, incomplete: str
+):
err_console.print(f"{args}")
- names = ctx.params.get("name") or []
- for name, help_text in valid_completion_items:
- if name.startswith(incomplete) and name not in names:
- yield (name, help_text)
+ previous_users = ctx.params.get(param.name) or []
+ for user, help_text in valid_completion_items:
+ if user.startswith(incomplete) and user not in previous_users:
+ yield (user, help_text)
app = typer.Typer()
@@ -26,13 +29,13 @@ def complete_name(ctx: typer.Context, args: List[str], incomplete: str):
@app.command()
def main(
- name: Annotated[
+ user: Annotated[
List[str],
- typer.Option(help="The name to say hi to.", autocompletion=complete_name),
+ typer.Option(help="The user to say hi to.", autocompletion=complete_user),
] = ["World"],
):
- for n in name:
- print(f"Hello {n}")
+ for u in user:
+ print(f"Hello {u}")
if __name__ == "__main__":
diff --git a/docs_src/options_autocompletion/tutorial010.py b/docs_src/options_autocompletion/tutorial010.py
new file mode 100644
index 0000000000..dce763cead
--- /dev/null
+++ b/docs_src/options_autocompletion/tutorial010.py
@@ -0,0 +1,42 @@
+from typing import List
+
+import click
+import typer
+from click.shell_completion import CompletionItem
+
+valid_completion_items = [
+ ("Camila", "The reader of books."),
+ ("Carlos", "The writer of scripts."),
+ ("Sebastian", "The type hints guy."),
+]
+
+
+def complete_user_or_greeter(
+ ctx: typer.Context, param: click.Parameter, incomplete: str
+):
+ previous_items = (ctx.params.get(param.name) if param.name else []) or []
+ for item, help_text in valid_completion_items:
+ if item.startswith(incomplete) and item not in previous_items:
+ yield CompletionItem(item, help=help_text)
+
+
+app = typer.Typer()
+
+
+@app.command()
+def main(
+ user: List[str] = typer.Option(
+ ["World"],
+ help="The user to say hi to.",
+ autocompletion=complete_user_or_greeter,
+ ),
+ greeter: List[str] = typer.Option(
+ None, help="The greeters.", autocompletion=complete_user_or_greeter
+ ),
+):
+ for u in user:
+ print(f"Hello {u}, from {' and '.join(greeter or [])}")
+
+
+if __name__ == "__main__":
+ app()
diff --git a/docs_src/options_autocompletion/tutorial010_an.py b/docs_src/options_autocompletion/tutorial010_an.py
new file mode 100644
index 0000000000..8ced36e546
--- /dev/null
+++ b/docs_src/options_autocompletion/tutorial010_an.py
@@ -0,0 +1,45 @@
+from typing import List
+
+import click
+import typer
+from click.shell_completion import CompletionItem
+from typing_extensions import Annotated
+
+valid_completion_items = [
+ ("Camila", "The reader of books."),
+ ("Carlos", "The writer of scripts."),
+ ("Sebastian", "The type hints guy."),
+]
+
+
+def complete_user_or_greeter(
+ ctx: typer.Context, param: click.Parameter, incomplete: str
+):
+ previous_items = (ctx.params.get(param.name) if param.name else []) or []
+ for item, help_text in valid_completion_items:
+ if item.startswith(incomplete) and item not in previous_items:
+ yield CompletionItem(item, help=help_text)
+
+
+app = typer.Typer()
+
+
+@app.command()
+def main(
+ user: Annotated[
+ List[str],
+ typer.Option(
+ help="The user to say hi to.", autocompletion=complete_user_or_greeter
+ ),
+ ] = ["World"],
+ greeter: Annotated[
+ List[str],
+ typer.Option(help="The greeters.", autocompletion=complete_user_or_greeter),
+ ] = [],
+):
+ for u in user:
+ print(f"Hello {u}, from {' and '.join(greeter)}")
+
+
+if __name__ == "__main__":
+ app()
diff --git a/pyproject.toml b/pyproject.toml
index ac89bbd9bb..0987bc5754 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -187,6 +187,7 @@ ignore = [
"docs_src/options_autocompletion/tutorial007_an.py" = ["B006"]
"docs_src/options_autocompletion/tutorial008_an.py" = ["B006"]
"docs_src/options_autocompletion/tutorial009_an.py" = ["B006"]
+"docs_src/options_autocompletion/tutorial010_an.py" = ["B006"]
"docs_src/parameter_types/enum/tutorial003_an.py" = ["B006"]
# Loop control variable `value` not used within loop body
"docs_src/progressbar/tutorial001.py" = ["B007"]
diff --git a/tests/assets/completion_argument.py b/tests/assets/completion_argument.py
index f91e2b7cfb..a2536d171d 100644
--- a/tests/assets/completion_argument.py
+++ b/tests/assets/completion_argument.py
@@ -12,7 +12,7 @@ def shell_complete(ctx: click.Context, param: click.Parameter, incomplete: str):
@app.command(context_settings={"auto_envvar_prefix": "TEST"})
-def main(name: str = typer.Argument(shell_complete=shell_complete)):
+def main(user: str = typer.Argument(shell_complete=shell_complete)):
"""
Say hello.
"""
diff --git a/tests/assets/completion_no_types.py b/tests/assets/completion_no_types.py
index 8dc610a1b2..dbf114d5d6 100644
--- a/tests/assets/completion_no_types.py
+++ b/tests/assets/completion_no_types.py
@@ -3,9 +3,10 @@
app = typer.Typer()
-def complete(ctx, args, incomplete):
+def complete(ctx, args, param, incomplete):
typer.echo(f"info name is: {ctx.info_name}", err=True)
typer.echo(f"args is: {args}", err=True)
+ typer.echo(f"param is: {param.name}", err=True)
typer.echo(f"incomplete is: {incomplete}", err=True)
return [
("Camila", "The reader of books."),
@@ -15,8 +16,8 @@ def complete(ctx, args, incomplete):
@app.command()
-def main(name: str = typer.Option("World", autocompletion=complete)):
- print(f"Hello {name}")
+def main(user: str = typer.Option("World", autocompletion=complete)):
+ print(f"Hello {user}")
if __name__ == "__main__":
diff --git a/tests/assets/completion_no_types_order.py b/tests/assets/completion_no_types_order.py
index dbbbc77f19..0acffdda58 100644
--- a/tests/assets/completion_no_types_order.py
+++ b/tests/assets/completion_no_types_order.py
@@ -3,9 +3,10 @@
app = typer.Typer()
-def complete(args, incomplete, ctx):
+def complete(args, incomplete, ctx, param):
typer.echo(f"info name is: {ctx.info_name}", err=True)
typer.echo(f"args is: {args}", err=True)
+ typer.echo(f"param is: {param.name}", err=True)
typer.echo(f"incomplete is: {incomplete}", err=True)
return [
("Camila", "The reader of books."),
@@ -15,8 +16,8 @@ def complete(args, incomplete, ctx):
@app.command()
-def main(name: str = typer.Option("World", autocompletion=complete)):
- print(f"Hello {name}")
+def main(user: str = typer.Option("World", autocompletion=complete)):
+ print(f"Hello {user}")
if __name__ == "__main__":
diff --git a/tests/test_others.py b/tests/test_others.py
index a8ba207a5f..fa3090b9b0 100644
--- a/tests/test_others.py
+++ b/tests/test_others.py
@@ -202,7 +202,7 @@ def test_completion_argument():
)
assert "Emma" in result.stdout or "_files" in result.stdout
assert "ctx: completion_argument" in result.stderr
- assert "arg is: name" in result.stderr
+ assert "arg is: user" in result.stderr
assert "incomplete is: E" in result.stderr
@@ -215,11 +215,12 @@ def test_completion_untyped_parameters():
env={
**os.environ,
"_COMPLETION_NO_TYPES.PY_COMPLETE": "complete_zsh",
- "_TYPER_COMPLETE_ARGS": "completion_no_types.py --name Sebastian --name Ca",
+ "_TYPER_COMPLETE_ARGS": "completion_no_types.py --user Sebastian --user Ca",
},
)
assert "info name is: completion_no_types.py" in result.stderr
assert "args is: []" in result.stderr
+ assert "param is: user" in result.stderr
assert "incomplete is: Ca" in result.stderr
assert '"Camila":"The reader of books."' in result.stdout
assert '"Carlos":"The writer of scripts."' in result.stdout
@@ -241,11 +242,12 @@ def test_completion_untyped_parameters_different_order_correct_names():
env={
**os.environ,
"_COMPLETION_NO_TYPES_ORDER.PY_COMPLETE": "complete_zsh",
- "_TYPER_COMPLETE_ARGS": "completion_no_types_order.py --name Sebastian --name Ca",
+ "_TYPER_COMPLETE_ARGS": "completion_no_types_order.py --user Sebastian --user Ca",
},
)
assert "info name is: completion_no_types_order.py" in result.stderr
assert "args is: []" in result.stderr
+ assert "param is: user" in result.stderr
assert "incomplete is: Ca" in result.stderr
assert '"Camila":"The reader of books."' in result.stdout
assert '"Carlos":"The writer of scripts."' in result.stdout
@@ -269,7 +271,7 @@ def main(name: str = typer.Option(..., autocompletion=name_callback)):
pass # pragma: no cover
with pytest.raises(click.ClickException) as exc_info:
- runner.invoke(app, ["--name", "Camila"])
+ runner.invoke(app, ["--user", "Camila"])
assert exc_info.value.message == "Invalid autocompletion callback parameters: val2"
diff --git a/tests/test_tutorial/test_options_autocompletion/test_tutorial002.py b/tests/test_tutorial/test_options_autocompletion/test_tutorial002.py
index 3e15fad9b6..b953906965 100644
--- a/tests/test_tutorial/test_options_autocompletion/test_tutorial002.py
+++ b/tests/test_tutorial/test_options_autocompletion/test_tutorial002.py
@@ -17,7 +17,7 @@ def test_completion():
env={
**os.environ,
"_TUTORIAL002.PY_COMPLETE": "complete_zsh",
- "_TYPER_COMPLETE_ARGS": "tutorial002.py --name ",
+ "_TYPER_COMPLETE_ARGS": "tutorial002.py --user ",
},
)
assert "Camila" in result.stdout
@@ -26,7 +26,7 @@ def test_completion():
def test_1():
- result = runner.invoke(mod.app, ["--name", "Camila"])
+ result = runner.invoke(mod.app, ["--user", "Camila"])
assert result.exit_code == 0
assert "Hello Camila" in result.output
diff --git a/tests/test_tutorial/test_options_autocompletion/test_tutorial002_an.py b/tests/test_tutorial/test_options_autocompletion/test_tutorial002_an.py
index 7db64bfa44..93109f6b9f 100644
--- a/tests/test_tutorial/test_options_autocompletion/test_tutorial002_an.py
+++ b/tests/test_tutorial/test_options_autocompletion/test_tutorial002_an.py
@@ -17,7 +17,7 @@ def test_completion():
env={
**os.environ,
"_TUTORIAL002_AN.PY_COMPLETE": "complete_zsh",
- "_TYPER_COMPLETE_ARGS": "tutorial002_an.py --name ",
+ "_TYPER_COMPLETE_ARGS": "tutorial002_an.py --user ",
},
)
assert "Camila" in result.stdout
@@ -26,7 +26,7 @@ def test_completion():
def test_1():
- result = runner.invoke(mod.app, ["--name", "Camila"])
+ result = runner.invoke(mod.app, ["--user", "Camila"])
assert result.exit_code == 0
assert "Hello Camila" in result.output
diff --git a/tests/test_tutorial/test_options_autocompletion/test_tutorial003.py b/tests/test_tutorial/test_options_autocompletion/test_tutorial003.py
index 60304e9e55..cc7ba5d1df 100644
--- a/tests/test_tutorial/test_options_autocompletion/test_tutorial003.py
+++ b/tests/test_tutorial/test_options_autocompletion/test_tutorial003.py
@@ -17,7 +17,7 @@ def test_completion_zsh():
env={
**os.environ,
"_TUTORIAL003.PY_COMPLETE": "complete_zsh",
- "_TYPER_COMPLETE_ARGS": "tutorial003.py --name Seb",
+ "_TYPER_COMPLETE_ARGS": "tutorial003.py --user Seb",
},
)
assert "Camila" not in result.stdout
@@ -33,7 +33,7 @@ def test_completion_powershell():
env={
**os.environ,
"_TUTORIAL003.PY_COMPLETE": "complete_powershell",
- "_TYPER_COMPLETE_ARGS": "tutorial003.py --name Seb",
+ "_TYPER_COMPLETE_ARGS": "tutorial003.py --user Seb",
"_TYPER_COMPLETE_WORD_TO_COMPLETE": "Seb",
},
)
@@ -43,7 +43,7 @@ def test_completion_powershell():
def test_1():
- result = runner.invoke(mod.app, ["--name", "Camila"])
+ result = runner.invoke(mod.app, ["--user", "Camila"])
assert result.exit_code == 0
assert "Hello Camila" in result.output
diff --git a/tests/test_tutorial/test_options_autocompletion/test_tutorial003_an.py b/tests/test_tutorial/test_options_autocompletion/test_tutorial003_an.py
index 7688f108f5..555e00c8d1 100644
--- a/tests/test_tutorial/test_options_autocompletion/test_tutorial003_an.py
+++ b/tests/test_tutorial/test_options_autocompletion/test_tutorial003_an.py
@@ -17,7 +17,7 @@ def test_completion_zsh():
env={
**os.environ,
"_TUTORIAL003_AN.PY_COMPLETE": "complete_zsh",
- "_TYPER_COMPLETE_ARGS": "tutorial003_an.py --name Seb",
+ "_TYPER_COMPLETE_ARGS": "tutorial003_an.py --user Seb",
},
)
assert "Camila" not in result.stdout
@@ -33,7 +33,7 @@ def test_completion_powershell():
env={
**os.environ,
"_TUTORIAL003_AN.PY_COMPLETE": "complete_powershell",
- "_TYPER_COMPLETE_ARGS": "tutorial003.py --name Seb",
+ "_TYPER_COMPLETE_ARGS": "tutorial003.py --user Seb",
"_TYPER_COMPLETE_WORD_TO_COMPLETE": "Seb",
},
)
@@ -43,7 +43,7 @@ def test_completion_powershell():
def test_1():
- result = runner.invoke(mod.app, ["--name", "Camila"])
+ result = runner.invoke(mod.app, ["--user", "Camila"])
assert result.exit_code == 0
assert "Hello Camila" in result.output
diff --git a/tests/test_tutorial/test_options_autocompletion/test_tutorial004.py b/tests/test_tutorial/test_options_autocompletion/test_tutorial004.py
index 17c5f5197a..94a308765f 100644
--- a/tests/test_tutorial/test_options_autocompletion/test_tutorial004.py
+++ b/tests/test_tutorial/test_options_autocompletion/test_tutorial004.py
@@ -17,7 +17,7 @@ def test_completion():
env={
**os.environ,
"_TUTORIAL004.PY_COMPLETE": "complete_zsh",
- "_TYPER_COMPLETE_ARGS": "tutorial004_aux.py --name ",
+ "_TYPER_COMPLETE_ARGS": "tutorial004_aux.py --user ",
},
)
assert '"Camila":"The reader of books."' in result.stdout
@@ -26,7 +26,7 @@ def test_completion():
def test_1():
- result = runner.invoke(mod.app, ["--name", "Camila"])
+ result = runner.invoke(mod.app, ["--user", "Camila"])
assert result.exit_code == 0
assert "Hello Camila" in result.output
diff --git a/tests/test_tutorial/test_options_autocompletion/test_tutorial004_an.py b/tests/test_tutorial/test_options_autocompletion/test_tutorial004_an.py
index dcfce0a4a7..2e3b91dbfa 100644
--- a/tests/test_tutorial/test_options_autocompletion/test_tutorial004_an.py
+++ b/tests/test_tutorial/test_options_autocompletion/test_tutorial004_an.py
@@ -17,7 +17,7 @@ def test_completion():
env={
**os.environ,
"_TUTORIAL004_AN.PY_COMPLETE": "complete_zsh",
- "_TYPER_COMPLETE_ARGS": "tutorial004_an_aux.py --name ",
+ "_TYPER_COMPLETE_ARGS": "tutorial004_an_aux.py --user ",
},
)
assert '"Camila":"The reader of books."' in result.stdout
@@ -26,7 +26,7 @@ def test_completion():
def test_1():
- result = runner.invoke(mod.app, ["--name", "Camila"])
+ result = runner.invoke(mod.app, ["--user", "Camila"])
assert result.exit_code == 0
assert "Hello Camila" in result.output
diff --git a/tests/test_tutorial/test_options_autocompletion/test_tutorial007.py b/tests/test_tutorial/test_options_autocompletion/test_tutorial007.py
index b8ce1b1598..908d904bd9 100644
--- a/tests/test_tutorial/test_options_autocompletion/test_tutorial007.py
+++ b/tests/test_tutorial/test_options_autocompletion/test_tutorial007.py
@@ -17,7 +17,7 @@ def test_completion():
env={
**os.environ,
"_TUTORIAL007.PY_COMPLETE": "complete_zsh",
- "_TYPER_COMPLETE_ARGS": "tutorial007.py --name Sebastian --name ",
+ "_TYPER_COMPLETE_ARGS": "tutorial007.py --user Sebastian --user ",
},
)
assert '"Camila":"The reader of books."' in result.stdout
@@ -26,7 +26,7 @@ def test_completion():
def test_1():
- result = runner.invoke(mod.app, ["--name", "Camila", "--name", "Sebastian"])
+ result = runner.invoke(mod.app, ["--user", "Camila", "--user", "Sebastian"])
assert result.exit_code == 0
assert "Hello Camila" in result.output
assert "Hello Sebastian" in result.output
diff --git a/tests/test_tutorial/test_options_autocompletion/test_tutorial007_an.py b/tests/test_tutorial/test_options_autocompletion/test_tutorial007_an.py
index c35f23eb61..bcfd0e245e 100644
--- a/tests/test_tutorial/test_options_autocompletion/test_tutorial007_an.py
+++ b/tests/test_tutorial/test_options_autocompletion/test_tutorial007_an.py
@@ -17,7 +17,7 @@ def test_completion():
env={
**os.environ,
"_TUTORIAL007_AN.PY_COMPLETE": "complete_zsh",
- "_TYPER_COMPLETE_ARGS": "tutorial007_an.py --name Sebastian --name ",
+ "_TYPER_COMPLETE_ARGS": "tutorial007_an.py --user Sebastian --user ",
},
)
assert '"Camila":"The reader of books."' in result.stdout
@@ -26,7 +26,7 @@ def test_completion():
def test_1():
- result = runner.invoke(mod.app, ["--name", "Camila", "--name", "Sebastian"])
+ result = runner.invoke(mod.app, ["--user", "Camila", "--user", "Sebastian"])
assert result.exit_code == 0
assert "Hello Camila" in result.output
assert "Hello Sebastian" in result.output
diff --git a/tests/test_tutorial/test_options_autocompletion/test_tutorial008.py b/tests/test_tutorial/test_options_autocompletion/test_tutorial008.py
index 0874f23c5d..bf609d76f4 100644
--- a/tests/test_tutorial/test_options_autocompletion/test_tutorial008.py
+++ b/tests/test_tutorial/test_options_autocompletion/test_tutorial008.py
@@ -17,7 +17,7 @@ def test_completion():
env={
**os.environ,
"_TUTORIAL008.PY_COMPLETE": "complete_zsh",
- "_TYPER_COMPLETE_ARGS": "tutorial008.py --name ",
+ "_TYPER_COMPLETE_ARGS": "tutorial008.py --user ",
},
)
assert '"Camila":"The reader of books."' in result.stdout
@@ -27,7 +27,7 @@ def test_completion():
def test_1():
- result = runner.invoke(mod.app, ["--name", "Camila", "--name", "Sebastian"])
+ result = runner.invoke(mod.app, ["--user", "Camila", "--user", "Sebastian"])
assert result.exit_code == 0
assert "Hello Camila" in result.output
assert "Hello Sebastian" in result.output
diff --git a/tests/test_tutorial/test_options_autocompletion/test_tutorial008_an.py b/tests/test_tutorial/test_options_autocompletion/test_tutorial008_an.py
index cb2481a67c..ea26462e8a 100644
--- a/tests/test_tutorial/test_options_autocompletion/test_tutorial008_an.py
+++ b/tests/test_tutorial/test_options_autocompletion/test_tutorial008_an.py
@@ -17,7 +17,7 @@ def test_completion():
env={
**os.environ,
"_TUTORIAL008_AN.PY_COMPLETE": "complete_zsh",
- "_TYPER_COMPLETE_ARGS": "tutorial008_an.py --name ",
+ "_TYPER_COMPLETE_ARGS": "tutorial008_an.py --user ",
},
)
assert '"Camila":"The reader of books."' in result.stdout
@@ -27,7 +27,7 @@ def test_completion():
def test_1():
- result = runner.invoke(mod.app, ["--name", "Camila", "--name", "Sebastian"])
+ result = runner.invoke(mod.app, ["--user", "Camila", "--user", "Sebastian"])
assert result.exit_code == 0
assert "Hello Camila" in result.output
assert "Hello Sebastian" in result.output
diff --git a/tests/test_tutorial/test_options_autocompletion/test_tutorial009.py b/tests/test_tutorial/test_options_autocompletion/test_tutorial009.py
index 3c7eb0cc64..45c9cd9a89 100644
--- a/tests/test_tutorial/test_options_autocompletion/test_tutorial009.py
+++ b/tests/test_tutorial/test_options_autocompletion/test_tutorial009.py
@@ -17,7 +17,7 @@ def test_completion():
env={
**os.environ,
"_TUTORIAL009.PY_COMPLETE": "complete_zsh",
- "_TYPER_COMPLETE_ARGS": "tutorial009.py --name Sebastian --name ",
+ "_TYPER_COMPLETE_ARGS": "tutorial009.py --user Sebastian --user ",
},
)
assert '"Camila":"The reader of books."' in result.stdout
@@ -27,7 +27,7 @@ def test_completion():
def test_1():
- result = runner.invoke(mod.app, ["--name", "Camila", "--name", "Sebastian"])
+ result = runner.invoke(mod.app, ["--user", "Camila", "--user", "Sebastian"])
assert result.exit_code == 0
assert "Hello Camila" in result.output
assert "Hello Sebastian" in result.output
diff --git a/tests/test_tutorial/test_options_autocompletion/test_tutorial009_an.py b/tests/test_tutorial/test_options_autocompletion/test_tutorial009_an.py
index 56182ac3b9..ef76f7953b 100644
--- a/tests/test_tutorial/test_options_autocompletion/test_tutorial009_an.py
+++ b/tests/test_tutorial/test_options_autocompletion/test_tutorial009_an.py
@@ -17,7 +17,7 @@ def test_completion():
env={
**os.environ,
"_TUTORIAL009_AN.PY_COMPLETE": "complete_zsh",
- "_TYPER_COMPLETE_ARGS": "tutorial009_an.py --name Sebastian --name ",
+ "_TYPER_COMPLETE_ARGS": "tutorial009_an.py --user Sebastian --user ",
},
)
assert '"Camila":"The reader of books."' in result.stdout
@@ -27,7 +27,7 @@ def test_completion():
def test_1():
- result = runner.invoke(mod.app, ["--name", "Camila", "--name", "Sebastian"])
+ result = runner.invoke(mod.app, ["--user", "Camila", "--user", "Sebastian"])
assert result.exit_code == 0
assert "Hello Camila" in result.output
assert "Hello Sebastian" in result.output
diff --git a/tests/test_tutorial/test_options_autocompletion/test_tutorial010.py b/tests/test_tutorial/test_options_autocompletion/test_tutorial010.py
new file mode 100644
index 0000000000..8496dd5a07
--- /dev/null
+++ b/tests/test_tutorial/test_options_autocompletion/test_tutorial010.py
@@ -0,0 +1,90 @@
+import os
+import subprocess
+import sys
+
+from typer.testing import CliRunner
+
+from docs_src.options_autocompletion import tutorial010 as mod
+
+runner = CliRunner()
+
+
+def test_completion():
+ result = subprocess.run(
+ [sys.executable, "-m", "coverage", "run", mod.__file__, " "],
+ capture_output=True,
+ encoding="utf-8",
+ env={
+ **os.environ,
+ "_TUTORIAL010.PY_COMPLETE": "complete_zsh",
+ "_TYPER_COMPLETE_ARGS": "tutorial010.py --user Sebastian --user ",
+ },
+ )
+ assert '"Camila":"The reader of books."' in result.stdout
+ assert '"Carlos":"The writer of scripts."' in result.stdout
+ assert '"Sebastian":"The type hints guy."' not in result.stdout
+
+
+def test_completion_greeter1():
+ result = subprocess.run(
+ [sys.executable, "-m", "coverage", "run", mod.__file__, " "],
+ capture_output=True,
+ encoding="utf-8",
+ env={
+ **os.environ,
+ "_TUTORIAL010.PY_COMPLETE": "complete_zsh",
+ "_TYPER_COMPLETE_ARGS": "tutorial010.py --user Sebastian --greeter Ca",
+ },
+ )
+ assert '"Camila":"The reader of books."' in result.stdout
+ assert '"Carlos":"The writer of scripts."' in result.stdout
+ assert '"Sebastian":"The type hints guy."' not in result.stdout
+
+
+def test_completion_greeter2():
+ result = subprocess.run(
+ [sys.executable, "-m", "coverage", "run", mod.__file__, " "],
+ capture_output=True,
+ encoding="utf-8",
+ env={
+ **os.environ,
+ "_TUTORIAL010.PY_COMPLETE": "complete_zsh",
+ "_TYPER_COMPLETE_ARGS": "tutorial010.py --user Sebastian --greeter Carlos --greeter ",
+ },
+ )
+ assert '"Camila":"The reader of books."' in result.stdout
+ assert '"Carlos":"The writer of scripts."' not in result.stdout
+ assert '"Sebastian":"The type hints guy."' in result.stdout
+
+
+def test_1():
+ result = runner.invoke(mod.app, ["--user", "Camila", "--user", "Sebastian"])
+ assert result.exit_code == 0
+ assert "Hello Camila" in result.output
+ assert "Hello Sebastian" in result.output
+
+
+def test_2():
+ result = runner.invoke(
+ mod.app, ["--user", "Camila", "--user", "Sebastian", "--greeter", "Carlos"]
+ )
+ assert result.exit_code == 0
+ assert "Hello Camila, from Carlos" in result.output
+ assert "Hello Sebastian, from Carlos" in result.output
+
+
+def test_3():
+ result = runner.invoke(
+ mod.app, ["--user", "Camila", "--greeter", "Carlos", "--greeter", "Sebastian"]
+ )
+ assert result.exit_code == 0
+ assert "Hello Camila, from Carlos and Sebastian" 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
diff --git a/tests/test_tutorial/test_options_autocompletion/test_tutorial010_an.py b/tests/test_tutorial/test_options_autocompletion/test_tutorial010_an.py
new file mode 100644
index 0000000000..de65f478b2
--- /dev/null
+++ b/tests/test_tutorial/test_options_autocompletion/test_tutorial010_an.py
@@ -0,0 +1,90 @@
+import os
+import subprocess
+import sys
+
+from typer.testing import CliRunner
+
+from docs_src.options_autocompletion import tutorial010_an as mod
+
+runner = CliRunner()
+
+
+def test_completion():
+ result = subprocess.run(
+ [sys.executable, "-m", "coverage", "run", mod.__file__, " "],
+ capture_output=True,
+ encoding="utf-8",
+ env={
+ **os.environ,
+ "_TUTORIAL010_AN.PY_COMPLETE": "complete_zsh",
+ "_TYPER_COMPLETE_ARGS": "tutorial010_an.py --user Sebastian --user ",
+ },
+ )
+ assert '"Camila":"The reader of books."' in result.stdout
+ assert '"Carlos":"The writer of scripts."' in result.stdout
+ assert '"Sebastian":"The type hints guy."' not in result.stdout
+
+
+def test_completion_greeter1():
+ result = subprocess.run(
+ [sys.executable, "-m", "coverage", "run", mod.__file__, " "],
+ capture_output=True,
+ encoding="utf-8",
+ env={
+ **os.environ,
+ "_TUTORIAL010_AN.PY_COMPLETE": "complete_zsh",
+ "_TYPER_COMPLETE_ARGS": "tutorial010_an.py --user Sebastian --greeter Ca",
+ },
+ )
+ assert '"Camila":"The reader of books."' in result.stdout
+ assert '"Carlos":"The writer of scripts."' in result.stdout
+ assert '"Sebastian":"The type hints guy."' not in result.stdout
+
+
+def test_completion_greeter2():
+ result = subprocess.run(
+ [sys.executable, "-m", "coverage", "run", mod.__file__, " "],
+ capture_output=True,
+ encoding="utf-8",
+ env={
+ **os.environ,
+ "_TUTORIAL010_AN.PY_COMPLETE": "complete_zsh",
+ "_TYPER_COMPLETE_ARGS": "tutorial010_an.py --user Sebastian --greeter Carlos --greeter ",
+ },
+ )
+ assert '"Camila":"The reader of books."' in result.stdout
+ assert '"Carlos":"The writer of scripts."' not in result.stdout
+ assert '"Sebastian":"The type hints guy."' in result.stdout
+
+
+def test_1():
+ result = runner.invoke(mod.app, ["--user", "Camila", "--user", "Sebastian"])
+ assert result.exit_code == 0
+ assert "Hello Camila" in result.output
+ assert "Hello Sebastian" in result.output
+
+
+def test_2():
+ result = runner.invoke(
+ mod.app, ["--user", "Camila", "--user", "Sebastian", "--greeter", "Carlos"]
+ )
+ assert result.exit_code == 0
+ assert "Hello Camila, from Carlos" in result.output
+ assert "Hello Sebastian, from Carlos" in result.output
+
+
+def test_3():
+ result = runner.invoke(
+ mod.app, ["--user", "Camila", "--greeter", "Carlos", "--greeter", "Sebastian"]
+ )
+ assert result.exit_code == 0
+ assert "Hello Camila, from Carlos and Sebastian" 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
diff --git a/typer/core.py b/typer/core.py
index e9631e56cf..4f2619e495 100644
--- a/typer/core.py
+++ b/typer/core.py
@@ -53,7 +53,10 @@ def _typer_param_setup_autocompletion_compat(
self: click.Parameter,
*,
autocompletion: Optional[
- Callable[[click.Context, List[str], str], List[Union[Tuple[str, str], str]]]
+ Callable[
+ [click.Context, List[str], click.core.Parameter, str],
+ List[Union[Tuple[str, str], str, "click.shell_completion.CompletionItem"]],
+ ]
] = None,
) -> None:
if self._custom_shell_complete is not None:
@@ -75,9 +78,11 @@ def compat_autocompletion(
out = []
- for c in autocompletion(ctx, [], incomplete):
+ for c in autocompletion(ctx, [], param, incomplete):
if isinstance(c, tuple):
use_completion = CompletionItem(c[0], help=c[1])
+ elif isinstance(c, CompletionItem):
+ use_completion = c
else:
assert isinstance(c, str)
use_completion = CompletionItem(c)
diff --git a/typer/main.py b/typer/main.py
index 71a25e6c4b..20698639e8 100644
--- a/typer/main.py
+++ b/typer/main.py
@@ -1026,6 +1026,7 @@ def get_param_completion(
parameters = get_params_from_function(callback)
ctx_name = None
args_name = None
+ param_name = None
incomplete_name = None
unassigned_params = list(parameters.values())
for param_sig in unassigned_params[:]:
@@ -1036,6 +1037,9 @@ def get_param_completion(
elif lenient_issubclass(origin, List):
args_name = param_sig.name
unassigned_params.remove(param_sig)
+ elif lenient_issubclass(param_sig.annotation, click.Parameter):
+ param_name = param_sig.name
+ unassigned_params.remove(param_sig)
elif lenient_issubclass(param_sig.annotation, str):
incomplete_name = param_sig.name
unassigned_params.remove(param_sig)
@@ -1047,6 +1051,9 @@ def get_param_completion(
elif args_name is None and param_sig.name == "args":
args_name = param_sig.name
unassigned_params.remove(param_sig)
+ elif param_name is None and param_sig.name == "param":
+ param_name = param_sig.name
+ unassigned_params.remove(param_sig)
elif incomplete_name is None and param_sig.name == "incomplete":
incomplete_name = param_sig.name
unassigned_params.remove(param_sig)
@@ -1057,12 +1064,19 @@ def get_param_completion(
f"Invalid autocompletion callback parameters: {show_params}"
)
- def wrapper(ctx: click.Context, args: List[str], incomplete: Optional[str]) -> Any:
+ def wrapper(
+ ctx: click.Context,
+ args: List[str],
+ param: click.core.Parameter,
+ incomplete: Optional[str],
+ ) -> Any:
use_params: Dict[str, Any] = {}
if ctx_name:
use_params[ctx_name] = ctx
if args_name:
use_params[args_name] = args
+ if param_name:
+ use_params[param_name] = param
if incomplete_name:
use_params[incomplete_name] = incomplete
return callback(**use_params)