Skip to content

Commit ebb968e

Browse files
snapbugMatt Cranesvlandeg
authored
🐛 Allow colon in zsh autocomplete values and descriptions (#988)
Co-authored-by: Matt Crane <[email protected]> Co-authored-by: svlandeg <[email protected]>
1 parent 438ae44 commit ebb968e

File tree

3 files changed

+245
-0
lines changed

3 files changed

+245
-0
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import typer
2+
3+
image_desc = [
4+
("alpine:latest", "latest alpine image"),
5+
("alpine:hello", "fake image: for testing"),
6+
("nvidia/cuda:10.0-devel-ubuntu18.04", ""),
7+
]
8+
9+
10+
def _complete(incomplete: str) -> str:
11+
for image, desc in image_desc:
12+
if image.startswith(incomplete):
13+
yield image, desc
14+
15+
16+
app = typer.Typer()
17+
18+
19+
@app.command()
20+
def image(name: str = typer.Option(autocompletion=_complete)):
21+
typer.echo(name)
22+
23+
24+
if __name__ == "__main__":
25+
app()
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
import os
2+
import subprocess
3+
import sys
4+
5+
from . import colon_example as mod
6+
7+
8+
def test_script():
9+
result = subprocess.run(
10+
[sys.executable, "-m", "coverage", "run", mod.__file__, "--name", "DeadPool"],
11+
capture_output=True,
12+
encoding="utf-8",
13+
)
14+
assert result.returncode == 0
15+
assert "DeadPool" in result.stdout
16+
17+
18+
def test_completion_colon_bash_all():
19+
result = subprocess.run(
20+
[sys.executable, "-m", "coverage", "run", mod.__file__, " "],
21+
capture_output=True,
22+
encoding="utf-8",
23+
env={
24+
**os.environ,
25+
"_COLON_EXAMPLE.PY_COMPLETE": "complete_bash",
26+
"COMP_WORDS": "colon_example.py --name ",
27+
"COMP_CWORD": "2",
28+
},
29+
)
30+
assert "alpine:hello" in result.stdout
31+
assert "alpine:latest" in result.stdout
32+
assert "nvidia/cuda:10.0-devel-ubuntu18.04" in result.stdout
33+
34+
35+
def test_completion_colon_bash_partial():
36+
result = subprocess.run(
37+
[sys.executable, "-m", "coverage", "run", mod.__file__, " "],
38+
capture_output=True,
39+
encoding="utf-8",
40+
env={
41+
**os.environ,
42+
"_COLON_EXAMPLE.PY_COMPLETE": "complete_bash",
43+
"COMP_WORDS": "colon_example.py --name alpine ",
44+
"COMP_CWORD": "2",
45+
},
46+
)
47+
assert "alpine:hello" in result.stdout
48+
assert "alpine:latest" in result.stdout
49+
assert "nvidia/cuda:10.0-devel-ubuntu18.04" not in result.stdout
50+
51+
52+
def test_completion_colon_bash_single():
53+
result = subprocess.run(
54+
[sys.executable, "-m", "coverage", "run", mod.__file__, " "],
55+
capture_output=True,
56+
encoding="utf-8",
57+
env={
58+
**os.environ,
59+
"_COLON_EXAMPLE.PY_COMPLETE": "complete_bash",
60+
"COMP_WORDS": "colon_example.py --name alpine:hell ",
61+
"COMP_CWORD": "2",
62+
},
63+
)
64+
assert "alpine:hello" in result.stdout
65+
assert "alpine:latest" not in result.stdout
66+
assert "nvidia/cuda:10.0-devel-ubuntu18.04" not in result.stdout
67+
68+
69+
def test_completion_colon_zsh_all():
70+
result = subprocess.run(
71+
[sys.executable, "-m", "coverage", "run", mod.__file__, " "],
72+
capture_output=True,
73+
encoding="utf-8",
74+
env={
75+
**os.environ,
76+
"_COLON_EXAMPLE.PY_COMPLETE": "complete_zsh",
77+
"_TYPER_COMPLETE_ARGS": "colon_example.py --name ",
78+
},
79+
)
80+
assert "alpine\\\\:hello" in result.stdout
81+
assert "alpine\\\\:latest" in result.stdout
82+
assert "nvidia/cuda\\\\:10.0-devel-ubuntu18.04" in result.stdout
83+
84+
85+
def test_completion_colon_zsh_partial():
86+
result = subprocess.run(
87+
[sys.executable, "-m", "coverage", "run", mod.__file__, " "],
88+
capture_output=True,
89+
encoding="utf-8",
90+
env={
91+
**os.environ,
92+
"_COLON_EXAMPLE.PY_COMPLETE": "complete_zsh",
93+
"_TYPER_COMPLETE_ARGS": "colon_example.py --name alpine",
94+
},
95+
)
96+
assert "alpine\\\\:hello" in result.stdout
97+
assert "alpine\\\\:latest" in result.stdout
98+
assert "nvidia/cuda\\\\:10.0-devel-ubuntu18.04" not in result.stdout
99+
100+
101+
def test_completion_colon_zsh_single():
102+
result = subprocess.run(
103+
[sys.executable, "-m", "coverage", "run", mod.__file__, " "],
104+
capture_output=True,
105+
encoding="utf-8",
106+
env={
107+
**os.environ,
108+
"_COLON_EXAMPLE.PY_COMPLETE": "complete_zsh",
109+
"_TYPER_COMPLETE_ARGS": "colon_example.py --name alpine:hell",
110+
},
111+
)
112+
assert "alpine\\\\:hello" in result.stdout
113+
assert "alpine\\\\:latest" not in result.stdout
114+
assert "nvidia/cuda\\\\:10.0-devel-ubuntu18.04" not in result.stdout
115+
116+
117+
def test_completion_colon_powershell_all():
118+
result = subprocess.run(
119+
[sys.executable, "-m", "coverage", "run", mod.__file__, " "],
120+
capture_output=True,
121+
encoding="utf-8",
122+
env={
123+
**os.environ,
124+
"_COLON_EXAMPLE.PY_COMPLETE": "complete_powershell",
125+
"_TYPER_COMPLETE_ARGS": "colon_example.py --name ",
126+
"_TYPER_COMPLETE_WORD_TO_COMPLETE": "",
127+
},
128+
)
129+
assert "alpine:hello" in result.stdout
130+
assert "alpine:latest" in result.stdout
131+
assert "nvidia/cuda:10.0-devel-ubuntu18.04" in result.stdout
132+
133+
134+
def test_completion_colon_powershell_partial():
135+
result = subprocess.run(
136+
[sys.executable, "-m", "coverage", "run", mod.__file__, " "],
137+
capture_output=True,
138+
encoding="utf-8",
139+
env={
140+
**os.environ,
141+
"_COLON_EXAMPLE.PY_COMPLETE": "complete_powershell",
142+
"_TYPER_COMPLETE_ARGS": "colon_example.py --name alpine",
143+
"_TYPER_COMPLETE_WORD_TO_COMPLETE": "alpine",
144+
},
145+
)
146+
assert "alpine:hello" in result.stdout
147+
assert "alpine:latest" in result.stdout
148+
assert "nvidia/cuda:10.0-devel-ubuntu18.04" not in result.stdout
149+
150+
151+
def test_completion_colon_powershell_single():
152+
result = subprocess.run(
153+
[sys.executable, "-m", "coverage", "run", mod.__file__, " "],
154+
capture_output=True,
155+
encoding="utf-8",
156+
env={
157+
**os.environ,
158+
"_COLON_EXAMPLE.PY_COMPLETE": "complete_powershell",
159+
"_TYPER_COMPLETE_ARGS": "colon_example.py --name alpine:hell",
160+
"_TYPER_COMPLETE_WORD_TO_COMPLETE": "alpine:hell",
161+
},
162+
)
163+
assert "alpine:hello" in result.stdout
164+
assert "alpine:latest" not in result.stdout
165+
assert "nvidia/cuda:10.0-devel-ubuntu18.04" not in result.stdout
166+
167+
168+
def test_completion_colon_pwsh_all():
169+
result = subprocess.run(
170+
[sys.executable, "-m", "coverage", "run", mod.__file__, " "],
171+
capture_output=True,
172+
encoding="utf-8",
173+
env={
174+
**os.environ,
175+
"_COLON_EXAMPLE.PY_COMPLETE": "complete_pwsh",
176+
"_TYPER_COMPLETE_ARGS": "colon_example.py --name",
177+
},
178+
)
179+
180+
assert "alpine:hello" in result.stdout
181+
assert "alpine:latest" in result.stdout
182+
assert "nvidia/cuda:10.0-devel-ubuntu18.04" in result.stdout
183+
184+
185+
def test_completion_colon_pwsh_partial():
186+
result = subprocess.run(
187+
[sys.executable, "-m", "coverage", "run", mod.__file__, " "],
188+
capture_output=True,
189+
encoding="utf-8",
190+
env={
191+
**os.environ,
192+
"_COLON_EXAMPLE.PY_COMPLETE": "complete_pwsh",
193+
"_TYPER_COMPLETE_ARGS": "colon_example.py --name alpine",
194+
"_TYPER_COMPLETE_WORD_TO_COMPLETE": "alpine",
195+
},
196+
)
197+
assert "alpine:hello" in result.stdout
198+
assert "alpine:latest" in result.stdout
199+
assert "nvidia/cuda:10.0-devel-ubuntu18.04" not in result.stdout
200+
201+
202+
def test_completion_colon_pwsh_single():
203+
result = subprocess.run(
204+
[sys.executable, "-m", "coverage", "run", mod.__file__, " "],
205+
capture_output=True,
206+
encoding="utf-8",
207+
env={
208+
**os.environ,
209+
"_COLON_EXAMPLE.PY_COMPLETE": "complete_pwsh",
210+
"_TYPER_COMPLETE_ARGS": "colon_example.py --name alpine:hell",
211+
"_TYPER_COMPLETE_WORD_TO_COMPLETE": "alpine:hell",
212+
},
213+
)
214+
assert "alpine:hello" in result.stdout
215+
assert "alpine:latest" not in result.stdout
216+
assert "nvidia/cuda:10.0-devel-ubuntu18.04" not in result.stdout
217+
218+
219+
# TODO: tests for complete_fish

typer/_completion_classes.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ def escape(s: str) -> str:
8686
.replace("'", "''")
8787
.replace("$", "\\$")
8888
.replace("`", "\\`")
89+
.replace(":", r"\\:")
8990
)
9091

9192
# TODO: Explore replicating the new behavior from Click, pay attention to

0 commit comments

Comments
 (0)