test_formatting.py 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. import click
  2. def test_basic_functionality(runner):
  3. @click.command()
  4. def cli():
  5. """First paragraph.
  6. This is a very long second
  7. paragraph and not correctly
  8. wrapped but it will be rewrapped.
  9. \b
  10. This is
  11. a paragraph
  12. without rewrapping.
  13. \b
  14. 1
  15. 2
  16. 3
  17. And this is a paragraph
  18. that will be rewrapped again.
  19. """
  20. result = runner.invoke(cli, ["--help"], terminal_width=60)
  21. assert not result.exception
  22. assert result.output.splitlines() == [
  23. "Usage: cli [OPTIONS]",
  24. "",
  25. " First paragraph.",
  26. "",
  27. " This is a very long second paragraph and not correctly",
  28. " wrapped but it will be rewrapped.",
  29. "",
  30. " This is",
  31. " a paragraph",
  32. " without rewrapping.",
  33. "",
  34. " 1",
  35. " 2",
  36. " 3",
  37. "",
  38. " And this is a paragraph that will be rewrapped again.",
  39. "",
  40. "Options:",
  41. " --help Show this message and exit.",
  42. ]
  43. def test_wrapping_long_options_strings(runner):
  44. @click.group()
  45. def cli():
  46. """Top level command"""
  47. @cli.group()
  48. def a_very_long():
  49. """Second level"""
  50. @a_very_long.command()
  51. @click.argument("first")
  52. @click.argument("second")
  53. @click.argument("third")
  54. @click.argument("fourth")
  55. @click.argument("fifth")
  56. @click.argument("sixth")
  57. def command():
  58. """A command."""
  59. # 54 is chosen as a length where the second line is one character
  60. # longer than the maximum length.
  61. result = runner.invoke(cli, ["a-very-long", "command", "--help"], terminal_width=54)
  62. assert not result.exception
  63. assert result.output.splitlines() == [
  64. "Usage: cli a-very-long command [OPTIONS] FIRST SECOND",
  65. " THIRD FOURTH FIFTH",
  66. " SIXTH",
  67. "",
  68. " A command.",
  69. "",
  70. "Options:",
  71. " --help Show this message and exit.",
  72. ]
  73. def test_wrapping_long_command_name(runner):
  74. @click.group()
  75. def cli():
  76. """Top level command"""
  77. @cli.group()
  78. def a_very_very_very_long():
  79. """Second level"""
  80. @a_very_very_very_long.command()
  81. @click.argument("first")
  82. @click.argument("second")
  83. @click.argument("third")
  84. @click.argument("fourth")
  85. @click.argument("fifth")
  86. @click.argument("sixth")
  87. def command():
  88. """A command."""
  89. result = runner.invoke(
  90. cli, ["a-very-very-very-long", "command", "--help"], terminal_width=54
  91. )
  92. assert not result.exception
  93. assert result.output.splitlines() == [
  94. "Usage: cli a-very-very-very-long command ",
  95. " [OPTIONS] FIRST SECOND THIRD FOURTH FIFTH",
  96. " SIXTH",
  97. "",
  98. " A command.",
  99. "",
  100. "Options:",
  101. " --help Show this message and exit.",
  102. ]
  103. def test_formatting_empty_help_lines(runner):
  104. @click.command()
  105. def cli():
  106. # fmt: off
  107. """Top level command
  108. """
  109. # fmt: on
  110. result = runner.invoke(cli, ["--help"])
  111. assert not result.exception
  112. assert result.output.splitlines() == [
  113. "Usage: cli [OPTIONS]",
  114. "",
  115. " Top level command",
  116. "",
  117. "",
  118. "",
  119. "Options:",
  120. " --help Show this message and exit.",
  121. ]
  122. def test_formatting_usage_error(runner):
  123. @click.command()
  124. @click.argument("arg")
  125. def cmd(arg):
  126. click.echo(f"arg:{arg}")
  127. result = runner.invoke(cmd, [])
  128. assert result.exit_code == 2
  129. assert result.output.splitlines() == [
  130. "Usage: cmd [OPTIONS] ARG",
  131. "Try 'cmd --help' for help.",
  132. "",
  133. "Error: Missing argument 'ARG'.",
  134. ]
  135. def test_formatting_usage_error_metavar_missing_arg(runner):
  136. """
  137. :author: @r-m-n
  138. Including attribution to #612
  139. """
  140. @click.command()
  141. @click.argument("arg", metavar="metavar")
  142. def cmd(arg):
  143. pass
  144. result = runner.invoke(cmd, [])
  145. assert result.exit_code == 2
  146. assert result.output.splitlines() == [
  147. "Usage: cmd [OPTIONS] metavar",
  148. "Try 'cmd --help' for help.",
  149. "",
  150. "Error: Missing argument 'metavar'.",
  151. ]
  152. def test_formatting_usage_error_metavar_bad_arg(runner):
  153. @click.command()
  154. @click.argument("arg", type=click.INT, metavar="metavar")
  155. def cmd(arg):
  156. pass
  157. result = runner.invoke(cmd, ["3.14"])
  158. assert result.exit_code == 2
  159. assert result.output.splitlines() == [
  160. "Usage: cmd [OPTIONS] metavar",
  161. "Try 'cmd --help' for help.",
  162. "",
  163. "Error: Invalid value for 'metavar': '3.14' is not a valid integer.",
  164. ]
  165. def test_formatting_usage_error_nested(runner):
  166. @click.group()
  167. def cmd():
  168. pass
  169. @cmd.command()
  170. @click.argument("bar")
  171. def foo(bar):
  172. click.echo(f"foo:{bar}")
  173. result = runner.invoke(cmd, ["foo"])
  174. assert result.exit_code == 2
  175. assert result.output.splitlines() == [
  176. "Usage: cmd foo [OPTIONS] BAR",
  177. "Try 'cmd foo --help' for help.",
  178. "",
  179. "Error: Missing argument 'BAR'.",
  180. ]
  181. def test_formatting_usage_error_no_help(runner):
  182. @click.command(add_help_option=False)
  183. @click.argument("arg")
  184. def cmd(arg):
  185. click.echo(f"arg:{arg}")
  186. result = runner.invoke(cmd, [])
  187. assert result.exit_code == 2
  188. assert result.output.splitlines() == [
  189. "Usage: cmd [OPTIONS] ARG",
  190. "",
  191. "Error: Missing argument 'ARG'.",
  192. ]
  193. def test_formatting_usage_custom_help(runner):
  194. @click.command(context_settings=dict(help_option_names=["--man"]))
  195. @click.argument("arg")
  196. def cmd(arg):
  197. click.echo(f"arg:{arg}")
  198. result = runner.invoke(cmd, [])
  199. assert result.exit_code == 2
  200. assert result.output.splitlines() == [
  201. "Usage: cmd [OPTIONS] ARG",
  202. "Try 'cmd --man' for help.",
  203. "",
  204. "Error: Missing argument 'ARG'.",
  205. ]
  206. def test_formatting_custom_type_metavar(runner):
  207. class MyType(click.ParamType):
  208. def get_metavar(self, param):
  209. return "MY_TYPE"
  210. @click.command("foo")
  211. @click.help_option()
  212. @click.argument("param", type=MyType())
  213. def cmd(param):
  214. pass
  215. result = runner.invoke(cmd, "--help")
  216. assert not result.exception
  217. assert result.output.splitlines() == [
  218. "Usage: foo [OPTIONS] MY_TYPE",
  219. "",
  220. "Options:",
  221. " --help Show this message and exit.",
  222. ]
  223. def test_truncating_docstring(runner):
  224. @click.command()
  225. @click.pass_context
  226. def cli(ctx):
  227. """First paragraph.
  228. This is a very long second
  229. paragraph and not correctly
  230. wrapped but it will be rewrapped.
  231. \f
  232. :param click.core.Context ctx: Click context.
  233. """
  234. result = runner.invoke(cli, ["--help"], terminal_width=60)
  235. assert not result.exception
  236. assert result.output.splitlines() == [
  237. "Usage: cli [OPTIONS]",
  238. "",
  239. " First paragraph.",
  240. "",
  241. " This is a very long second paragraph and not correctly",
  242. " wrapped but it will be rewrapped.",
  243. "",
  244. "Options:",
  245. " --help Show this message and exit.",
  246. ]
  247. def test_truncating_docstring_no_help(runner):
  248. @click.command()
  249. @click.pass_context
  250. def cli(ctx):
  251. """
  252. \f
  253. This text should be truncated.
  254. """
  255. result = runner.invoke(cli, ["--help"], terminal_width=60)
  256. assert not result.exception
  257. assert result.output.splitlines() == [
  258. "Usage: cli [OPTIONS]",
  259. "",
  260. "Options:",
  261. " --help Show this message and exit.",
  262. ]
  263. def test_removing_multiline_marker(runner):
  264. @click.group()
  265. def cli():
  266. pass
  267. @cli.command()
  268. def cmd1():
  269. """\b
  270. This is command with a multiline help text
  271. which should not be rewrapped.
  272. The output of the short help text should
  273. not contain the multiline marker.
  274. """
  275. pass
  276. result = runner.invoke(cli, ["--help"])
  277. assert "\b" not in result.output
  278. def test_global_show_default(runner):
  279. @click.command(context_settings=dict(show_default=True))
  280. @click.option("-f", "in_file", default="out.txt", help="Output file name")
  281. def cli():
  282. pass
  283. result = runner.invoke(cli, ["--help"])
  284. # the default to "--help" is not shown because it is False
  285. assert result.output.splitlines() == [
  286. "Usage: cli [OPTIONS]",
  287. "",
  288. "Options:",
  289. " -f TEXT Output file name [default: out.txt]",
  290. " --help Show this message and exit.",
  291. ]
  292. def test_formatting_with_options_metavar_empty(runner):
  293. cli = click.Command("cli", options_metavar="", params=[click.Argument(["var"])])
  294. result = runner.invoke(cli, ["--help"])
  295. assert "Usage: cli VAR\n" in result.output
  296. def test_help_formatter_write_text():
  297. text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
  298. formatter = click.HelpFormatter(width=len(" Lorem ipsum dolor sit amet,"))
  299. formatter.current_indent = 2
  300. formatter.write_text(text)
  301. actual = formatter.getvalue()
  302. expected = " Lorem ipsum dolor sit amet,\n consectetur adipiscing elit\n"
  303. assert actual == expected