test_bashcomplete.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. # -*- coding: utf-8 -*-
  2. import pytest
  3. import click
  4. from click._bashcomplete import get_choices
  5. def choices_without_help(cli, args, incomplete):
  6. completions = get_choices(cli, "dummy", args, incomplete)
  7. return [c[0] for c in completions]
  8. def choices_with_help(cli, args, incomplete):
  9. return list(get_choices(cli, "dummy", args, incomplete))
  10. def test_single_command():
  11. @click.command()
  12. @click.option("--local-opt")
  13. def cli(local_opt):
  14. pass
  15. assert choices_without_help(cli, [], "-") == ["--local-opt"]
  16. assert choices_without_help(cli, [], "") == []
  17. def test_boolean_flag():
  18. @click.command()
  19. @click.option("--shout/--no-shout", default=False)
  20. def cli(local_opt):
  21. pass
  22. assert choices_without_help(cli, [], "-") == ["--shout", "--no-shout"]
  23. def test_multi_value_option():
  24. @click.group()
  25. @click.option("--pos", nargs=2, type=float)
  26. def cli(local_opt):
  27. pass
  28. @cli.command()
  29. @click.option("--local-opt")
  30. def sub(local_opt):
  31. pass
  32. assert choices_without_help(cli, [], "-") == ["--pos"]
  33. assert choices_without_help(cli, ["--pos"], "") == []
  34. assert choices_without_help(cli, ["--pos", "1.0"], "") == []
  35. assert choices_without_help(cli, ["--pos", "1.0", "1.0"], "") == ["sub"]
  36. def test_multi_option():
  37. @click.command()
  38. @click.option("--message", "-m", multiple=True)
  39. def cli(local_opt):
  40. pass
  41. assert choices_without_help(cli, [], "-") == ["--message", "-m"]
  42. assert choices_without_help(cli, ["-m"], "") == []
  43. def test_small_chain():
  44. @click.group()
  45. @click.option("--global-opt")
  46. def cli(global_opt):
  47. pass
  48. @cli.command()
  49. @click.option("--local-opt")
  50. def sub(local_opt):
  51. pass
  52. assert choices_without_help(cli, [], "") == ["sub"]
  53. assert choices_without_help(cli, [], "-") == ["--global-opt"]
  54. assert choices_without_help(cli, ["sub"], "") == []
  55. assert choices_without_help(cli, ["sub"], "-") == ["--local-opt"]
  56. def test_long_chain():
  57. @click.group("cli")
  58. @click.option("--cli-opt")
  59. def cli(cli_opt):
  60. pass
  61. @cli.group("asub")
  62. @click.option("--asub-opt")
  63. def asub(asub_opt):
  64. pass
  65. @asub.group("bsub")
  66. @click.option("--bsub-opt")
  67. def bsub(bsub_opt):
  68. pass
  69. COLORS = ["red", "green", "blue"]
  70. def get_colors(ctx, args, incomplete):
  71. for c in COLORS:
  72. if c.startswith(incomplete):
  73. yield c
  74. def search_colors(ctx, args, incomplete):
  75. for c in COLORS:
  76. if incomplete in c:
  77. yield c
  78. CSUB_OPT_CHOICES = ["foo", "bar"]
  79. CSUB_CHOICES = ["bar", "baz"]
  80. @bsub.command("csub")
  81. @click.option("--csub-opt", type=click.Choice(CSUB_OPT_CHOICES))
  82. @click.option("--csub", type=click.Choice(CSUB_CHOICES))
  83. @click.option("--search-color", autocompletion=search_colors)
  84. @click.argument("color", autocompletion=get_colors)
  85. def csub(csub_opt, color):
  86. pass
  87. assert choices_without_help(cli, [], "-") == ["--cli-opt"]
  88. assert choices_without_help(cli, [], "") == ["asub"]
  89. assert choices_without_help(cli, ["asub"], "-") == ["--asub-opt"]
  90. assert choices_without_help(cli, ["asub"], "") == ["bsub"]
  91. assert choices_without_help(cli, ["asub", "bsub"], "-") == ["--bsub-opt"]
  92. assert choices_without_help(cli, ["asub", "bsub"], "") == ["csub"]
  93. assert choices_without_help(cli, ["asub", "bsub", "csub"], "-") == [
  94. "--csub-opt",
  95. "--csub",
  96. "--search-color",
  97. ]
  98. assert (
  99. choices_without_help(cli, ["asub", "bsub", "csub", "--csub-opt"], "")
  100. == CSUB_OPT_CHOICES
  101. )
  102. assert choices_without_help(cli, ["asub", "bsub", "csub"], "--csub") == [
  103. "--csub-opt",
  104. "--csub",
  105. ]
  106. assert (
  107. choices_without_help(cli, ["asub", "bsub", "csub", "--csub"], "")
  108. == CSUB_CHOICES
  109. )
  110. assert choices_without_help(cli, ["asub", "bsub", "csub", "--csub-opt"], "f") == [
  111. "foo"
  112. ]
  113. assert choices_without_help(cli, ["asub", "bsub", "csub"], "") == COLORS
  114. assert choices_without_help(cli, ["asub", "bsub", "csub"], "b") == ["blue"]
  115. assert choices_without_help(
  116. cli, ["asub", "bsub", "csub", "--search-color"], "een"
  117. ) == ["green"]
  118. def test_chaining():
  119. @click.group("cli", chain=True)
  120. @click.option("--cli-opt")
  121. @click.argument("arg", type=click.Choice(["cliarg1", "cliarg2"]))
  122. def cli(cli_opt, arg):
  123. pass
  124. @cli.command()
  125. @click.option("--asub-opt")
  126. def asub(asub_opt):
  127. pass
  128. @cli.command(help="bsub help")
  129. @click.option("--bsub-opt")
  130. @click.argument("arg", type=click.Choice(["arg1", "arg2"]))
  131. def bsub(bsub_opt, arg):
  132. pass
  133. @cli.command()
  134. @click.option("--csub-opt")
  135. @click.argument("arg", type=click.Choice(["carg1", "carg2"]), default="carg1")
  136. def csub(csub_opt, arg):
  137. pass
  138. assert choices_without_help(cli, [], "-") == ["--cli-opt"]
  139. assert choices_without_help(cli, [], "") == ["cliarg1", "cliarg2"]
  140. assert choices_without_help(cli, ["cliarg1", "asub"], "-") == ["--asub-opt"]
  141. assert choices_without_help(cli, ["cliarg1", "asub"], "") == ["bsub", "csub"]
  142. assert choices_without_help(cli, ["cliarg1", "bsub"], "") == ["arg1", "arg2"]
  143. assert choices_without_help(cli, ["cliarg1", "asub", "--asub-opt"], "") == []
  144. assert choices_without_help(
  145. cli, ["cliarg1", "asub", "--asub-opt", "5", "bsub"], "-"
  146. ) == ["--bsub-opt"]
  147. assert choices_without_help(cli, ["cliarg1", "asub", "bsub"], "-") == ["--bsub-opt"]
  148. assert choices_without_help(cli, ["cliarg1", "asub", "csub"], "") == [
  149. "carg1",
  150. "carg2",
  151. ]
  152. assert choices_without_help(cli, ["cliarg1", "bsub", "arg1", "csub"], "") == [
  153. "carg1",
  154. "carg2",
  155. ]
  156. assert choices_without_help(cli, ["cliarg1", "asub", "csub"], "-") == ["--csub-opt"]
  157. assert choices_with_help(cli, ["cliarg1", "asub"], "b") == [("bsub", "bsub help")]
  158. def test_argument_choice():
  159. @click.command()
  160. @click.argument("arg1", required=True, type=click.Choice(["arg11", "arg12"]))
  161. @click.argument("arg2", type=click.Choice(["arg21", "arg22"]), default="arg21")
  162. @click.argument("arg3", type=click.Choice(["arg", "argument"]), default="arg")
  163. def cli():
  164. pass
  165. assert choices_without_help(cli, [], "") == ["arg11", "arg12"]
  166. assert choices_without_help(cli, [], "arg") == ["arg11", "arg12"]
  167. assert choices_without_help(cli, ["arg11"], "") == ["arg21", "arg22"]
  168. assert choices_without_help(cli, ["arg12", "arg21"], "") == ["arg", "argument"]
  169. assert choices_without_help(cli, ["arg12", "arg21"], "argu") == ["argument"]
  170. def test_option_choice():
  171. @click.command()
  172. @click.option("--opt1", type=click.Choice(["opt11", "opt12"]), help="opt1 help")
  173. @click.option("--opt2", type=click.Choice(["opt21", "opt22"]), default="opt21")
  174. @click.option("--opt3", type=click.Choice(["opt", "option"]))
  175. def cli():
  176. pass
  177. assert choices_with_help(cli, [], "-") == [
  178. ("--opt1", "opt1 help"),
  179. ("--opt2", None),
  180. ("--opt3", None),
  181. ]
  182. assert choices_without_help(cli, [], "--opt") == ["--opt1", "--opt2", "--opt3"]
  183. assert choices_without_help(cli, [], "--opt1=") == ["opt11", "opt12"]
  184. assert choices_without_help(cli, [], "--opt2=") == ["opt21", "opt22"]
  185. assert choices_without_help(cli, ["--opt2"], "=") == ["opt21", "opt22"]
  186. assert choices_without_help(cli, ["--opt2", "="], "opt") == ["opt21", "opt22"]
  187. assert choices_without_help(cli, ["--opt1"], "") == ["opt11", "opt12"]
  188. assert choices_without_help(cli, ["--opt2"], "") == ["opt21", "opt22"]
  189. assert choices_without_help(cli, ["--opt1", "opt11", "--opt2"], "") == [
  190. "opt21",
  191. "opt22",
  192. ]
  193. assert choices_without_help(cli, ["--opt2", "opt21"], "-") == ["--opt1", "--opt3"]
  194. assert choices_without_help(cli, ["--opt1", "opt11"], "-") == ["--opt2", "--opt3"]
  195. assert choices_without_help(cli, ["--opt1"], "opt") == ["opt11", "opt12"]
  196. assert choices_without_help(cli, ["--opt3"], "opti") == ["option"]
  197. assert choices_without_help(cli, ["--opt1", "invalid_opt"], "-") == [
  198. "--opt2",
  199. "--opt3",
  200. ]
  201. def test_option_and_arg_choice():
  202. @click.command()
  203. @click.option("--opt1", type=click.Choice(["opt11", "opt12"]))
  204. @click.argument("arg1", required=False, type=click.Choice(["arg11", "arg12"]))
  205. @click.option("--opt2", type=click.Choice(["opt21", "opt22"]))
  206. def cli():
  207. pass
  208. assert choices_without_help(cli, ["--opt1"], "") == ["opt11", "opt12"]
  209. assert choices_without_help(cli, [""], "--opt1=") == ["opt11", "opt12"]
  210. assert choices_without_help(cli, [], "") == ["arg11", "arg12"]
  211. assert choices_without_help(cli, ["--opt2"], "") == ["opt21", "opt22"]
  212. assert choices_without_help(cli, ["arg11"], "--opt") == ["--opt1", "--opt2"]
  213. assert choices_without_help(cli, [], "--opt") == ["--opt1", "--opt2"]
  214. def test_boolean_flag_choice():
  215. @click.command()
  216. @click.option("--shout/--no-shout", default=False)
  217. @click.argument("arg", required=False, type=click.Choice(["arg1", "arg2"]))
  218. def cli(local_opt):
  219. pass
  220. assert choices_without_help(cli, [], "-") == ["--shout", "--no-shout"]
  221. assert choices_without_help(cli, ["--shout"], "") == ["arg1", "arg2"]
  222. def test_multi_value_option_choice():
  223. @click.command()
  224. @click.option("--pos", nargs=2, type=click.Choice(["pos1", "pos2"]))
  225. @click.argument("arg", required=False, type=click.Choice(["arg1", "arg2"]))
  226. def cli(local_opt):
  227. pass
  228. assert choices_without_help(cli, ["--pos"], "") == ["pos1", "pos2"]
  229. assert choices_without_help(cli, ["--pos", "pos1"], "") == ["pos1", "pos2"]
  230. assert choices_without_help(cli, ["--pos", "pos1", "pos2"], "") == ["arg1", "arg2"]
  231. assert choices_without_help(cli, ["--pos", "pos1", "pos2", "arg1"], "") == []
  232. def test_multi_option_choice():
  233. @click.command()
  234. @click.option("--message", "-m", multiple=True, type=click.Choice(["m1", "m2"]))
  235. @click.argument("arg", required=False, type=click.Choice(["arg1", "arg2"]))
  236. def cli(local_opt):
  237. pass
  238. assert choices_without_help(cli, ["-m"], "") == ["m1", "m2"]
  239. assert choices_without_help(cli, ["-m", "m1", "-m"], "") == ["m1", "m2"]
  240. assert choices_without_help(cli, ["-m", "m1"], "") == ["arg1", "arg2"]
  241. def test_variadic_argument_choice():
  242. @click.command()
  243. @click.option("--opt", type=click.Choice(["opt1", "opt2"]))
  244. @click.argument("src", nargs=-1, type=click.Choice(["src1", "src2"]))
  245. def cli(local_opt):
  246. pass
  247. assert choices_without_help(cli, ["src1", "src2"], "") == ["src1", "src2"]
  248. assert choices_without_help(cli, ["src1", "src2"], "--o") == ["--opt"]
  249. assert choices_without_help(cli, ["src1", "src2", "--opt"], "") == ["opt1", "opt2"]
  250. assert choices_without_help(cli, ["src1", "src2"], "") == ["src1", "src2"]
  251. def test_variadic_argument_complete():
  252. def _complete(ctx, args, incomplete):
  253. return ["abc", "def", "ghi", "jkl", "mno", "pqr", "stu", "vwx", "yz"]
  254. @click.group()
  255. def entrypoint():
  256. pass
  257. @click.command()
  258. @click.option("--opt", autocompletion=_complete)
  259. @click.argument("arg", nargs=-1)
  260. def subcommand(opt, arg):
  261. pass
  262. entrypoint.add_command(subcommand)
  263. assert choices_without_help(entrypoint, ["subcommand", "--opt"], "") == _complete(
  264. 0, 0, 0
  265. )
  266. assert choices_without_help(
  267. entrypoint, ["subcommand", "whatever", "--opt"], ""
  268. ) == _complete(0, 0, 0)
  269. assert (
  270. choices_without_help(entrypoint, ["subcommand", "whatever", "--opt", "abc"], "")
  271. == []
  272. )
  273. def test_long_chain_choice():
  274. @click.group()
  275. def cli():
  276. pass
  277. @cli.group()
  278. @click.option("--sub-opt", type=click.Choice(["subopt1", "subopt2"]))
  279. @click.argument(
  280. "sub-arg", required=False, type=click.Choice(["subarg1", "subarg2"])
  281. )
  282. def sub(sub_opt, sub_arg):
  283. pass
  284. @sub.command(short_help="bsub help")
  285. @click.option("--bsub-opt", type=click.Choice(["bsubopt1", "bsubopt2"]))
  286. @click.argument(
  287. "bsub-arg1", required=False, type=click.Choice(["bsubarg1", "bsubarg2"])
  288. )
  289. @click.argument(
  290. "bbsub-arg2", required=False, type=click.Choice(["bbsubarg1", "bbsubarg2"])
  291. )
  292. def bsub(bsub_opt):
  293. pass
  294. @sub.group("csub")
  295. def csub():
  296. pass
  297. @csub.command()
  298. def dsub():
  299. pass
  300. assert choices_with_help(cli, ["sub", "subarg1"], "") == [
  301. ("bsub", "bsub help"),
  302. ("csub", ""),
  303. ]
  304. assert choices_without_help(cli, ["sub"], "") == ["subarg1", "subarg2"]
  305. assert choices_without_help(cli, ["sub", "--sub-opt"], "") == ["subopt1", "subopt2"]
  306. assert choices_without_help(cli, ["sub", "--sub-opt", "subopt1"], "") == [
  307. "subarg1",
  308. "subarg2",
  309. ]
  310. assert choices_without_help(
  311. cli, ["sub", "--sub-opt", "subopt1", "subarg1", "bsub"], "-"
  312. ) == ["--bsub-opt"]
  313. assert choices_without_help(
  314. cli, ["sub", "--sub-opt", "subopt1", "subarg1", "bsub"], ""
  315. ) == ["bsubarg1", "bsubarg2"]
  316. assert choices_without_help(
  317. cli, ["sub", "--sub-opt", "subopt1", "subarg1", "bsub", "--bsub-opt"], ""
  318. ) == ["bsubopt1", "bsubopt2"]
  319. assert choices_without_help(
  320. cli,
  321. [
  322. "sub",
  323. "--sub-opt",
  324. "subopt1",
  325. "subarg1",
  326. "bsub",
  327. "--bsub-opt",
  328. "bsubopt1",
  329. "bsubarg1",
  330. ],
  331. "",
  332. ) == ["bbsubarg1", "bbsubarg2"]
  333. assert choices_without_help(
  334. cli, ["sub", "--sub-opt", "subopt1", "subarg1", "csub"], ""
  335. ) == ["dsub"]
  336. def test_chained_multi():
  337. @click.group()
  338. def cli():
  339. pass
  340. @cli.group()
  341. def sub():
  342. pass
  343. @sub.group()
  344. def bsub():
  345. pass
  346. @sub.group(chain=True)
  347. def csub():
  348. pass
  349. @csub.command()
  350. def dsub():
  351. pass
  352. @csub.command()
  353. def esub():
  354. pass
  355. assert choices_without_help(cli, ["sub"], "") == ["bsub", "csub"]
  356. assert choices_without_help(cli, ["sub"], "c") == ["csub"]
  357. assert choices_without_help(cli, ["sub", "csub"], "") == ["dsub", "esub"]
  358. assert choices_without_help(cli, ["sub", "csub", "dsub"], "") == ["esub"]
  359. def test_hidden():
  360. @click.group()
  361. @click.option("--name", hidden=True)
  362. @click.option("--choices", type=click.Choice([1, 2]), hidden=True)
  363. def cli(name):
  364. pass
  365. @cli.group(hidden=True)
  366. def hgroup():
  367. pass
  368. @hgroup.group()
  369. def hgroupsub():
  370. pass
  371. @cli.command()
  372. def asub():
  373. pass
  374. @cli.command(hidden=True)
  375. @click.option("--hname")
  376. def hsub():
  377. pass
  378. assert choices_without_help(cli, [], "--n") == []
  379. assert choices_without_help(cli, [], "--c") == []
  380. # If the user exactly types out the hidden param, complete its options.
  381. assert choices_without_help(cli, ["--choices"], "") == [1, 2]
  382. assert choices_without_help(cli, [], "") == ["asub"]
  383. assert choices_without_help(cli, [], "") == ["asub"]
  384. assert choices_without_help(cli, [], "h") == []
  385. # If the user exactly types out the hidden command, complete its subcommands.
  386. assert choices_without_help(cli, ["hgroup"], "") == ["hgroupsub"]
  387. assert choices_without_help(cli, ["hsub"], "--h") == ["--hname"]
  388. @pytest.mark.parametrize(
  389. ("args", "part", "expect"),
  390. [
  391. ([], "-", ["--opt"]),
  392. (["value"], "--", ["--opt"]),
  393. ([], "-o", []),
  394. (["--opt"], "-o", []),
  395. (["--"], "", ["name", "-o", "--opt", "--"]),
  396. (["--"], "--o", ["--opt"]),
  397. ],
  398. )
  399. def test_args_with_double_dash_complete(args, part, expect):
  400. def _complete(ctx, args, incomplete):
  401. values = ["name", "-o", "--opt", "--"]
  402. return [x for x in values if x.startswith(incomplete)]
  403. @click.command()
  404. @click.option("--opt")
  405. @click.argument("args", nargs=-1, autocompletion=_complete)
  406. def cli(opt, args):
  407. pass
  408. assert choices_without_help(cli, args, part) == expect