test_basic.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  1. import os
  2. from itertools import chain
  3. import pytest
  4. import click
  5. def test_basic_functionality(runner):
  6. @click.command()
  7. def cli():
  8. """Hello World!"""
  9. click.echo("I EXECUTED")
  10. result = runner.invoke(cli, ["--help"])
  11. assert not result.exception
  12. assert "Hello World!" in result.output
  13. assert "Show this message and exit." in result.output
  14. assert result.exit_code == 0
  15. assert "I EXECUTED" not in result.output
  16. result = runner.invoke(cli, [])
  17. assert not result.exception
  18. assert "I EXECUTED" in result.output
  19. assert result.exit_code == 0
  20. def test_repr():
  21. @click.command()
  22. def command():
  23. pass
  24. @click.group()
  25. def group():
  26. pass
  27. @group.command()
  28. def subcommand():
  29. pass
  30. assert repr(command) == "<Command command>"
  31. assert repr(group) == "<Group group>"
  32. assert repr(subcommand) == "<Command subcommand>"
  33. def test_return_values():
  34. @click.command()
  35. def cli():
  36. return 42
  37. with cli.make_context("foo", []) as ctx:
  38. rv = cli.invoke(ctx)
  39. assert rv == 42
  40. def test_basic_group(runner):
  41. @click.group()
  42. def cli():
  43. """This is the root."""
  44. click.echo("ROOT EXECUTED")
  45. @cli.command()
  46. def subcommand():
  47. """This is a subcommand."""
  48. click.echo("SUBCOMMAND EXECUTED")
  49. result = runner.invoke(cli, ["--help"])
  50. assert not result.exception
  51. assert "COMMAND [ARGS]..." in result.output
  52. assert "This is the root" in result.output
  53. assert "This is a subcommand." in result.output
  54. assert result.exit_code == 0
  55. assert "ROOT EXECUTED" not in result.output
  56. result = runner.invoke(cli, ["subcommand"])
  57. assert not result.exception
  58. assert result.exit_code == 0
  59. assert "ROOT EXECUTED" in result.output
  60. assert "SUBCOMMAND EXECUTED" in result.output
  61. def test_group_commands_dict(runner):
  62. """A Group can be built with a dict of commands."""
  63. @click.command()
  64. def sub():
  65. click.echo("sub", nl=False)
  66. cli = click.Group(commands={"other": sub})
  67. result = runner.invoke(cli, ["other"])
  68. assert result.output == "sub"
  69. def test_group_from_list(runner):
  70. """A Group can be built with a list of commands."""
  71. @click.command()
  72. def sub():
  73. click.echo("sub", nl=False)
  74. cli = click.Group(commands=[sub])
  75. result = runner.invoke(cli, ["sub"])
  76. assert result.output == "sub"
  77. @pytest.mark.parametrize(
  78. ("args", "expect"),
  79. [
  80. ([], "S:[no value]"),
  81. (["--s=42"], "S:[42]"),
  82. (["--s"], "Error: Option '--s' requires an argument."),
  83. (["--s="], "S:[]"),
  84. (["--s=\N{SNOWMAN}"], "S:[\N{SNOWMAN}]"),
  85. ],
  86. )
  87. def test_string_option(runner, args, expect):
  88. @click.command()
  89. @click.option("--s", default="no value")
  90. def cli(s):
  91. click.echo(f"S:[{s}]")
  92. result = runner.invoke(cli, args)
  93. assert expect in result.output
  94. if expect.startswith("Error:"):
  95. assert result.exception is not None
  96. else:
  97. assert result.exception is None
  98. @pytest.mark.parametrize(
  99. ("args", "expect"),
  100. [
  101. ([], "I:[84]"),
  102. (["--i=23"], "I:[46]"),
  103. (["--i=x"], "Error: Invalid value for '--i': 'x' is not a valid integer."),
  104. ],
  105. )
  106. def test_int_option(runner, args, expect):
  107. @click.command()
  108. @click.option("--i", default=42)
  109. def cli(i):
  110. click.echo(f"I:[{i * 2}]")
  111. result = runner.invoke(cli, args)
  112. assert expect in result.output
  113. if expect.startswith("Error:"):
  114. assert result.exception is not None
  115. else:
  116. assert result.exception is None
  117. @pytest.mark.parametrize(
  118. ("args", "expect"),
  119. [
  120. ([], "U:[ba122011-349f-423b-873b-9d6a79c688ab]"),
  121. (
  122. ["--u=821592c1-c50e-4971-9cd6-e89dc6832f86"],
  123. "U:[821592c1-c50e-4971-9cd6-e89dc6832f86]",
  124. ),
  125. (["--u=x"], "Error: Invalid value for '--u': 'x' is not a valid UUID."),
  126. ],
  127. )
  128. def test_uuid_option(runner, args, expect):
  129. @click.command()
  130. @click.option(
  131. "--u", default="ba122011-349f-423b-873b-9d6a79c688ab", type=click.UUID
  132. )
  133. def cli(u):
  134. click.echo(f"U:[{u}]")
  135. result = runner.invoke(cli, args)
  136. assert expect in result.output
  137. if expect.startswith("Error:"):
  138. assert result.exception is not None
  139. else:
  140. assert result.exception is None
  141. @pytest.mark.parametrize(
  142. ("args", "expect"),
  143. [
  144. ([], "F:[42.0]"),
  145. ("--f=23.5", "F:[23.5]"),
  146. ("--f=x", "Error: Invalid value for '--f': 'x' is not a valid float."),
  147. ],
  148. )
  149. def test_float_option(runner, args, expect):
  150. @click.command()
  151. @click.option("--f", default=42.0)
  152. def cli(f):
  153. click.echo(f"F:[{f}]")
  154. result = runner.invoke(cli, args)
  155. assert expect in result.output
  156. if expect.startswith("Error:"):
  157. assert result.exception is not None
  158. else:
  159. assert result.exception is None
  160. @pytest.mark.parametrize("default", [True, False])
  161. @pytest.mark.parametrize(
  162. ("args", "expect"), [(["--on"], True), (["--off"], False), ([], None)]
  163. )
  164. def test_boolean_switch(runner, default, args, expect):
  165. @click.command()
  166. @click.option("--on/--off", default=default)
  167. def cli(on):
  168. return on
  169. if expect is None:
  170. expect = default
  171. result = runner.invoke(cli, args, standalone_mode=False)
  172. assert result.return_value is expect
  173. @pytest.mark.parametrize("default", [True, False])
  174. @pytest.mark.parametrize(("args", "expect"), [(["--f"], True), ([], False)])
  175. def test_boolean_flag(runner, default, args, expect):
  176. @click.command()
  177. @click.option("--f", is_flag=True, default=default)
  178. def cli(f):
  179. return f
  180. if default:
  181. expect = not expect
  182. result = runner.invoke(cli, args, standalone_mode=False)
  183. assert result.return_value is expect
  184. @pytest.mark.parametrize(
  185. ("value", "expect"),
  186. chain(
  187. ((x, "True") for x in ("1", "true", "t", "yes", "y", "on")),
  188. ((x, "False") for x in ("0", "false", "f", "no", "n", "off")),
  189. ),
  190. )
  191. def test_boolean_conversion(runner, value, expect):
  192. @click.command()
  193. @click.option("--flag", type=bool)
  194. def cli(flag):
  195. click.echo(flag, nl=False)
  196. result = runner.invoke(cli, ["--flag", value])
  197. assert result.output == expect
  198. result = runner.invoke(cli, ["--flag", value.title()])
  199. assert result.output == expect
  200. def test_file_option(runner):
  201. @click.command()
  202. @click.option("--file", type=click.File("w"))
  203. def input(file):
  204. file.write("Hello World!\n")
  205. @click.command()
  206. @click.option("--file", type=click.File("r"))
  207. def output(file):
  208. click.echo(file.read())
  209. with runner.isolated_filesystem():
  210. result_in = runner.invoke(input, ["--file=example.txt"])
  211. result_out = runner.invoke(output, ["--file=example.txt"])
  212. assert not result_in.exception
  213. assert result_in.output == ""
  214. assert not result_out.exception
  215. assert result_out.output == "Hello World!\n\n"
  216. def test_file_lazy_mode(runner):
  217. do_io = False
  218. @click.command()
  219. @click.option("--file", type=click.File("w"))
  220. def input(file):
  221. if do_io:
  222. file.write("Hello World!\n")
  223. @click.command()
  224. @click.option("--file", type=click.File("r"))
  225. def output(file):
  226. pass
  227. with runner.isolated_filesystem():
  228. os.mkdir("example.txt")
  229. do_io = True
  230. result_in = runner.invoke(input, ["--file=example.txt"])
  231. assert result_in.exit_code == 1
  232. do_io = False
  233. result_in = runner.invoke(input, ["--file=example.txt"])
  234. assert result_in.exit_code == 0
  235. result_out = runner.invoke(output, ["--file=example.txt"])
  236. assert result_out.exception
  237. @click.command()
  238. @click.option("--file", type=click.File("w", lazy=False))
  239. def input_non_lazy(file):
  240. file.write("Hello World!\n")
  241. with runner.isolated_filesystem():
  242. os.mkdir("example.txt")
  243. result_in = runner.invoke(input_non_lazy, ["--file=example.txt"])
  244. assert result_in.exit_code == 2
  245. assert "Invalid value for '--file': 'example.txt'" in result_in.output
  246. def test_path_option(runner):
  247. @click.command()
  248. @click.option("-O", type=click.Path(file_okay=False, exists=True, writable=True))
  249. def write_to_dir(o):
  250. with open(os.path.join(o, "foo.txt"), "wb") as f:
  251. f.write(b"meh\n")
  252. with runner.isolated_filesystem():
  253. os.mkdir("test")
  254. result = runner.invoke(write_to_dir, ["-O", "test"])
  255. assert not result.exception
  256. with open("test/foo.txt", "rb") as f:
  257. assert f.read() == b"meh\n"
  258. result = runner.invoke(write_to_dir, ["-O", "test/foo.txt"])
  259. assert "is a file" in result.output
  260. @click.command()
  261. @click.option("-f", type=click.Path(exists=True))
  262. def showtype(f):
  263. click.echo(f"is_file={os.path.isfile(f)}")
  264. click.echo(f"is_dir={os.path.isdir(f)}")
  265. with runner.isolated_filesystem():
  266. result = runner.invoke(showtype, ["-f", "xxx"])
  267. assert "does not exist" in result.output
  268. result = runner.invoke(showtype, ["-f", "."])
  269. assert "is_file=False" in result.output
  270. assert "is_dir=True" in result.output
  271. @click.command()
  272. @click.option("-f", type=click.Path())
  273. def exists(f):
  274. click.echo(f"exists={os.path.exists(f)}")
  275. with runner.isolated_filesystem():
  276. result = runner.invoke(exists, ["-f", "xxx"])
  277. assert "exists=False" in result.output
  278. result = runner.invoke(exists, ["-f", "."])
  279. assert "exists=True" in result.output
  280. def test_choice_option(runner):
  281. @click.command()
  282. @click.option("--method", type=click.Choice(["foo", "bar", "baz"]))
  283. def cli(method):
  284. click.echo(method)
  285. result = runner.invoke(cli, ["--method=foo"])
  286. assert not result.exception
  287. assert result.output == "foo\n"
  288. result = runner.invoke(cli, ["--method=meh"])
  289. assert result.exit_code == 2
  290. assert (
  291. "Invalid value for '--method': 'meh' is not one of 'foo', 'bar', 'baz'."
  292. in result.output
  293. )
  294. result = runner.invoke(cli, ["--help"])
  295. assert "--method [foo|bar|baz]" in result.output
  296. def test_choice_argument(runner):
  297. @click.command()
  298. @click.argument("method", type=click.Choice(["foo", "bar", "baz"]))
  299. def cli(method):
  300. click.echo(method)
  301. result = runner.invoke(cli, ["foo"])
  302. assert not result.exception
  303. assert result.output == "foo\n"
  304. result = runner.invoke(cli, ["meh"])
  305. assert result.exit_code == 2
  306. assert (
  307. "Invalid value for '{foo|bar|baz}': 'meh' is not one of 'foo',"
  308. " 'bar', 'baz'." in result.output
  309. )
  310. result = runner.invoke(cli, ["--help"])
  311. assert "{foo|bar|baz}" in result.output
  312. def test_datetime_option_default(runner):
  313. @click.command()
  314. @click.option("--start_date", type=click.DateTime())
  315. def cli(start_date):
  316. click.echo(start_date.strftime("%Y-%m-%dT%H:%M:%S"))
  317. result = runner.invoke(cli, ["--start_date=2015-09-29"])
  318. assert not result.exception
  319. assert result.output == "2015-09-29T00:00:00\n"
  320. result = runner.invoke(cli, ["--start_date=2015-09-29T09:11:22"])
  321. assert not result.exception
  322. assert result.output == "2015-09-29T09:11:22\n"
  323. result = runner.invoke(cli, ["--start_date=2015-09"])
  324. assert result.exit_code == 2
  325. assert (
  326. "Invalid value for '--start_date': '2015-09' does not match the formats"
  327. " '%Y-%m-%d', '%Y-%m-%dT%H:%M:%S', '%Y-%m-%d %H:%M:%S'."
  328. ) in result.output
  329. result = runner.invoke(cli, ["--help"])
  330. assert (
  331. "--start_date [%Y-%m-%d|%Y-%m-%dT%H:%M:%S|%Y-%m-%d %H:%M:%S]" in result.output
  332. )
  333. def test_datetime_option_custom(runner):
  334. @click.command()
  335. @click.option("--start_date", type=click.DateTime(formats=["%A %B %d, %Y"]))
  336. def cli(start_date):
  337. click.echo(start_date.strftime("%Y-%m-%dT%H:%M:%S"))
  338. result = runner.invoke(cli, ["--start_date=Wednesday June 05, 2010"])
  339. assert not result.exception
  340. assert result.output == "2010-06-05T00:00:00\n"
  341. def test_required_option(runner):
  342. @click.command()
  343. @click.option("--foo", required=True)
  344. def cli(foo):
  345. click.echo(foo)
  346. result = runner.invoke(cli, [])
  347. assert result.exit_code == 2
  348. assert "Missing option '--foo'" in result.output
  349. def test_evaluation_order(runner):
  350. called = []
  351. def memo(ctx, param, value):
  352. called.append(value)
  353. return value
  354. @click.command()
  355. @click.option("--missing", default="missing", is_eager=False, callback=memo)
  356. @click.option("--eager-flag1", flag_value="eager1", is_eager=True, callback=memo)
  357. @click.option("--eager-flag2", flag_value="eager2", is_eager=True, callback=memo)
  358. @click.option("--eager-flag3", flag_value="eager3", is_eager=True, callback=memo)
  359. @click.option("--normal-flag1", flag_value="normal1", is_eager=False, callback=memo)
  360. @click.option("--normal-flag2", flag_value="normal2", is_eager=False, callback=memo)
  361. @click.option("--normal-flag3", flag_value="normal3", is_eager=False, callback=memo)
  362. def cli(**x):
  363. pass
  364. result = runner.invoke(
  365. cli,
  366. [
  367. "--eager-flag2",
  368. "--eager-flag1",
  369. "--normal-flag2",
  370. "--eager-flag3",
  371. "--normal-flag3",
  372. "--normal-flag3",
  373. "--normal-flag1",
  374. "--normal-flag1",
  375. ],
  376. )
  377. assert not result.exception
  378. assert called == [
  379. "eager2",
  380. "eager1",
  381. "eager3",
  382. "normal2",
  383. "normal3",
  384. "normal1",
  385. "missing",
  386. ]
  387. def test_hidden_option(runner):
  388. @click.command()
  389. @click.option("--nope", hidden=True)
  390. def cli(nope):
  391. click.echo(nope)
  392. result = runner.invoke(cli, ["--help"])
  393. assert result.exit_code == 0
  394. assert "--nope" not in result.output
  395. def test_hidden_command(runner):
  396. @click.group()
  397. def cli():
  398. pass
  399. @cli.command(hidden=True)
  400. def nope():
  401. pass
  402. result = runner.invoke(cli, ["--help"])
  403. assert result.exit_code == 0
  404. assert "nope" not in result.output
  405. def test_hidden_group(runner):
  406. @click.group()
  407. def cli():
  408. pass
  409. @cli.group(hidden=True)
  410. def subgroup():
  411. pass
  412. @subgroup.command()
  413. def nope():
  414. pass
  415. result = runner.invoke(cli, ["--help"])
  416. assert result.exit_code == 0
  417. assert "subgroup" not in result.output
  418. assert "nope" not in result.output
  419. def test_summary_line(runner):
  420. @click.group()
  421. def cli():
  422. pass
  423. @cli.command()
  424. def cmd():
  425. """
  426. Summary line without period
  427. Here is a sentence. And here too.
  428. """
  429. pass
  430. result = runner.invoke(cli, ["--help"])
  431. assert "Summary line without period" in result.output
  432. assert "Here is a sentence." not in result.output
  433. def test_help_invalid_default(runner):
  434. cli = click.Command(
  435. "cli",
  436. params=[
  437. click.Option(
  438. ["-a"],
  439. type=click.Path(exists=True),
  440. default="not found",
  441. show_default=True,
  442. ),
  443. ],
  444. )
  445. result = runner.invoke(cli, ["--help"])
  446. assert result.exit_code == 0
  447. assert "default: not found" in result.output